Как найти документы в радиусе x миль по геохешам без фильтрации на клиенте?

Итак, в настоящее время я использую геохэши для выполнения запросов на основе местоположения как таковых (после этого сообщения stackoverflow: 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)
           }
       }

Как видите, я запрашиваю документы с определенным префиксом, а затем СНОВА фильтрую эти документы на клиенте. Это безопасно? Если нет, то каков обходной путь? Использовать облачные функции, другой алгоритм (предложите, если он у вас есть) или что-то еще?


person Kunal M    schedule 15.08.2020    source источник


Ответы (1)


Запрос геохэшей возвращает точки в пределах определенного диапазона геохэшей, которые представляют собой (несколько) прямоугольные области.

Геозапрос по центральной точке и расстоянию возвращает точки, находящиеся в круге.

Поскольку эти две формы различны, ваш код использует проверку на стороне клиента, чтобы отрезать точки, которые находятся за пределами круга, но внутри прямоугольников. Это обычный шаг при выполнении геозапросов на максимальное расстояние вокруг точки при использовании геохэшей.


Вот пример этого на карте:

введите здесь описание изображения

Зеленые булавки находятся в 250-километровом круге вокруг Сан-Франциско, что я и запросил. Красные булавки находятся за пределами этого круга, но в пределах набора диапазонов геохэшей (здесь [["9q0","9qh"],["9nh","9n~"],["9r0","9rh"],["9ph","9p~"]]), которые нужно было запросить, чтобы убедиться, что все точки находятся в диапазоне.

Как уже говорилось: эта так называемая избыточная выборка присуща использованию геохэшей для выполнения запросов типа «точка-и-расстояние». По моему опыту, вы в конечном итоге прочитаете от 2 до 8 раз больше документов.

Возможно можно уменьшить количество ложных срабатываний, запрашивая больше, но меньших диапазонов, но я не знаю ни одной геобиблиотеки для Firestore, которая делает это.

Я отобразил дополнительную стоимость как ментальную модель: поиск документов на определенном расстоянии от точки обходится мне в 2-8 раз дороже, чем обычное чтение документа.


Перемещение операции в облачные функции на сервере не влияет на количество документов, которые необходимо прочитать, а только на то, где они читаются. Таким образом, вы можете выполнить операцию на сервере, чтобы уменьшить пропускную способность при передаче документов из базы данных клиенту. Но это не будет иметь значения в количестве документов, которые необходимо прочитать.

Как обсуждалось в комментариях: выполнение запроса на сервере позволит удалить клиентский доступ к данным, чтобы вы могли гарантировать, что код приложения никогда не увидит документы, которые не находятся в запрошенном диапазоне. . Поэтому, если вы беспокоитесь о доступе к документам, выполнение запроса в доверенной среде (например, на сервере, которым вы управляете, или в облачных функциях) является хорошим вариантом.


Чтобы не платить за дополнительные чтения документов, подумайте о том, чтобы найти сервис, который изначально поддерживает геозапросы с моделью ценообразования, основанной на количестве возвращаемых результатов, а не на количестве результатов, которые он должен учитывать. Такая услуга (весьма вероятно) по-прежнему будет рассматривать слишком много баллов, но если модель ценообразования соответствует тому, что вы хотите, это может стоить того.

person Frank van Puffelen    schedule 15.08.2020
comment
Во-первых, привет, Фрэнк! Я увлекся геохешированием только после просмотра вашей презентации: youtube.com/watch?v= mx1mMdHBi5Q&t=2280s. Во-вторых, если вы смотрите это видео с 29:50 до 30:35 youtube.com/watch ?v=lW7DWV2jST0&t=1800s Разве это не та же проблема, что и у меня, если бы я фильтровал на клиенте? - person Kunal M; 15.08.2020
comment
Чтобы выполнить геозапрос из клиента, пользователь должен иметь возможность прочитать каждый документ в коллекции. Невозможно ограничить доступ к географическим диапазонам с помощью правил безопасности, которые являются единственным, что стоит между вашим кодом приложения и доступом ко всем данным. Если вы беспокоитесь о том, что люди могут получить доступ к документам за пределами диапазона, вам всегда следует выполнять запрос в безопасной среде (сервер или облачные функции). Но в этом случае проблема заключается не в избыточной выборке (что, по-видимому, предполагает ваше внимание к клиентской стороне isWithin), а в доступе к документам для начала. - person Frank van Puffelen; 15.08.2020
comment
Я также добавил немного больше об этом в свой ответ. Извините за то, что это было немного длинно, мы работали над некоторыми геоматериалами на этой неделе, так что это сейчас для меня на первом месте. :) - person Frank van Puffelen; 15.08.2020
comment
Я не беспокоюсь о чрезмерных запросах; Меня беспокоит потенциальная уязвимость, связанная с фильтрацией на клиенте. Итак, вы предлагаете сделать это в облачных функциях? Я не очень разбираюсь в облачных функциях, так возможна ли эта самая задача? - person Kunal M; 15.08.2020
comment
Я также попытался объяснить соображения безопасности в своем ответе. В правилах безопасности нет способа защитить запросы по диапазону (или даже по диапазону геохэшей). Если вам нужно гарантировать, что пользователи никогда не смогут видеть документы, кроме тех, которые находятся в диапазоне, вам нужно будет выполнять запросы в доверенной среде, а не напрямую от клиента. - person Frank van Puffelen; 15.08.2020