最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Modelling a Chat like Application in Firebase - Stack Overflow

programmeradmin5浏览0评论

I have a Firebase database structuring question. My scenario is close to a chat application. Here are the specifics

- 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: ''

Now imagine building a conversations view for an individual user. So I want to fetch all messages from that particular user and to that particular user

I am writing it as follows right now:

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*/})

Questions:

  1. Is this the right way to model the problem?
  2. If yes, is there a way to combine the above 2 queries?

I have a Firebase database structuring question. My scenario is close to a chat application. Here are the specifics

- 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: ''

Now imagine building a conversations view for an individual user. So I want to fetch all messages from that particular user and to that particular user

I am writing it as follows right now:

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*/})

Questions:

  1. Is this the right way to model the problem?
  2. If yes, is there a way to combine the above 2 queries?
Share Improve this question edited Sep 22, 2017 at 18:01 CommunityBot 11 silver badge asked Sep 1, 2016 at 2:11 RajatRajat 34.1k20 gold badges69 silver badges88 bronze badges
Add a comment  | 

5 Answers 5

Reset to default 10 +200

I would store the data like this:

- 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 recommends storing things like this. So in your case your query would be

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

This allows it to be very fast as there is no orderBy being done. Then you would loop over each message and get it's profile from the messages node.

The structure you have is one possible way to model this data. If you're building an application like this, I would highly recommend the angularfire-slack tutorial. One potentially faster way to model the data would be to model the data like is suggested in this tutorial 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
        }
      }
    }
  }
}

The one thing you need to watch for in this case if you choose to do it like this is that before your query, you need to sort which user will be the "primary user" under whom the messages will be stored. As long as you make sure that is the same every time, you should be good to go.

One improvement you could make to this structure is something you already pointed out - flattening your data and moving the messages to another node - like you did in your example.

To answer your second question, if you were to keep that structure, I think you would need both of those queries, because firebase does not support a more complicated OR query that would allow you to search both at the same time.

No. Firebase Auth subsystem is where you want to store the email, displayName, password, and photoURL for each user. The function below is how you do it for a password-based user. oAuth-based users are easier. If you have other properties you want to store, like age for example, put those under a users node with each users uid that Firebase Authentication provides you.

  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 + '.');
  }

As for the messages node, get the random id from Firebase Realtime Database's push method and use that as the id of each message under messages. Firebase queries are used:

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')
});

Define an index for messages-from-user and messages-to-user in your Rules:

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

Below data structure gives you more flexibility with you data. Instead of having to store each messages that user sent back and forth I would suggest to store it in separate node and store the messageID with each user that is involved in the conversation.

Obviously you need to set the security rules, so other user can't see conversation if they are not in the conversation.

By doing this we are not creating deep chain node inside user info

   - 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: ''

Firebase has actually built a demo (and extendible) chat application called Firechat. The source and documentation is provided, and of particular note is the section on their data structures.

Although they've implemented chatrooms, you can see that they've flattened their data structures as in many of the other answers. You can read more about how and why this is done in the Firebase guide.

发布评论

评论列表(0)

  1. 暂无评论