Firebase 查询子项的子项是不是包含值

Posted

技术标签:

【中文标题】Firebase 查询子项的子项是不是包含值【英文标题】:Firebase query if child of child contains a valueFirebase 查询子项的子项是否包含值 【发布时间】:2020-07-26 10:00:28 【问题描述】:

表的结构是:

聊天 --> 随机标识符 -->--> 参与者 -->-->--> 0: 'name1' -->-->--> 1: 'name2' -->--> 聊天项目

我想要做的是查询聊天表以通过传入的用户名字符串查找所有与参与者保持联系的聊天。

这是我目前所拥有的:

 subscribeChats(username: string) 
    return this.af.database.list('chats', 
        query: 
            orderByChild: 'participants',
            equalTo: username, // How to check if participants contain username
        
    );
 

【问题讨论】:

【参考方案1】:

您当前的数据结构非常适合查找特定聊天的参与者。然而,它不是一个很好的查找逆向结构:用户参与的聊天。

这里有几个问题:

您将 set 存储为数组 您只能在固定路径上编制索引

集合 vs 数组

一个聊天可以有多个参与者,因此您将其建模为一个数组。但这实际上并不是理想的数据结构。可能每个参与者只能在聊天中出现一次。但是通过使用数组,我可以:

participants: ["puf", "puf"]

这显然不是您的想法,但数据结构允许这样做。您可以尝试在代码和安全规则中保护这一点,但如果您从隐式匹配模型更好的数据结构开始会更容易。

我的经验法则:如果你发现自己在写array.contains(),你应该使用集合

集合是一种结构,其中每个孩子最多只能出现一次,因此它自然可以防止重复。在 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

这是 Cloud Firestore 更好地支持此类查询的情况之一。它的array-contains 运算符允许过滤数组中具有特定值的文档,而arrayRemove 允许您将数组视为一个集合。有关这方面的更多信息,请参阅Better Arrays in Cloud Firestore。

【讨论】:

这是一个很棒的解释。我的数据是这样存储的,它允许我从用户列表中获取聊天室,或者从聊天室成员中获取用户。但它不允许在我所属的聊天室中观察.childChanged。如果我尝试为members/myId = true 的聊天设置观察者,则会收到未指定的索引错误。你会怎么做?我在这里发布了一个问题:***.com/questions/47769044/… @frank-van-puffelen 引用您的上一个代码 sn-p,假设我是“user1”,并且我获取了我所属的聊天室的所有密钥。然后对于每个聊天室键,我获取完整的聊天室对象,并在列表中显示聊天室列表。这很好,但是,如果我还需要访问作为聊天室一部分的完整用户对象怎么办。似乎很奇怪,我必须为每个聊天室中的每个用户执行另一个 for 循环(2 级嵌套 for 循环!)获取该数据。有没有更好的办法? 很好的解释。这是一个很好的例子,说明您需要如何对数据进行不同的建模以利用 NoSQL(以及使用它的廉价平台)。

以上是关于Firebase 查询子项的子项是不是包含值的主要内容,如果未能解决你的问题,请参考以下文章

firebase 查询如何从子项中检索两个特定值

检查firebase子项是不是具有特定值[重复]

Firebase:按儿童计数排序的查询

如何查询 Firebase 中不存在子项的位置? [复制]

如何更新 Firebase DB 中子项的特定值

选择所有子项仅包含相同值且没有其他方案的行