Hi I am working on a location based app (ie uber, doordash, tinder, bumble, etc) that is expected to have a large database of users from around the world. I am new to both Xcode + swift and firebase and I m currently using Firestore. I have recently found out about GeoFire and using a hash but it seems that most of the information I am finding is outdated.
What I am trying to understand is the proper way to structure my database and code for best performance and lower costs.
My current database looks something like this:
FirestoreDB -> Users -> UserId -> Document Data = [ Latitude, Longitude, GeoHash ]
And the code I am using is from Firebase docs (*I do not have any collections/documents currently for cities, countries etc.) -
// Compute the GeoHash for a lat/lng point
let latitude = 51.5074
let longitude = 0.12780
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let hash = GFUtils.geoHash(forLocation: location)
// Add the hash and the lat/lng to the document. We will use the hash
// for queries and the lat/lng for distance comparisons.
let documentData: [String: Any] = [
"geohash": hash,
"lat": latitude,
"lng": longitude
]
let londonRef = db.collection("cities").document("LON")
londonRef.updateData(documentData) { error in
// ...
}
And in the same doc, the code to query -
// Find cities within 50km of London
let center = CLLocationCoordinate2D(latitude: 51.5074, longitude: 0.1278)
let radiusInM: Double = 50 * 1000
// Each item in bounds represents a startAt/endAt pair. We have to issue
// a separate query for each pair. There can be up to 9 pairs of bounds
// depending on overlap, but in most cases there are 4.
let queryBounds = GFUtils.queryBounds(forLocation: center,
withRadius: radiusInM)
let queries = queryBounds.map { bound -> Query in
return db.collection("cities")
.order(by: "geohash")
.start(at: [bound.startValue])
.end(at: [bound.endValue])
}
var matchingDocs = [QueryDocumentSnapshot]()
// Collect all the query results together into a single list
func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
guard let documents = snapshot?.documents else {
print("Unable to fetch snapshot data. (String(describing: error))")
return
}
for document in documents {
let lat = document.data()["lat"] as? Double ?? 0
let lng = document.data()["lng"] as? Double ?? 0
let coordinates = CLLocation(latitude: lat, longitude: lng)
let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude)
// We have to filter out a few false positives due to GeoHash accuracy, but
// most will match
let distance = GFUtils.distance(from: centerPoint, to: coordinates)
if distance <= radiusInM {
matchingDocs.append(document)
}
}
}
// After all callbacks have executed, matchingDocs contains the result. Note that this
// sample does not demonstrate how to wait on all callbacks to complete.
for query in queries {
query.getDocuments(completion: getDocumentsCompletion)
}
According to this, for my situation I would need to create a collection for each city/location(?) and query all the "userId" documents in the database, which of course wouldn t be optimal. I see the functions for saving a key and retrieving by key, such as -
geoFire.getLocationForKey("firebase-hq")
but I don t see the key being added to Firestore and I m not sure how this would even work in my situation since any location and users locations needs to be dynamic and needs to update on the go? So how can that be used in a dynamic way so documents can be grouped by key(?) and queries ran against those documents based on the key that matches the current location s hash?
I m unsure what is the best approach on this, there are a lot of popular location based apps out there so I m wondering how do they handle it. Any suggestions is appreciated, thanks.
EDIT: To clarify further: I am trying to use Firestore geohashes but I am looking for a better way rather than querying every document in the database.