skip to Main Content

I’m building a feed where a user can follow projects, and those projects will show up in the user’s feed, sorted by a lastUpdated field, and allow for pagination, but I can’t figure out what the best approach is.

The basic Firestore structure looks like:

users (collection)
  projects (subcollection)
    lastUpdated (timestamp)
  followingProjects (array of project Document IDs)

I could use an in operator like:

let followingProjects = user.followingProjects
firestore().collectionGroup("projects")
           .whereField(FieldPath.documentID(), in: followingProjects)
           .order(by: "lastUpdated", descending: true)
           .limit(to: 20)
           // And later for pagination :
           .start(afterDocument: startAfter)

But I believe the in operator is limited to 30 values in followingProjects, and I would like to allow following more than 30 projects.

A solution I can think of is to create a separate subcollection for followingProjects, so the new structure would be:

users (collection)
  projects (subcollection)
    lastUpdated (timestamp)
  followingProjects (subcollection)
    id (string)
    lastUpdated (timestamp)

In this case, to get the feed, I’d run a collectionGroup query to get the latest followingProjects so I can sort and paginate, followed by individual getDocument() queries for each project. Every time a project is updated, it would have to update the lastUpdated for each user that’s following the project though, and this seems inefficient.

Is there a better approach I’m missing?

2

Answers


  1. Chosen as BEST ANSWER

    I found the answer I needed in another answer!

    https://stackoverflow.com/a/58100387/482536

    Instead of storing Project IDs in a followingProjects array as part of the User, the Project should store User IDs in a followers array. Then I can run the following query without worrying about the disjunctions limit, and there are no separate documents to keep updated whenever a Project is updated.

    let userID = user.id
    firestore().collectionGroup("projects")
           .whereField("followers", arrayContains: userID)
           .order(by: "lastUpdated", descending: true)
           .limit(to: 20)
           // And later for pagination :
           .start(afterDocument: startAfter)
    

  2. In a collection group index, the __name__ field (which is what FieldPath.documentID() maps to) is populated with the path of the document, rather than just its document ID. So if you want to filter on FieldPath.documentID(), you will need to know the entire path of the document(s).

    Alternatively, you can add a regular field with the document ID to each document, and filter on that in your collection group query.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search