Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firestore search array contains for multiple values

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?

like image 315
Lucas Azzopardi Avatar asked Mar 04 '19 16:03

Lucas Azzopardi


People also ask

How do I query multiple values in firestore?

Starting with… in queries! With the in query, you can query a specific field for multiple values (up to 10) in a single query. You do this by passing a list containing all the values you want to search for, and Cloud Firestore will match any document whose field equals one of those values.

How do I use array-contains in firestore?

Firebase introduced an array-contains operator that can be used with where to query array fields. It will return all documents that contain a the provided value in the array. Currently, this is only supported with one value, so don't try chaining more than one of these in a single query. const col = firestore.

How do I query a collection in firestore?

To start querying a collection, you need to provide the hooks with a CollectionReference or Query reference created directly from the firebase/firestore library. The reference can be a simple collection pointer or a fully constrained query using the querying functionality provided by the Firestore SDK.

Are arrays indexed in firestore?

Automatic indexing For each map field, Cloud Firestore creates one collection-scope ascending index and one descending index for each non-array and non-map subfield in the map. For each array field in a document, Cloud Firestore creates and maintains a collection-scope array-contains index.


2 Answers

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) 
like image 60
Doug Stevenson Avatar answered Sep 22 '22 23:09

Doug Stevenson


Firestore introduced the whereIn, arrayContains and arrayContainsAny methods in late 2019. These methods perform logical 'OR' queries, but have a limit of 10 clauses you can pass in (so max 10 vitamins you can search for).

Some solutions already mentioned restructuring your data. Another solution leveraging the restructuring approach is to not include the Vitamins into the Fruit document, but the other way around. This way you get all the documents 'Banana' is part of.

let vitaminsRef = db.collection('Vitamins').where('fruits', arrayContains: 'banana'); 

This solution allows you to circumvent the limit of 10 clauses. It gets all the 'Vitamin' documents that have 'Banana' in their 'Fruits' array in one read operation (think about pagination, if too many).

like image 22
friartuck Avatar answered Sep 19 '22 23:09

friartuck