具有多个ID的iOS Firestore复合查询

时间:2018-11-27 13:05:12

标签: swift google-cloud-firestore algolia

这是我在尝试将地理位置与Firestore混合使用时遇到的一个问题。长话短说-我需要在用户所在位置附近的餐馆。为了完成地理搜索,我使用了Algolia。当我向Algolia发送请求时,它会返回一个数组的唯一餐厅ID,这些ID与Firestore文档ID相对应。这样就可以了。

使事情变得复杂的是,我还需要两个条件-我需要将查询限制为平均评分> = 8的餐馆。而且我还想限制返回文档的数量(2、5、20等) 。

这就是伪代码中的样子:

db.restaurantsCollection
    .documentIds([111, 222, 333, 444, 555, 666, 777, 888, 999])
    .whereField("averageRating", isGreaterThanOrEqualTo: 8)
    .order(by: "averageRating", descending: true)
    .limit(to: 2)
    .getDocuments()

到目前为止,我知道Firestore不支持具有多个文档ID的查询。那么执行这种查询的最优化方法是什么?

如果我在文档中将documentId设置为id字段,然后遍历从Algolia返回的所有ID,然后执行类似的操作(那么我可以纯粹地进行排序和限制Swift):

for id in ids {
    db.restaurantsCollection
        .whereField("averageRating", isGreaterThanOrEqualTo: 8)
        .whereField("id", isEqualTo: id)
        .getDocuments()
}

但这仍然意味着很多请求。还有其他想法吗?

2 个答案:

答案 0 :(得分:0)

如果这对我发布的任何人有帮助,那么如果您以我实现它们的方式实现地理查询,可以考虑使用Algolia。因此,使用Algolia的方式是保持搜索索引的坐标。

例如:

{
  "_geoloc": {
    "lng": 8.507785799999999,
    "lat": 47.17652589999999
  },
  "objectID": "1234abcd"
}

其中objectID对应于餐厅或您要进行地理查询的任何场所的文档ID。

然后,您通过Algolia API创建查询。如果要获取CLLocation周围的所有项目,则应进行设置-为此,请使用query.aroundLatLng属性。您还可以以米为单位设置distance属性。结果将是一个JSON,其中您的ID按照距离的顺序从最接近的位置开始排序。

到目前为止,我们已经解决了最大的问题-按地理位置查询。

现在,如果我想从我从阿尔及利亚获得的所有N个ID中获得最高评分,该怎么办?可悲的是,到目前为止,还没有可以通过多个ID查询的API。因此,这意味着如果我想获得所有分数大于等于8的餐厅,则需要读取N个Firestore文档,然后获得最高评分。远非理想。

选项1。

使用Algolia数据保存餐厅的平均值。

我知道这意味着额外的联网,但这是一个不错的方法。缺点-每次在Firestore中更新餐厅平均价格时,也需要在Algolia中更新餐厅平均价格。如果像我一样使用Cloud Functions计算平均值,则也需要在Algolia中设置平均值。是的,这意味着您需要升级到付费Firebase计划,因为这是外部Cloud Functions调用。

现在您在阿尔及利亚的有效负载中拥有平均值,您可以轻松获取您所在地区的所有餐厅ID,然后按其阿尔及利亚评级客户端对数据进行排序。对ID进行排序后,即可从Firestore请求文档。

选项2。

保存城市名称和国家

您可以将城市和国家/地区名称以及位置数据保存在Firestore中。这样,您可以将它们用于查询。伪Firestore查询代码:

db.restaurants.where("city" == "current_city").where("country" == "current_country").where("averageRating" >= 8).getDocuments()

但是,这种方法并不十分准确,并且通常适用于大城市。它还不会计算您附近的位置。

我希望这会有所帮助。

答案 1 :(得分:0)

这是一种更有效的方法:

    firestoreDB
        .collection("someCollection")
        .whereField(FieldPath.documentID(), in: [
            "05C0632C-601B-4D98-BD2B-3E809D0496B1", //up to 10 ids
            "087686CA-6B21-4268-9E4C-CF833FCA92DE"
        ]).getDocuments { snap, error in

            if let snap = snap {
                snap.documents.forEach { print($0.data()) }

            } else if let error = error {
                print("Error: \(error)")
            }
        }

以10为批次获取它们可以进行排序(使用.order(by:)等) 它不是完美的,但看起来像是对您已有的改进。 您可以进行多个此类调用,然后将结果合并为一个,然后对它们进行排序。