我正在向我的应用程序添加聊天(没有群组,只有个人聊天(2 个用户之间)),如果一个用户删除了与其他用户的完整聊天,两个用户将无法阅读他们之前的对话,点赞发生在 Telegram 上.
有了这个用例,我根本不需要数据库非规范化。
这是我的 NoSql 模型:
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"
我遇到的问题是创建聊天室的逻辑。正如您所看到的,我有一个“成员”字段,其中包含两个用户的 ID。我想做的只是进行查询并尝试在聊天集合中找到两个用户(消息的发送者和消息的接收者)都是成员的房间。如果房间存在,我只需要添加消息即可。如果没有,我将不得不使用新的第一条消息创建房间。
例如,如果用户“Jack”要向用户“Josh”发送第一条消息(聊天室尚不存在),服务器将首先创建一个聊天室(两个用户都是成员),以及新的第一条消息作为数据。
但是...如果两个用户同时发送消息会发生什么? (不一致:可能会创建 2 个聊天室)。为了避免此类不一致,我在数据库事务中以原子方式运行此逻辑,如下所示:
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
});
});
但是,文档说
复合查询中最多可以包含一个 array-contains 或 array-contains-any 子句。
这就是我陷入困境的地方,因为我需要找到一个两个用户都是成员的房间......而我能想到的唯一其他方法是执行两个查询(OR)
where("member1", "==", fromId).where("memeber2", "==", toId)
// and
where("member1", "==", toId).where("memeber2", "==", fromId)
如何通过单个查询来完成这项工作?有什么想法吗?
谢谢你。
我没有使用数组,而是使用了映射,其中键是用户的 id,值是 true 布尔值。
members = {
"832384798238912789": true,
"90392p2010219021ud": true,
}
然后我查询:
.where(`members.${fromId}`, "==", true)
.where(`members${toId}`, "==", true)
我建议您在聊天文档中创建一个额外的字段,例如将两个用户 ID 连接起来。您可以在创建聊天室的同一事务中或使用 firebase 函数来完成此操作。 这将简化查询,因为您只需搜索这个新字段:
where("membersconcat", "==", fromId+toId)
我花了一段时间才接受这些额外的字段,但它确实避免了很多与 Firestore 查询的限制作斗争的情况
我发现如果你像这样构造数据而不是数组:
members = {
"832384798238912789": "832384798238912789",
"90392p2010219021ud": "90392p2010219021ud",
}
您可以使用
array-contains
创建查询并检查值是否存在(本质上与 array-contains 相同)。
所以这个:
.where(`members.${fromId}`, "==",`${fromId}`)
.where("members", "array-contains", toId)
应该像你正在运行这个一样:
.where("members", "array-contains", fromId)
.where("members", "array-contains", toId)