Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to query for more than one item in an array in Firestore? ("array-contains")

I am building a 1-to-1 chat application with Firestore and Javascript (vue.js).

I have a conversation and a message document defined as follows:

.collection("coversation").doc()

conversation: {
  participantsArray: ["id1", "id2"],
  participants: {
    id1: {
      /* details like avatar & name */
    },
    id2: {
      /* details like avatar & name */
    }
  }
}

.collection("coversation").doc("foo").collection("messages").doc()

message: {
  message: "foo"
}

This is great, so I can now query for all conversations of a specific user like so:

db.collection('conversations').where("participantsArray", "array-contains", userId)

The issue I am having now is the following:

I want to query for a specific conversation, like so:

db.collection('conversations')
  .where("participantsArray", "array-contains", userId)
  .where("participantsArray", "array-contains", chatpartnerId)

But this does not work since double "array-contains" is not allowed and throws an error message.

What works for me for now is to make two calls like so:

db.collection('conversations')
  .where(`participantsArray`, '==', [userId, chatpartnerId])

db.collection('conversations')
  .where(`participantsArray`, '==', [chatpartnerId, userId])

and then check if any of these queries returns a conversation. If so, I use that conversation to add a message, otherwise I create a new conversation.

It works, but it's a bunch of code and quite inefficient.

So my question would be:

  1. Is there a better way to do this?
  2. How to do "array-contains" to check for multiple items?
  3. I have both a participantsArray and a participants field because I did not figure out how to do "array-contains" on an array of objects. Is there a way or should I keep both fields?

PS: Before, I used to query on the objects in the participants object like so:

db.collection("conversations").where(`participants.${userId}.id`, "==", userId)

This was the proposed way to query before "array-contains" existed - but when combined with orderBy I would have to create an index for each user, which is not really an option.

like image 234
Pascal Avatar asked Aug 24 '18 19:08

Pascal


People also ask

How do I query multiple values in firestore?

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.

What is QuerySnapshot in firebase?

A QuerySnapshot contains zero or more QueryDocumentSnapshot objects representing the results of a query. The documents can be accessed as an array via the docs property or enumerated using the forEach method. The number of documents can be determined via the empty and size properties.

How big can an array be in firestore?

The array [1, 2, 3] has elements equal to the first three elements of [1, 2, 3, 1] but is shorter in length. Up to 1,048,487 bytes (1 MiB - 89 bytes). Only the first 1,500 bytes are considered by queries.


1 Answers

I found a way around how to query for a single conversation.

Since I have both (1) the participants with the details in an "participants" object and (2) the participant ID's in "participantsArray", I can query for a single conversation as follows:

db.collection('conversations')
      .where(`participants.${userId}.id`, "==", userId)
      .where(`participants.${chatPartnerId}.id`, "==", chatPartnerId)

So I am happy for now. However, the three questions remain, so if someone can answer them properly their answer would be very valuable :)

like image 186
Pascal Avatar answered Oct 26 '22 23:10

Pascal