Firestore search array contains for multiple value

2020-02-04 17:20发布

If I have a simple collection such as:

Fruits:
  Banana:
   title: "Banana"
   vitamins: ["potassium","B6","C"]
  Apple:
   title: "Apple"
   vitamins: ["A","B6","C"]

And if I want to search fruits that contain vitamin B6, I would do the following:

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())")
            }
        }
    }

And that would show me all the Fruits in my collection that contain the vitamin A. However, how would I be able to search fruits that contain multiple vitamins, say vitamins B6 and C? I cannot simply search ["B6","C"] as it would then be looking for an array as opposed to independent strings.

Is this even possible in Cloud Firestore? If not is there any alternative way to do this?

3条回答
可以哭但决不认输i
2楼-- · 2020-02-04 17:38

There is no way to do multiple array-contains queries like you're asking in Firestore. You can, however, combine as many where conditions as practically possible. Therefore, consider this:

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)

This, however, does not allow you to do a clean or search, just and. To query for fruits with vitamin-x or vitamin-y, you'd have to get more creative. Furthermore, this approach should not be used in situations where there are "a lot" of possible values and they need to be combined with compound queries. That is because Firestore does not permit more than 200 composite indices and each composite index would need to specify the exact vitamin. For example, you'd have to create an individual composite index for vitamins.b6, vitamins.potassium, etc. and you only have 200 total.

查看更多
叼着烟拽天下
3楼-- · 2020-02-04 17:43

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")

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
   }

Then you could query like this:

db.collection("Fruits")
    .whereField("vitamins.B6", isEqualTo: true)
    .whereField("vitamins.C", isEqualTo: true)
查看更多
Luminary・发光体
4楼-- · 2020-02-04 18:01

There is no way in Firestore to query a Firestore database with more than one arrayContains. If you will use more than one, an error like this might occur:

Invalid Query. Queries only support having a single array-contains filter.

If you need to filter on more than one vitamins, you'll need to change the logic of structuring your database by creating a property for each individual vitamin that you have and then chain .whereField() function calls. I know it sounds a little weird but this is how Cloud Firestore works.

Your schema should like this:

vitamins: { 
    "potassium": true,
    "B6": true,
    "C": true
}

To find all the fruits with potassium, B6 and C vitamins, you should use a query that looks like this:

let fruitsRef = db.collection("Fruits")
    .whereField("vitamins.potassium", isEqualTo: true)
    .whereField("vitamins.B6", isEqualTo: true)
    .whereField("vitamins.C", isEqualTo: true)
查看更多
登录 后发表回答