在 Firebase 中构建聊天应用程序的数据

Posted

技术标签:

【中文标题】在 Firebase 中构建聊天应用程序的数据【英文标题】:Structuring data for chat app in Firebase 【发布时间】:2016-09-27 15:16:33 【问题描述】:

我正在关注 structuring data 的 Firebase 指南以获取聊天应用程序。他们提出了如下所示的结构。


  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": 
    "one": 
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    ,
    "two":  ... ,
    "three":  ... 
  ,

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": 
    // we'll talk about indices like this below
    "one": 
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    ,
    "two":  ... ,
    "three":  ... 
  ,

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // converation ID
  "messages": 
    "one": 
      "m1": 
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      ,
      "m2":  ... ,
      "m3":  ... 
    ,
    "two":  ... ,
    "three":  ... 
  

我如何构建我的用户数据,以便我可以轻松地显示他们所属的所有聊天的列表,并为每个聊天显示最后一条消息和时间戳。如果我执行以下结构:

   "users": 
    "ghopper": 
      "name": "Gary Hopper",
      "chats": 
          "one: true",
          "two": true
      
    ,
    "alovelace"  ... 
  ,

我可以通过以下方式轻松获取特定用户(例如 ghopper)的每个聊天组的列表:

ref.child("users").child("ghopper").child("chats").observeSingleEventOfType(.Value, withBlock:  (snapshot) in
  //do something with data

但是,我不会在此快照中包含 lastMessage 和时间戳。我需要做什么才能访问这些数据?

为每个用户复制所有这些数据?即添加 users/ghopper/chats/one/ "lastMessage": "ghopper: Relay failure found. Cause: moth.", "timestamp" : 1459361875666 为用户参与的每个聊天查询“chats/specificGroupId”(添加多个侦听器)? 其他方式?

【问题讨论】:

【参考方案1】:

如何组织我的用户数据,以便我可以轻松地显示列表 他们参与的所有聊天,并且每个聊天都显示 最后一条消息和时间戳。

通过添加聊天节点中的用户来稍微改变聊天结构

"chats": 
    "one": 
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
      users
       uid_1: true
       uid_3: true
    ,
    "two":  ... ,

然后您可以深入查询特定用户参与的所有聊天 - 这将返回 uid_3 参与的聊天

chatsRef.queryOrderedByChild("users/uid_3").queryEqualToValue(true)
     .observeSingleEventOfType(.Value, withBlock:  snapshot in

     //.Value can return multiple nodes within the snapshot so iterate over them
     for child in snapshot.children 
          let lastmsg = child.value["lastMessage"] as! String
          let timestamp = child.value["timestamp"] as! String
          print(lastmsg)
          print(timestamp)
          
)

请注意,当通过 auth.uid 创建用户时,每个 firebase 用户都有一个谨慎的用户 ID。这应该(通常)用作每个用户的密钥。

【讨论】:

您好,Jay,感谢您的回答。我已经尝试过您的解决方案,它似乎可以解决问题。但是考虑到我需要查看所有“聊天/”才能找到用户参与的所有聊天,一旦我有很多用户,这个操作会不会变得太昂贵? @Nilsymbol 很高兴它成功了!看起来您并没有拉下兆字节的数据,只是几百个带有一些子节点的节点。 Firebase 速度非常快,因此在数千个节点上进行这样的查询非常快;它甚至不会让 Firebase 呼吸困难。我们已经测试过了! @Jay 我了解大部分结构,只是不了解用户在“聊天”子项中的表示方式。它是用户 uid 的数组吗?您能否再解释一下布尔值与 uid 的关联?非常感谢! -科尔比 @Jay 忽略最后的评论,我现在明白了。一个小小的实验消除了我的困惑。谢谢! @Jay 安全性如何融入其中?如果我的用户不是聊天的一部分并且我将自己添加到该聊天中 - 这意味着我可以查询它 - 这意味着所有用户都可以阅读所有聊天。如果现有聊天成员添加了我 - 那么该聊天成员必须知道我的用户 ID - 这意味着所有用户都可以读取所有用户。有没有解决的办法?如何更新索引?【参考方案2】:

在您拥有用户所在的所有聊天列表的区块中,您可以这样做:

var dictionary: [String: Long]   
var lastMessage: String 
for chat in listOfChatsUserIsIn
    ref.child("chats").child("\(chat)").child("lastMessage").observeSingleEventOfType(.Value, withBlock:  (snapshot) in
        lastMessage = snapshot
        ref.child("chats").child("\(chat)").child("timestamp").observeSingleEventOfType(.Value, withBlock:  (snapshot) in
            //add timestamp and last message to dictionary
        
    

我不知道我的语法有多正确。还在学习firebase。但是,我认为这基本上是您的第二个建议。不知道您将如何获取数据。这将是 O(2n) 虽然这还不错。

[[更新 1]]

我对我的代码很懒惰。我放了 lastMessage = snapshot 来保存它,这样你就可以在下一个块中将它添加到字典中。

至于 Firebase 是异步的。我认为只要您使用时间戳或消息作为键,另一个作为字典中的值,这仍然有效。它可能会乱序填充,但您以后总是可以按时间戳对其进行排序。虽然,是的,这可能不是最佳实践。

Jay,我喜欢你的解决方案。您是否还必须列出uid_2: false

不幸的是,随着用户 -> inf 和聊天 -> inf,这两个数据库结构似乎都增长了 n^2。

【讨论】:

由于您没有使用该变量,lastMesssage = snapshot 的功能是什么?此外,在块(闭包)中使用 self.variable 名称是一种很好的做法。此外,第一个 ref.child 中的 ref.child 是重复的,因此它将读取相同的数据。最大的问题是 Firebase 是异步的,并且紧密的 for 循环运行速度会比观察块中返回的数据快 方式,所以我认为我们遇到了一些数据完整性问题 - 可能应该让 Firebase 异步完成它的“东西”。 回答你的答案中的问题.... 不,uid_2: false 不需要,因为它不存在,默认情况下它可能是 false(取决于它的编码)。不,数据库不会成倍增长。如果您有 10 个用户和 1 个聊天,则所有 10 个用户都在该聊天中,因此唯一增长的将是聊天中的用户节点(在我的回答中)同样,如果您有 1000 个用户和 100 个聊天,每个聊天与两个用户,仍然只有 100 个聊天,每个聊天都有一个用户节点和两个用户。即使有 1000 个用户和 1000 个聊天,这也不是大量的数据。

以上是关于在 Firebase 中构建聊天应用程序的数据的主要内容,如果未能解决你的问题,请参考以下文章

使用 Firebase 和 Flutter 构建聊天应用程序的最简单方法是啥

使用 Angularjs 和 Firebase 构建聊天应用程序

用于聊天应用的 Firebase 实时数据库或 Firestore? [关闭]

Firebase 聊天应用 setValue 在公共数据库中失败?

使用 Firebase 的 Android 聊天应用程序,但未在 firebase 中存储数据

Firebase 网络应用聊天重复聊天输入