该表的结构是:聊天--> randomId-->--> 参与者-->-->--> 0:'name1'-->-->--> 1:'name2'-->--> chatItems 等我想做的是查询聊天表以查找所有
该表的结构如下:
ETC
我想要做的是查询聊天表以通过传入的用户名字符串查找包含参与者的所有聊天。
以下是我目前所掌握的信息:
subscribeChats(username: string) {
return this.af.database.list('chats', {
query: {
orderByChild: 'participants',
equalTo: username, // How to check if participants contain username
}
});
}
您当前的数据结构非常适合查找特定聊天的参与者。但是,它不是一个很好的结构,无法查找相反的内容:用户参与的聊天。
这里有几个问题:
一个聊天可以有多个参与者,因此你将其建模为一个数组。但这实际上不是理想的数据结构。每个参与者可能只能参与一次聊天。但通过使用数组,我可以:
participants: ["puf", "puf"]
这显然不是您想要的,但数据结构允许这样做。您可以尝试在代码和安全规则中确保这一点,但如果您从隐式匹配模型的数据结构开始,会更容易。
我的经验法则: if you find yourself writing array.contains()
, you should be using a set .
集合是一种结构,其中每个子元素最多只能出现一次,因此它自然可以防止出现重复。在 Firebase 中,你可以将集合建模为:
participants: {
"puf": true
}
这里 true
实际上只是一个虚拟值:重要的是我们已将名称移至键。现在,如果我再次尝试加入此聊天,则会出现错误:
participants: {
"puf": true
}
当你加入时:
participants: {
"john": true,
"puf": true
}
这是您的要求最直接的表述:一个只能包含每个参与者一次的集合。
通过上述结构,您 可以 查询您正在参与的聊天:
ref.child("chats").orderByChild("participants/john").equalTo(true)
问题在于,这需要您在“participants/john\”上定义一个索引:
{
"rules": {
"chats": {
"$chatid": {
"participants": {
".indexOn": ["john", "puf"]
}
}
}
}
}
这会起作用并且性能很好。但现在每次有新人加入聊天应用程序时,您都需要添加另一个索引。这显然不是一个可扩展的模型。我们需要更改数据结构以允许您想要的查询。
第二条经验法则: 对数据进行建模以反映您在应用中显示的内容 .
由于您希望显示用户的聊天室列表,因此请存储每个用户的聊天室:
userChatrooms: {
john: {
chatRoom1: true,
chatRoom2: true
},
puf: {
chatRoom1: true,
chatRoom3: true
}
}
现在,您可以轻松使用以下方法确定您的聊天室列表:
ref.child("userChatrooms").child("john")
然后循环遍历钥匙以获取每个房间。
您将希望在您的应用中拥有两个相关列表:
在这种情况下,您还将在数据库中拥有这两个列表。
chatroomUsers
chatroom1
user1: true
user2: true
chatroom2
user1: true
user3: true
userChatrooms
user1:
chatroom1: true
chatroom2: true
user2:
chatroom1: true
user2:
chatroom2: true
我已将两个列表拉到树的顶层,因为 Firebase 建议不要嵌套数据。
在 NoSQL 解决方案中,同时拥有这两个列表是完全正常的。在上面的例子中,我们将以下列表称为 userChatrooms
倒排索引: chatroomsUsers
.
这是 Cloud Firestore 对此类查询提供更好支持的案例之一。其 array-contains
运算符允许过滤数组中具有特定值的文档,同时 arrayRemove
允许您将数组视为集合。有关更多信息,请参阅 Cloud Firestore 中的更好的数组 .