What is the best practice of firestore data structure?

10,283

What about using arrays or Collection Groups?

solution 1: arrays

posts
  -postID
   -title,content,author(userID),createdDate,favoriteCount
  -[favoriters(userID)]

Now you can query for a user's favorites by querying posts that "array-contains" the user's ID. You can also modify individual posts without iterating through a bunch data copies.

There's a limit to this approach though. Maximum size for a document is 1 MiB; assuming that a user ID is 4 bytes, a document can contain no more than 250K favoriters. Clients would also have to do some O(N) processing to add / remove favoriters.

solution 2: Collection Groups

posts
  -postID
   -title,content,author(userID),createdDate,favoriteCount
  -favoriters {collection}
   -userID

A collection group consists of all collections with the same ID. By default, queries retrieve results from a single collection in your database. Use a collection group query to retrieve documents from a collection group instead of from a single collection.

So we can fetch a user's favorite posts via

db.collectionGroup("favoriters").whereEqualTo("userID", <userID>).get();

To favorite a post, we just do

const postsRef = db.collection("posts");
postsRef.document(<postID>).collection("favoriters").add({ "userID", <userID> });
Share:
10,283
yn1043
Author by

yn1043

Updated on June 05, 2022

Comments

  • yn1043
    yn1043 about 2 years

    I'm making a blog app using firebase.

    I want to know the best practice of data structure.

    As far as I know, there are 2 case. (I'm using react native)

    case 1:

    posts
      -postID
       -title,content,author(userID),createdDate,favoriteCount
    
    favorites
      -userID
        -favoriteList
          -postID(onlyID)
          -postID(onlyID)
    

    In this case, for example, when we need to get favorite posts.

    firebase.firestore().collection(`favorites/${userID}/favoriteList`)
        .get()
        .then((snapshot) => {
          snapshot.forEach((favorite) => {
            firebase.firestore().collection(`favorites/`).doc(`${favorite.id}`)
              .get()
              .then((post) => {
              myPostList.push(post.data())
            });
      });
    

    in this case, we can't order the favorite posts by createdDate. So, need to sort client side. Even if so, we don't use limit() function.

    case 2:

    posts
      -postID
      -title,content,author(userID),createdDate,favoriteCount
    
    favorites
      -userID
         -favoriteList
           -postID
             -title,content,author(userID),createdDate,favoriteCount
           -postID
             -title,content,author(userID),createdDate,favoriteCount
    

    firebase.firestore().collection(`favorites/${userID}/favoriteList`).orderBy('createdDate','desc').limit(30)
        .get()
        .then((snapshot) => {
          snapshot.forEach((post) => {
            myPostList.push(post.data())
          });
      });
    

    in this case, When the favorite post is modified by the author, we have to update all of the favorite posts. (e.g. If 100 users save the post as a favorite, we have to update to 100 data.)

    (And I'm not sure we can increment favoritecount by a transaction, exactly same.)

    I think if we use firebase.batch(), we can manage it. But I think it seems Inefficient.

    It seems that both ways are not perfect. Do you know the best practice of this case?