Firebase 查询子项的子项是否包含值
Firebase query if child of child contains a value
table 的结构是:
- 聊天
- --> 随机编号
- -->--> 参与者
- -->-->--> 0: 'name1'
- -->-->--> 1: 'name2'
- -->--> 聊天项目
等等
我想做的是查询聊天 table 以通过传入的用户名字符串查找所有包含参与者的聊天。
这是我目前的情况:
subscribeChats(username: string) {
return this.af.database.list('chats', {
query: {
orderByChild: 'participants',
equalTo: username, // How to check if participants contain username
}
});
}
您当前的数据结构非常适合查找特定聊天的参与者。然而,它不是一个很好的逆向结构:用户参与的聊天。
这里有几个问题:
- 您正在将集合存储为数组
- 您只能在固定路径上建立索引
集合与数组
一次聊天可以有多个参与者,因此您将其建模为一个数组。但这实际上并不是理想的数据结构。可能每个参与者只能参与一次聊天。但是通过使用数组,我可以:
participants: ["puf", "puf"]
这显然不是您想要的,但数据结构允许这样做。您可以尝试在代码和安全规则中保护这一点,但如果您从隐含地更好地匹配您的模型的数据结构开始,这会更容易。
我的经验法则:如果你发现自己在写 array.contains()
,你应该使用集合。
集合是一种结构,其中每个 child 最多可以出现一次,因此它自然可以防止重复。在 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
我已将两个列表都拉到树的 top-level 中,因为 Firebase 不建议嵌套数据。
在 NoSQL 解决方案中同时拥有两个列表是完全正常的。在上面的示例中,我们将 userChatrooms
称为 chatroomsUsers
.
的倒排索引
云 Firestore
这是 Cloud Firestore 更好地支持此类查询的案例之一。它的 array-contains
运算符允许过滤在数组中具有特定值的文档,而 arrayRemove
允许您将数组视为一个集合。有关更多信息,请参阅 Better Arrays in Cloud Firestore.
table 的结构是:
- 聊天
- --> 随机编号
- -->--> 参与者
- -->-->--> 0: 'name1'
- -->-->--> 1: 'name2'
- -->--> 聊天项目
等等
我想做的是查询聊天 table 以通过传入的用户名字符串查找所有包含参与者的聊天。
这是我目前的情况:
subscribeChats(username: string) {
return this.af.database.list('chats', {
query: {
orderByChild: 'participants',
equalTo: username, // How to check if participants contain username
}
});
}
您当前的数据结构非常适合查找特定聊天的参与者。然而,它不是一个很好的逆向结构:用户参与的聊天。
这里有几个问题:
- 您正在将集合存储为数组
- 您只能在固定路径上建立索引
集合与数组
一次聊天可以有多个参与者,因此您将其建模为一个数组。但这实际上并不是理想的数据结构。可能每个参与者只能参与一次聊天。但是通过使用数组,我可以:
participants: ["puf", "puf"]
这显然不是您想要的,但数据结构允许这样做。您可以尝试在代码和安全规则中保护这一点,但如果您从隐含地更好地匹配您的模型的数据结构开始,这会更容易。
我的经验法则:如果你发现自己在写 array.contains()
,你应该使用集合。
集合是一种结构,其中每个 child 最多可以出现一次,因此它自然可以防止重复。在 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
我已将两个列表都拉到树的 top-level 中,因为 Firebase 不建议嵌套数据。
在 NoSQL 解决方案中同时拥有两个列表是完全正常的。在上面的示例中,我们将 userChatrooms
称为 chatroomsUsers
.
云 Firestore
这是 Cloud Firestore 更好地支持此类查询的案例之一。它的 array-contains
运算符允许过滤在数组中具有特定值的文档,而 arrayRemove
允许您将数组视为一个集合。有关更多信息,请参阅 Better Arrays in Cloud Firestore.