简体   繁体   English

Firebase Firestore - 复合查询中包含多个数组

[英]Firebase Firestore - Multiple array-contains in a compound query

Introduction介绍

I am adding a chat to my app (without groups, only individuals chats (between 2 users)) where if one user deletes his full chat with other user, both users will not be able to read their previous conversation, likes happens on Telegram.我正在向我的应用程序添加聊天(没有群组,只有个人聊天(在 2 个用户之间)),如果一个用户删除他与另一个用户的完整聊天,两个用户将无法阅读他们之前的对话,就像 Telegram 上发生的那样。

Database model数据库 model

With this use case, I don't need database denormalization at all.对于这个用例,我根本不需要数据库反规范化。

This is my NoSql model:这是我的 NoSql model:

usernames/ (collection) 
     jack/ (document)
         -userId: "41b932028490"
     josh/ (document)
         -userId: "212930294348"

users/ (collection)
     41b932028490/ (document)
         -avatar: {uri}
         -username: jack
         -...other data
     212930294348/ (document)
         -avatar: {uri}
         -username: "josh"
         -...other data

chats/ (collection)
      chatId/ (document) <------------ (Some kind of chat room)
           -members: ["41b932028490", "212930294348"]
           messages/ (sub-collection)
               message1Id/ (document)
                   -userId: "41b932028490" // sender
                   -type: "text" 
                   -text: "hello world!"

               message2Id/ (document)
                   -userId: "41b932028490" // sender
                   -type: "GIF"
                   -uri: "https://media.giphy.com/media/HP7mtfNa1E4CEqNbNL/giphy.gif"

Problem问题

The problem I am having is with the logic to create the chat rooms.我遇到的问题是创建聊天室的逻辑。 As you can see, I have a field "members", with the ids of both users.如您所见,我有一个字段“成员”,其中包含两个用户的 ID。 What I have thought to do is just making a query and trying to find the room where both users (the message's sender and the message's receiver) are members, in the chats collection.我想做的只是进行查询并尝试在聊天集合中找到两个用户(消息的发送者和消息的接收者)都是成员的房间。 If the room exists, I will only have to add the message.如果房间存在,我只需要添加消息。 If not, I will have to create the room with the new first message.如果没有,我将不得不用新的第一条消息创建房间。

For example, if a user "Jack" is going to send a first message to the user "Josh" (the chat room doesn't exist yet), the server will firstly create a chat room (with both users as members), and the new first message as data.例如,如果用户“Jack”要向用户“Josh”发送第一条消息(聊天室还不存在),服务器将首先创建一个聊天室(两个用户都是成员),并且新的第一条消息作为数据。

But... what would happen if both users send the message at the same time?但是......如果两个用户同时发送消息会发生什么? (Inconsistencie: 2 chat rooms might be created). (不一致:可能会创建 2 个聊天室)。 To avoid those kinds of inconsistencies, I am running this logic atomically, in a database transaction, like this:为了避免这些类型的不一致,我在数据库事务中以原子方式运行此逻辑,如下所示:

await firestore.runTransaction((transaction) => {
      // Does the chat room exists in the chats' collection?
      const chatsRef = firestore.collection("chats");
      return chatsRef
        .where("members", "array-contains", fromId) <--------- PROBLEM!
        .where("members", "array-contains", toId) <----------- PROBLEM!
        .limit(1)
        .get()
        .then((snapshot) => {
          if (!snapshot.empty) {
            // If yes, add the message to the chat room
            const chatRoom = snapshot.docs[0];
            const chatRoomPath = chatRoom.ref.path;
            const newMessageRef = firestore
              .doc(chatRoomPath)
              .collection("messages")
              .doc(); // Auto-generated uuid

            const newMessageData = {
              userId: fromId,
              message,
              date: admin.firestore.FieldValue.serverTimestamp(),
            };

            transaction.set(newMessageRef, newMessageData);
          } else {
            // TODO - Check that the receiver exists in the database (document read)
            // TODO - If exists -> create chat room with the new message
            // TODO - If not exists -> throw error
          }

          // TODO - Increment the chat counter for the receiver when adding the message
        });
    });

But, the docs says但是,文档说

you can include at most one array-contains or array-contains-any clause in a compound query.您最多可以在复合查询中包含一个 array-contains 或 array-contains-any 子句。

This is where i'm stuck, as I need to find a room with both users as members... and the only other way I can think of is to perform two queries (an OR)这就是我卡住的地方,因为我需要找到一个有两个用户都是成员的房间......我能想到的唯一其他方法是执行两个查询(OR)

 where("member1", "==", fromId).where("memeber2", "==", toId)

 // and

 where("member1", "==", toId).where("memeber2", "==", fromId)

How can I make this work with a single query?我怎样才能用一个查询来完成这项工作? Any ideas?有任何想法吗?

Thank you.谢谢你。

I suggest you create an extra field in the chats document with for example a concatenation of both user ids.我建议您在聊天文档中创建一个额外的字段,例如将两个用户 ID 串联起来。 You can do it in the same transaction of the chat room creation or with a firebase function. This would simplify the query since you just search for this new field:您可以在创建聊天室的同一笔交易中或使用 firebase function 执行此操作。这将简化查询,因为您只需搜索这个新字段:

where("membersconcat", "==", fromId+toId)

It took me a while to accept these kind of extra fields but it really avoid a lot of struggling with limitations of firestore queries我花了一段时间才接受这些额外的字段,但它确实避免了很多与 firestore 查询限制的斗争

Instead of using an array, I have used a map where the key is the user's id, and the value a true boolean.我没有使用数组,而是使用了 map,其中键是用户的 ID,值为真 boolean。

 members = {
    "832384798238912789": true,
    "90392p2010219021ud": true,
 }

Then I query like:然后我查询如下:

 .where(`members.${fromId}`, "==", true)
 .where(`members${toId}`, "==", true)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM