So currently I am using geohashes to do location based queries as such (following this stackoverflow post: Finding geohashes of certain length within radius from a point)
public extension CLLocationCoordinate2D {
func boundingBox(radius: CLLocationDistance) -> (max: CLLocationCoordinate2D, min: CLLocationCoordinate2D) {
// 0.0000089982311916 ~= 1m
let offset = 0.0000089982311916 * radius
let latMax = self.latitude + offset
let latMin = self.latitude - offset
// 1 degree of longitude = 111km only at equator
// (gradually shrinks to zero at the poles)
// So need to take into account latitude too
let lngOffset = offset * cos(self.latitude * .pi / 180.0)
let lngMax = self.longitude + lngOffset
let lngMin = self.longitude - lngOffset
let max = CLLocationCoordinate2D(latitude: latMax, longitude: lngMax)
let min = CLLocationCoordinate2D(latitude: latMin, longitude: lngMin)
return (max, min)
}
func isWithin(min: CLLocationCoordinate2D, max: CLLocationCoordinate2D) -> Bool {
return
self.latitude > min.latitude &&
self.latitude < max.latitude &&
self.longitude > min.longitude &&
self.longitude < max.longitude
}
}
func getGeohashPrefix(){
let loc = CLLocationCoordinate2D(latitude: lat!, longitude: long!)
MBR = loc.boundingBox(radius: 16093.4) //16093.4 meters = 10 miles
//corners = [NorthWest, SouthWest, SouthEast, NorthEast] in lat n long
let corners = [CLLocationCoordinate2D(latitude: MBR.0.latitude,longitude: MBR.1.longitude),
MBR.1, CLLocationCoordinate2D(latitude: MBR.1.latitude, longitude: MBR.0.longitude),
MBR.0]
var geohashes_of_corners: [String] = []
for corner in corners {
geohashes_of_corners.append(corner.geohash(length: 12))
}
geohashes_prefix = geohashes_of_corners.longestCommonPrefix()
}
var query: Query = db.collection("Users").whereField("geohash",isGreaterThanOrEqualTo: geohashes_prefix).whereField("geohash",isLessThanOrEqualTo: geohashes_prefix + "~").order(by: "geohash", descending: false)
query.getDocuments { (querySnapshot, err) in
if err != nil{
print("error getting da documents")
}else{
if querySnapshot!.isEmpty{
return completion(arr_of_people)
}
for document in querySnapshot!.documents {
let d = document.data()
let isPersonWithin = CLLocationCoordinate2D(latitude: (d["loc"] as! GeoPoint).latitude, longitude: (d["loc"] as! GeoPoint).longitude).isWithin(min: self.MBR.1, max: self.MBR.0)
if !isPersonWithin{
continue
}
arr_of_people.append([d["firstName"] as! String, d["lastName"] as! String])
}
return completion(arr_of_people)
}
}
As you can see, I am querying for documents with a specific prefix and then filtering those documents AGAIN on the client. Is that safe? If not, what is the workaround? Use cloud functions, different algorithm (suggest one if you have it), or something else?