Ooto
Ooto

Reputation: 1257

Firestore: What is the efficient way to list messages in chat?

I am trying to create a chat like app using Firestore and trying to list all messages in chat and updates it every time when a message is added.

I tried this way first to implement it.

mounted() {

    docRef.collection('messages').orderBy('timestamp', 'desc').limit(1).onSnapshot((querySnapShot) => {
        querySnapShot.forEach((doc) => {
            if (!doc.metadata.hasPendingWrites) {
                this.messages.push(doc.data())
            }
        })
    })

}

This way seems efficient because this way only gets the latest message in collection. However, I found out this way has a problem. When a user refreshs a page, the user can't get the past messages.

So I changed it like this.

mounted() {
    docRef.collection('messages').orderBy('timestamp', 'asc').onSnapshot((querySnapShot) => {
        querySnapShot.docChanges().forEach((change) => {
            if (change.type === 'added') {
                 this.messages.push(change.doc.data())
              }
        })
    })
}

This way works as I expected. But this way needs a lot of requests because every time the collection is changed I need to read all the documents in the collection.

What is the efficient way to list messages in chat?

I thought it works if I get all the current messages first and set the listener for new messages but the listener is triggered immediately after I enter the page even though there is no change in the collection and read the latest message twice.

Upvotes: 5

Views: 1979

Answers (2)

Ooto
Ooto

Reputation: 1257

I ended up just using a flag to check whether the initial trigger is done. I don't know if this is smart way but this works.

// Get all current messages
  docRef.collection('messages').orderBy('timestamp', 'asc').get().then((querySnapShot) => {
    querySnapShot.forEach((doc) => {
      this.messages.push(doc.data())
    })
  })

// Update for new messages (Skip the initial loading)

  docRef.collection('messages').orderBy('timestamp', 'desc').limit(1).onSnapshot((querySnapShot) => {
      querySnapShot.forEach((doc) => {
        if (!doc.metadata.hasPendingWrites && this.isInitialDone) {
          this.messages.push(doc.data())
        }
        this.isInitialDone = true
      })
  })

Upvotes: 2

Doug Stevenson
Doug Stevenson

Reputation: 317750

You are going to need to either set an appropriate limit, or add a filter to determine which ones you want.

If you want to go with a size limit, just change limit(1) in your first query to the number you actually want.

If you want to go with a filter, you should probably use the timestamp you already have in place in order to determine how far back in the past you would like to go.

Those are your two options for limiting the size of the results. You can use either one or both together, but there is no other way of limiting the size of the result set.

Upvotes: 0

Related Questions