30

Recently I moved my data model from Firebase to Firestore. All my code is working, but I'm having some ugly troubles regarding my nested queries for retrieve some data. Here is the point:

Right now my data model for this part looks like this(Yes! Another followers/feed example):

{
  "Users": { //Collection
    "UserId1" : { //Document
      "Feed" : { //Subcollection of Id of posts from users this user Follow
        "PostId1" : { //Document
          "timeStamp" : "SomeDate"
        },
        "PostId2" : {
          "timeStamp" : "SomeDate"
        },
        "PostId3" : {
          "timeStamp" : "SomeDate"
        }
      }
      //Some data
    }
  },
  "Posts":{ //Collection
    "PostId1":{ //Document
      "Comments" :{ //Subcollection
        "commentId" : { //Document
          "authorId": "UserId1"
          //comentsData
        }
      },
      "Likes" : { //Subcollection
        "UserId1" : { //Document
          "liked" : true
        }       
      }
    }
  }
}

My problem is that for retrieve the Posts of the feed of an user I should query in the next way:

  • Get the last X documents orderer by timeStamp from my Feed

feedCol(userId).orderBy(CREATION_DATE, Query.Direction.DESCENDING).limit(limit)

  • After that I should do a single query of each post retrieved from the list: workoutPostCol.document(postId)

  • Now I have the data of each post, but I want shot the username, picture, points.. etc of the author, which is in a different Document, so, again I should do another single query for each authorId retrieved in the list of posts userSocial(userId).document(toId)

  • Finally, and not less important, I need to know if my current user already liked that post, so I need to do a single query for each post(again) and check if my userId is inside posts/likes/{userId}

Right now everything is working, but thinking that the price of Firestore is depending of the number of database calls, and also that it doesn't make my queries more simple, I don't know if it's just that my data model is not good for this kind of database and I should move to normal SQL or just back to Firebase again.

Note: I know that EVERYTHING, would be a lot more easier moving this subcollections of likes, feed, etc to arraylists inside my user or post documents, but the limit of a Document is 1MB and if this grow to much, It will crash in the future. In other hand Firestore doesnt allow subdocument queries(yet) or an OR clause using multiple whereEqualTo.

I have read a lot of posts from users who have problems looking for a simple way to store this kind of ID's relationship to make joins and queries in their Collections, use Arraylists would be awesome, but the limit of 1MB limit it to much.

Hope that someone will be able to clarify this, or at least teach me something new; maybe my model is just crap and there is a simple and easiest way to do this? Or maybe my model is not possible for a non-sql database.

halfer
  • 19,824
  • 17
  • 99
  • 186
Francisco Durdin Garcia
  • 12,540
  • 9
  • 53
  • 95

1 Answers1

21

Not 100% sure if this solves the problem entirely, since there may be edge cases for your usage. But with a 5 min quick thinking, I feel like the following could solve your problem :

You can consider using a model similar to Instagram's. If my memory serves me well, what they use is an events-based collection. By events in this specific context I mean all actions the user takes. So a comment is an event, a like is an event etc.

This would make it so that you'll need three main collections in total.

users
-- userID1
---- userdata (profile pic, bio etc.)
---- postsByUser : [postID1, postID2]
---- followedBy : [userID2, ... ]
---- following : [userID2, ... ]
-- userID2
---- userdata (profile pic, bio etc.)


posts
-- postID1 (timestamp, so it's sortable)
---- contents
---- author : userID1
---- authorPic : authorPicUrl
---- authorPoints : 12345
---- taggedUsers : []
---- comments
------ comment1 : { copy of comment event }
---- likes : [userID1, userID2]
-- postID2 (timestamp)
---- contents
... 


events
-- eventID1
---- type : comment
---- timestamp
---- byWhom : userID
---- toWhichPost : postID
---- contents : comment-text
-- eventID2
---- type : like
---- timestamp
---- byWhom : userID
---- toWhichPost : postID

For your user-bio page, you would query users.

For the news feed you would query posts for all posts by userIDs your user is following in the last 1 day (or any given timespan),

For the activity feed page (comments / likes etc.) you would query events that are relevant to your userID limited to the last 1 day (or any given timespan)

Finally query the next days for posts / events as the user scrolls (or if there's no new activity in those days)

Again, this is merely a quick thought, I know the elders of SOF have a habit of crucifying these usually, so forgive me fellow members of SOF if this answer has flaws :)

Hope it helps Francisco,

Good luck!

johnozbay
  • 2,192
  • 1
  • 25
  • 28
  • I like the `events` part, I thought something like that for also control that notifications are sent just once. In other hand, I already saw that I should denormalice my data more(save username and photoUrl instead of just the ID), an user will not change their name or picture that often, and in the end will be ligher than add infinite nested queries in the posts, comments, likes... etc. – Francisco Durdin Garcia Oct 24 '17 at 14:12
  • Finally I think, that for the `feed` I should stay in a list of references to the posts of each user I follow, cause in the end, copy just once or delete just once all the ID's will be more easy than make big queries to retrieve all the last posts of each user I follow everytime I want to check my feed. – Francisco Durdin Garcia Oct 24 '17 at 14:13
  • 1
    @FranciscoDurdinGarcia - The reason I suggested instagram's API is because I believe they manage to keep it light by using an `events` method too. With regards to keeping a list of all posts by users you follow, that's a slippery slope, because that requires an inverse data model. Say you're following me, and I make a post. This requires me to write a piece of data to your user. Aside from making security more complex, at scale, this solution would require my user to write the 1 post into 200k other users' profiles if I have 200k followers. I'd choose that part based on your estimated scale. – johnozbay Oct 24 '17 at 19:31
  • 1
    @canozbay can u please suggest some answer for this question. https://stackoverflow.com/questions/47174075/firestore-how-to-get-the-collection-value-from-another-collection-document-id-is – Rijo Nov 16 '17 at 02:24
  • @Rijo can you say if this method is recommended by Google? – Francis Rodrigues Feb 27 '18 at 15:29