35

I want to store data in following format:

{
   "chatName": "Football",
   "chatMembers":
   [
      {
         "userId": "nSWnbKwL6GW9fqIQKREZENTdVyq2",
         "name": "Niklas"
      },
      {
         "userId": "V3QONGrVegQBnnINYHzXtnG1kXu1",
         "name": "Timo"
      },
   ]
} 

My goal is to get all chats, where the signed in user with a userId is in the chatMembers list. If the userId of the signed in user is not in the chatMembers property, then that chat should be ignored. Is this possible?

If this is not possible, how can i achive this with subcollections?

My development language is dart, but you can also post solutions in other languages.

My current attempt is this, but this is not working:

_firestore.collection(collectionName).where("chatMembers.userId", isEqualTo: userId).snapshots()
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
Niklas Raab
  • 1,576
  • 1
  • 16
  • 32

2 Answers2

29

Since August 2018 there is the new array_contains operator which allows filtering based on array values. The doc is here: https://firebase.google.com/docs/firestore/query-data/queries#array_membership

It works very well with arrays of string. However, I think it is not possible to query for a specific property of an object stored in the array. One workaround is to query for the entire object, as follows (in Javascript). Of course this may not be feasible in every situation....

    var db = firebase.firestore();
    var query = db.collection('chatDocs').where("chatMembers", "array-contains", { userId: "xyz", userName: "abc" });
Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
  • See https://stackoverflow.com/questions/51924896/firestore-flutter-array-contains it may help – Renaud Tarnec Oct 20 '18 at 13:44
  • 2
    your firestore model must match exactly how you query the collections document property unfortunately :( They should fix this. – Jason Jul 28 '20 at 05:47
  • 1
    @Jason If you cannot query with the entire object, you could duplicate data, i.e. add to the document an extra field with the value you want to search for. – Renaud Tarnec Jul 28 '20 at 06:46
  • 3
    It's really unfortunate that it doesn't support querying for a single field within the object that's inside the array. – emirhosseini Aug 21 '20 at 16:00
27

Renaud Tarnec's, which complete, doesn't work in every case. Situations where only one or not all of the fields are known won't return the expected documents. However, by restructuring the data in the document, a query can be made to work where only one field is known, like a user identifier (uid).

Here's the data structure inside one of the document:

{
  "members": {
    "user4": {
      "active": true,
      "userName": "King Edward III",
      "avatar": "www.photos.gov/animeGirl5.png"
    },
    "user7": {
      "active": true,
      "userName": "Dave K.",
      "avatar": "www.photos.gov/gunsAmericanFlag.png"
    }
  }

Here's the query:

uid = 'user4';
collectionQuery = collectionReference.where(`members.${uid}.active`,"==", true);

In this example, "user4" represents a user who may or may not be in a group. This will return all documents in the collection where the uid "user4" is an active member. This works by only needing to know the UID of the member and works without needing to know their name or avatar uri ahead of time.

Brett S
  • 579
  • 4
  • 8
  • Is this the only solution? – bg9848 Jul 09 '20 at 10:10
  • this defeats the purpose of querying – Jason Jul 28 '20 at 05:47
  • 2
    Note that the OP actually executes a query on an Array field. – Renaud Tarnec Aug 21 '20 at 17:45
  • 1
    You are not wrong, the OP does query on an Array field, however the OP has a data structure where they do have a unique key in their array-nested maps, and if they restructured to a map-nested map where the first level map key was the unique key they already have, then they have what they *actually* want vs just an answer to their question. So I think this answer is still good, it helped me think about this problem (which I am having right now!), while the accepted answer obviously does answer the literal question. Cheers – Mike Hardy Dec 02 '21 at 22:36