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

Viewed 3147

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.

2 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 :)

An alternative is to have two fields in the document, one for lowest alphabetically USer ID (LAUID) and one for highest alphabetically user id (HAUID). If you have both user IDs on the client, but don't know which one is which, you can figure out Which Id is greater of the two (UID2) and which is the smallest (UID1) , then your query will be where HAUID equals UID2 and where LAUID equals UID1

Related