28

Here is the schema for a test database:

Enter image description here

I want to write a query that can get all the documents where contacts have id=1 in any of the array index.

I have checked array_contains operator for Firestore, but my array has a map which then have field id.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
john
  • 2,324
  • 3
  • 20
  • 37

4 Answers4

30

You currently can't build a query that looks for an object field within an array. The only things you can find in an array are the entire contents of an array element, not a part of an element.

With NoSQL type databases, the usual practice is to structure you data in a way that suits your queries. So, in your case, you're going to have to structure your data to let you find documents where an array item contains a certain string. So, you could create another array that contains just the strings you want to query for. You will have make sure to keep these arrays up to date.

You could also reconsider the use of an array here. Arrays are good for positional data, but unless these contacts need to be stored in a certain order, you might want to instead store an object whose keys are the id, and whose field data also contains the id and name.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Thanks @Doug Stevenson - exactly what I was looking for. To clarify - it's not possible to query based on the keys of a map, right? (i.e. Only way is to create a separate array of only these keys) – Jbbae Mar 18 '19 at 05:13
  • 3
    Or a separate object where the keys map to something like true/false. – Doug Stevenson Mar 18 '19 at 05:24
  • @DougStevenson out of interest, considering you still cannot query based on the keys of a map, what is the benefit of converting this to one? Either way you need the additional array of ids kept in sync. Unless of course you can foresee needing constant time lookup. – Scott Fister May 10 '20 at 16:05
8

As Doug said, you can't query it, but, if you could structure your data to something that looks like this

  1. Store your data as a map
  2. Use id as key and name as value
  3. Now you can write a query that can get all the documents where contacts have id=1
db.collection("test").where("contacts.1", ">=", "").get()
Christopher Moore
  • 15,626
  • 10
  • 42
  • 52
MOhan
  • 281
  • 4
  • 3
2

If you have an array of maps or objects and want to get the docoments that contains this specific map/object like this,

array_of_maps.png

you can use:

queryForCategory(categName: string, categAlias: string): Observable[] {
    firestore.collection('<Collection name>', ref =>

        ref.where('categories',
          "array-contains", {
            "alias": categAlias,
            "title": categName
          }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hassan Gad
  • 21
  • 2
1

One solution is, as Doug said, to add an array with the data you need to query, and keep it up-to-date.

Another solution is to make contacts as a sub collection of the document, and_ then you could make queries of the contacts and get its parent.

Enter image description here

final Query contacts = db.collectionGroup("contacts").whereEqualTo("id", 1);
for (DocumentSnapshot document : querySnapshot.get().getDocuments()) {
  System.out.println(document.getId());
  System.out.println(document.getString("parent_id"));
}

If you don't want to add the parent as another field, you can get it from the parent reference. Check this answer to Firestore - get the parent document of a subcollection.

snapshot.getRef().getParent().getParent().collection("people")

Collection group queries

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Borislav Gizdov
  • 1,323
  • 12
  • 22