该表的 struct 如下:

  • 聊天
  • --> randomId
  • -->--> participants
  • -->-->--> 0: 'name1'
  • -->-->--> 1: 'name2'
  • -->--> chatItems

我试图做的是查询聊天表,以通过传入的用户名字符串查找包含参与者的所有聊天.

以下是我目前掌握的情况:

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

推荐答案

您当前的数据 struct 非常适合查找特定聊天的参与者.然而,对于查找相反的内容:用户参与的聊天,它不是一个很好的 struct .

这里有几个问题:

  • 您将set存储为一个数组
  • 只能在固定路径上建立索引

Set vs array

一个聊天可以有多个参与者,所以您将其建模为一个array.但这实际上并不是理想的数据 struct .很可能每个参与者只能参与一次聊天.但通过使用数组,我可以:

participants: ["puf", "puf"]

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

我的经验法则是:if you find yourself writing 100, you should be using a set.

集合是一种 struct ,每个子元素最多只能出现一次,因此它自然可以防止重复.在Firebase中,您可以将集合建模为:

participants: {
  "puf": true
}

这里的true实际上只是一个虚拟值:重要的是我们已经将名称移到了键上.现在,如果我再试着加入这个聊天,那我就不会同意了:

participants: {
  "puf": true
}

当你加入时:

participants: {
  "john": true,
  "puf": true
}

这是对您的需求最直接的表示:一个只能包含每个参与者一次的集合.

You can only index known properties

使用上面的 struct ,您可以could查询与您在一起的聊天内容:

ref.child("chats").orderByChild("participants/john").equalTo(true)

问题是,这需要在"参与者/约翰"上定义一个索引:

{
  "rules": {
    "chats": {
      "$chatid": {
        "participants": {
          ".indexOn": ["john", "puf"]
        }
      }
    }
  }
}

这将起作用,表现出色.但现在每次有新用户加入聊天应用程序时,你都需要添加另一个索引.这显然不是一个可伸缩的模型.我们需要更改数据 struct 以允许您进行所需的查询.

Invert the index - pull categories up, flattening the tree

第二条经验法则:model your data to reflect what you show in your app.

由于您希望为用户显示聊天室列表,请存储每个用户的聊天室:

userChatrooms: {
  john: {
    chatRoom1: true,
    chatRoom2: true
  },
  puf: {
    chatRoom1: true,
    chatRoom3: true
  }
}

现在,您只需通过以下方式确定聊天室列表:

ref.child("userChatrooms").child("john")

然后把 keys 循环到每个房间.

您的应用程序中会有两个相关列表:

  • 特定用户的聊天室列表
  • 特定聊天室中的参与者列表

在这种情况下,您还会在数据库中同时拥有这两个列表.

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.

Angular相关问答推荐

如何go 除融合图中的拖尾水印?

间歇性不正确的SSR重定向(server. ts级别的请求和响应不匹配)

尾风手风琴中的Ngor

Angular 15:将数据从主零部件传递到辅零部件

更改动态表单控制Angular 的值

Angular 17:延迟加载带参数的独立组件?

在Cypress中,对xlsx文件的读取并不总是正确的

无法在 app.component.html 中呈现自定义组件

使用 Angular 类构造函数来获取依赖项

Angular Mat Select 显示键盘焦点和鼠标悬停焦点的问题

从 applescript 调用Angular 前端以从列表中 Select 一个项目

Angular14:支持旧版浏览器的官方方式是什么?

等待两个订阅完成而不嵌套

如何在 Angular 中创建通用的可重用组件

RxJS 基于先前的数据响应执行请求(涉及循环对象数组)

刷新页面导致 401 错误-Angular 8

使用 NPM 安装 Font Awesome 5

当父组件上的变量发生变化时重新加载子组件

如何在 ANGULAR 2 中提交表单时重置表单验证

动态添加/删除验证器