Firestore搜索数组包含多个值

Luc*_*rdi 8 firebase swift google-cloud-firestore

如果我有一个简单的集合,例如:

Fruits:
  Banana:
   title: "Banana"
   vitamins: ["potassium","B6","C"]
  Apple:
   title: "Apple"
   vitamins: ["A","B6","C"]
Run Code Online (Sandbox Code Playgroud)

如果我要搜索含有维生素B6的水果,则可以执行以下操作:

db.collection("Fruits").whereField("vitamins", arrayContains: "A").getDocuments() { (querySnapshot, err)  in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
            for document in querySnapshot!.documents {
                print("\(document.documentID) => \(document.data())")
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

这样一来,我便可以看到我收藏的所有含有维生素A的水果。但是,我如何能够搜索含有多种维生素(例如维生素B6和C)的水果?我不能简单地搜索["B6","C"],因为那样会寻找数组,而不是独立的字符串。

在Cloud Firestore中甚至有可能吗?如果没有,还有其他替代方法吗?

Dou*_*son 11

It would be tempting to to look for documents that match multiple conditions, by chaining the conditions in the query:

db.collection("Fruits")
    .whereField("vitamins", arrayContains: "B6")
    .whereField("vitamins", arrayContains: "C")
Run Code Online (Sandbox Code Playgroud)

But the documentation on compound queries suggests that you cannot currently have multiple arrayContains conditions in a single query.

So, it is not possible with what you have today. Consider instead using a map structure instead of an array:

Fruits:
  Banana:
   title: "Banana"
   vitamins: {
     "potassium": true,
     "B6": true,
     "C": true
   }
Run Code Online (Sandbox Code Playgroud)

Then you could query like this:

db.collection("Fruits")
    .whereField("vitamins.B6", isEqualTo: true)
    .whereField("vitamins.C", isEqualTo: true)
Run Code Online (Sandbox Code Playgroud)

  • @LucasAzzopardi 这里唯一的缺陷是 Firestore 不允许超过 200 个复合索引,这意味着如果您只对维生素执行复合查询,那么应该没问题,因为没有数百种维生素。但是,例如,如果有 100 种维生素,并且您执行的复合查询要求每个查询都有一个复合索引,那么您将很快达到最大值,因为每个复合索引都必须指定特定的维生素。您不能简单地在复合索引中包含“维生素”,它必须是“维生素.钾”、“维生素.b6”等。 (6认同)
  • 这是一个很好的解决方案。但我尝试了一下,立即遇到了 @bsod 提到的复合索引问题。当我动态生成这些字段时,我无法为所有可能的字段创建索引。如果有任何解决办法,请告诉我!我刚刚用一个问题替换了另一个问题。 (3认同)

bso*_*sod 7

无法在 Firestore 中执行多个包含数组的查询。但是,您可以结合where尽可能多的条件。因此,请考虑一下:

[Fruits]
    <Banana>
        title: "Banana"
        vitamins:
            potassium: true
            b6: true
            c: true

Firestore.firestore().collection("Fruits").whereField("vitamins.potassium", isEqualTo: true).whereField("vitamins.b6", isEqualTo: true)
Run Code Online (Sandbox Code Playgroud)

然而,这仍然不允许进行干净的OR搜索。要查询含有维生素 X 或维生素 Y 的水果,您必须发挥更多创意。此外,如果有很多可能的值并且需要将它们与复合查询结合起来,则不应使用此方法。这是因为 Firestore 不允许超过 200 个复合索引,并且每个复合索引都需要指定确切的维生素。这意味着您必须为vitamins.b6vitamins.potassium等创建单独的复合索引,而总共只有 200 个。


fri*_*uck 5

Firestore 在 2019 年末引入了 whereIn、arrayContains 和 arrayContainsAny 方法。这些方法执行逻辑“或”查询,但您可以传入的子句限制为 10 个(因此您最多可以搜索 10 个维生素)。

一些解决方案已经提到重组您的数据。利用重组方法的另一个解决方案是不将维生素包含在 Fruit 文档中,而是反过来。通过这种方式,您可以获得“香蕉”所属的所有文件。

let vitaminsRef = db.collection('Vitamins').where('fruits', arrayContains: 'banana');
Run Code Online (Sandbox Code Playgroud)

此解决方案允许您绕过 10 个子句的限制。它在一次读取操作中获取所有在“水果”数组中包含“香蕉”的“维生素”文档(考虑分页,如果太多的话)。