【问题标题】:Modelling a Chat like Application in Firebase在 Firebase 中为类似聊天的应用程序建模
【发布时间】:2016-09-01 02:11:50
【问题描述】:

我有一个 Firebase 数据库结构问题。我的场景接近聊天应用程序。以下是具体内容

- users(node storing several users of the app)
  - id1
      name: John
  - id2
      name: Meg
  - id2
      name: Kelly
- messages(node storing messages between two users)
  - message1
      from: id1
      to: id2
      text: ''
  - message2
      from: id3
      to: id1
      text: ''

现在想象一下为单个用户构建对话视图。所以我想从那个特定用户那里获取所有消息 和那个特定的用户

我现在是这样写的:

let fromMessagesRef = firebase.database().ref('messages').orderByChild('from').equalTo(firebase.auth().currentUser.uid)
fromMessagesRef.once("value").then((snapshot) => {/* do something here*/})

let toMessagesRef = firebase.database().ref('messages').orderByChild('to').equalTo(firebase.auth().currentUser.uid)
toMessagesRef.once("value").then((snapshot) => {/* do something here*/})

问题:

  1. 这是对问题建模的正确方法吗?
  2. 如果是,有没有办法将上述两个查询结合起来?

【问题讨论】:

    标签: javascript firebase firebase-realtime-database nosql


    【解决方案1】:

    我会这样存储数据:

    - users(node storing several users of the app)
      - id1
          name: John
          messages
            message1: true
            message2: true
      - id2
          name: Meg
          messages
            message1: true
            message3: true
      - id3
          name: Kelly
          messages
            message2: true
            message3:true
    - messages(node storing messages between two users)
      - message1
          from: id1
          to: id2
          text: ''
      - message2
          from: id3
          to: id1
          text: ''
      - message3
          from: id2
          to: id3
          text: ''
    

    Firebase 建议存储此类内容。因此,在您的情况下,您的查询将是

    let fromMessagesRef = firebase.database().child('users').child(firebase.auth().currentUser.uid).child('messages')
    

    这允许它非常快,因为没有 orderBy 正在完成。然后您将遍历每条消息并从消息节点获取它的配置文件。

    【讨论】:

    • 澄清一下,Kelly 的意思是id3
    • 哈哈..是的。这是问题的复制/粘贴。
    • 那么这是否会返回一个用户的所有消息,而不一定只是特定对话中两个用户之间的消息?例如,如果message3 介于id2id3 之间,那么id2 在运行此查询时是否仍会看到message1?似乎您会看到的不仅仅是两个当前用户之间的消息?
    • 在这种情况下,您可以运行两个查询,一个是 from 是 id2,to 是 id3,另一个是 from 是 d3,to 是 id2。如果您只想运行一个查询,则必须像这样存储数据。因此,在每个用户下,您将拥有另一个用户的另一个密钥,然后它的密钥将是两个用户共享的消息。
    【解决方案2】:

    您拥有的结构是对这些数据进行建模的一种可能方式。如果您正在构建这样的应用程序,我强烈推荐 angularfire-slack 教程。一种可能更快的数据建模方法是像本教程中建议的那样对数据建模https://thinkster.io/angularfire-slack-tutorial#creating-direct-messages

    {
      "userMessages": {
        "user:id1": {
          "user:id2": {
            "messageId1": {
              "from": "simplelogin:1",
              "body": "Hello!",
              "timestamp": Firebase.ServerValue.TIMESTAMP
            },
            "messageId2": {
              "from": "simplelogin:2",
              "body": "Hey!",
              "timestamp": Firebase.ServerValue.TIMESTAMP
            }
          }
        }
      }
    }
    

    在这种情况下,如果您选择这样做,您需要注意的一件事是,在查询之前,您需要对将存储消息的“主要用户”进行排序。只要确保每次都相同,就可以开始了。

    您可以对此结构进行的一项改进是您已经指出的 - 展平数据并将消息移动到另一个节点 - 就像您在示例中所做的那样。

    要回答您的第二个问题,如果您要保留该结构,我认为您将需要这两个查询,因为 firebase 不支持更复杂的 OR 查询,该查询允许您同时搜索这两个查询.

    【讨论】:

      【解决方案3】:

      没有。 Firebase Auth 子系统是您要为每个用户存储 emaildisplayNamepasswordphotoURL 的位置。下面的功能是您如何为基于密码的用户执行此操作。基于 oAuth 的用户更容易。如果您有其他要存储的属性,例如 age,请将它们放在 users 节点下,每个用户 uid Firebase 身份验证为您提供。

        function registerPasswordUser(email,displayName,password,photoURL){
          var user = null;
          //NULLIFY EMPTY ARGUMENTS
          for (var i = 0; i < arguments.length; i++) {
            arguments[i] = arguments[i] ? arguments[i] : null;
          }
          auth.createUserWithEmailAndPassword(email, password)
          .then(function () {
            user = auth.currentUser;
            user.sendEmailVerification();
          })
          .then(function () {
            user.updateProfile({
              displayName: displayName,
              photoURL: photoURL
            });
          })
          .catch(function(error) {
            console.log(error.message);
          });
          console.log('Validation link was sent to ' + email + '.');
        }
      

      对于messages 节点,从Firebase 实时数据库的push 方法中获取随机ID,并将其用作messages 下每条消息的id。使用 Firebase 查询:

      var messages = firebase.database().ref('messages');
      var messages-from-user = messages.orderByChild('from').equalTo('<your users uid>');
      var messages-to-user = messages.orderByChild('to').equalTo('<your users uid>');
      
      messages-from-user.once('value', function(snapshot) {
        console.log('A message from <your users uid> does '+(snapshot.exists()?'':'not ')+' exist')
      });
      
      messages-to-user.once('value', function(snapshot) {
        console.log('A message to <your users uid> does '+(snapshot.exists()?'':'not ')+' exist')
      });
      

      在您的规则中为来自用户的消息和发给用户的消息定义一个索引:

      {
        "rules": {
          "messages": {
            ".indexOn": ["from", "to"] 
          }
        }
      }
      

      【讨论】:

      • 由于 Firebase 身份验证没有用于检索用户列表的公共 API,因此还将用户配置文件中的特定属性存储在数据库中是很常见的。
      【解决方案4】:

      以下数据结构为您提供了更大的数据灵活性。我建议不必存储用户来回发送的每条消息,而是将其存储在单独的节点中,并将 messageID 存储在对话中涉及的每个用户中。

      显然您需要设置安全规则,这样如果其他用户不在对话中,他们就无法看到对话。

      通过这样做,我们不会在用户信息中创建深链节点

         - users(node storing several users of the app)
            - id1
                name: John
                messages: [msID1, msID9]
            - id2
                name: Meg
                messages: [msID1, msID7]
            - id3
                name: Kelly
                 messages: [msID9, msID7]
      
          - messages(node storing messages between two users)
            - msID1
                from: id1
                to: id2
                text: ''
            - msID7
                from: id3
                to: id2
                text: ''
             - msID9
                from: id3 
                to: id1
                text: ''
      

      【讨论】:

        【解决方案5】:

        Firebase 实际上已经构建了一个名为 Firechat 的演示(和可扩展)聊天应用程序。提供了源代码和文档,特别值得注意的是他们的data structures 部分。

        尽管他们已经实现了聊天室,但您可以看到他们已经将数据结构扁平化,就像在许多其他答案中一样。您可以在Firebase guide 中阅读有关如何以及为什么这样做的更多信息。

        【讨论】:

          猜你喜欢
          • 2016-09-27
          • 2017-08-16
          • 2017-09-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-28
          • 2019-02-23
          • 2013-01-20
          相关资源
          最近更新 更多