Referencing documents with firebase

Hi, I’m building a chat app and I’m wondering if there’s a good way to have a relational-ish data structure with firebase firestore.

MongoDB with mongoose has something like this: (idk if this is exactly you do it, but it’s the general idea)

const MessageSchema = new mongoose.Schema({
  uid: {
    type: mongoose.Schema.ObjectID,
    ref: 'User'
  }
})

Is there something similar you can do with firestore? I haven’t been able to find anything about that.

I have a users collection in firestore, which holds information such as the users name, image URL and id. I also have a messages collection, which stores all the messages posted by users. The message has a uid field that points to the id of the user that posted the message. Is there a good way to get the user’s information from that id? I really don’t want to have to use something like the code below to do it.

let messages = await getDocs(query(messagesRef, limit(50)))

messages = await Promise.all(messages.map(msg => {
  return new Promise(resolve => {
    getData(doc(db, 'users', msg.uid)).then(userDoc => {
      resolve({ ...msg, userData: userDoc.data() })
    })
  })
}))

This seems very slow, and would use a ton of reads from firestore. Is there a better way to do it?

Thanks for you help!

Hello!

Why don’t you read the user first and then the messages?

Is there a way to read the user first? If I’m wanting to pull up, say, the last 50 messages from the feed, how would that work?

quick google shows this

getData() async{
    String userId = (await FirebaseAuth.instance.currentUser()).uid;
    return Firestore.instance.collection('users').document(userId);
  }

assuming you are using the firebase provided auth mechanism.

1 Like

I suppose you would have some kind of conversation model or some way to group them, no? If so, then you query that first (the participants), and then query the messages for each one.

If you, on the other hand, need to retrieve all the messages for every user, then there’s no other way.

Check this out though: Top questions from Stack Overflow and Twitter with Frank van Puffelen, Part II - #AskFirebase - YouTube (addresses the query speed). The question on Stack Overflow

Also the main docs have some examples:

https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data

to limit the results use limit as per docs

https://firebase.google.com/docs/firestore/query-data/order-limit-data#order_and_limit_data

To be honest firebase is very well documented. You will find everything you need there.

For pagination here:

https://firebase.google.com/docs/firestore/query-data/query-cursors#add_a_simple_cursor_to_a_query

Would it be better to store the user data in the message as it is created instead of just the user id? Btw I’m using firebase auth, but the users collection is just so that their public data their data is accessible to other users.

I think that is safe to be fair, but it depends on the nature of the application.
Heavily accessed? How big these documents are? What is the latency impact of a given query.
I guess you will generate some indexes to help with that when is necessary.

99% you should be fine to have some user data on the message, if not bothered of duplicates.

Why don’t you have a sub collection per user and each user has his messages. Would that not work better in your case?

Sorry, but funny enough your use case is an example in the docs once again.

“The best way to store messages in this scenario is by using subcollections. A subcollection is a collection associated with a specific document.”

https://firebase.google.com/docs/firestore/data-model#subcollections

1 Like

@skaparate and @GeorgeCrisan thanks so much for your help! After doing some research, I found out that mongoose actually has to query for each document that it references by id as well, so it looks like the way I originally had it is the way to go. But I really appreciate your feedback, it definitely gave me some things to think about.

2 Likes