1

I am trying to retrieve posts from a list of accounts that a user follows, but am unsure on the best way to do so? I have implemented a the same structure from this stack overflow answer, as I found it sensible for what I was trying to create (see below).

Firestore-root
   |
   --- users (collection)
   |     |
   |     --- uid (documents)
   |          |
   |          --- name: "User Name"
   |          |
   |          --- email: "email@email.com"
   |
   --- following (collection)
   |      |
   |      --- uid (document)
   |           |
   |           --- userFollowing (collection)
   |                 |
   |                 --- uid (documents)
   |                 |
   |                 --- uid (documents)
   |
   --- posts (collection)
         |
         --- uid (documents)
              |
              --- userPosts (collection)
                    |
                    --- postId (documents)
                    |     |
                    |     --- title: "Post Title"
                    |     |
                    |     --- date: September 03, 2018 at 6:16:58 PM UTC+3
                    |
                    --- postId (documents)
                          |
                          --- title: "Post Title"
                          |
                          --- date: September 03, 2018 at 6:16:58 PM UTC+3

However, I am struggling to understand the best way to retrieve a list of posts where its document UID is the same as the one a user is following.

I have attempted to get all the users followers and then for each document, loop over it using another get, but haven't found a successful solution. An example of this code can be seen below:

var userFollowingList = [User]()

db.collection("Following").document(currentUserUID).collection("userFollowing")
    .getDocuments() { (querySnapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
            completion(false)
        } else {
            for document in querySnapshot!.documents {
                print("\(document.documentID) => \(document.data())")
                userFollowingList = querySnapshot!.documents.compactMap { querySnapshot -> User? in
                    return try? querySnapshot.data(as: User.self)
                }
            }
for user in userFollowingList {
    db.collection("Posts")
        .getDocuments() { (tripQuerySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
                completion(false)
            } else {
                for document in tripQuerySnapshot!.documents {
                    print("\(document.documentID) => \(document.data())")
                    self.followingTrips = querySnapshot!.documents.compactMap { querySnapshot -> Trip? in
                        return try? querySnapshot.data(as: Trip.self)
                    }
                }
            }
        }
    }   
}

What is the most efficient way to achieve this? Thanks.

30/03/2020 UPDATE:

I ended up looping over each userID in the following list and returning all the posts for the users, but I'm still not sure if this is the best way. See example below:

let userFollowingIDs = ["01","02","03"]
    
    
    for id in userFollowingIDs {
        db.collection("Trips").document(id).collection("userPosts")
            .getDocuments() { (querySnapshot, err) in
                if let err = err {
                    print("Error getting documents: \(err)")
                    completion(false)
                } else {
                    for document in querySnapshot!.documents {
                        print("\(document.documentID) => \(document.data())")
                        self.followingTripsList = querySnapshot!.documents.compactMap { querySnapshot -> Trip? in
                            return try? querySnapshot.data(as: Trip.self)
                        }
                    }
                    completion(true)
                }
            }
        }
user2047296
  • 315
  • 3
  • 12
  • The question and problem are a bit unclear. There's a *following* node which has documents with a users uid as the key to each. Within that is a list of users uid's they are following. So if you load that *userFollowing* collection in, you know all of the uid's they are following. There's a*posts* collection organized by uid's. If you iterate over each uid you loaded, you could then load that corresponding users posts from the posts collection. Is there something more? What are you doing with all of that data at once? +1 for your ascii art btw - made the Firestore structure really clear. – Jay Mar 30 '21 at 19:15
  • Hi Jay. I have ended up going with that approach that you noted and updated my post to show the latest code. Sorry for not being clear, you are correct and that's what I was looking to do, I just was hoping there was a better way so I didn't need to get the usersFollowing collection first. Unfortunately the ascii art isn't mine lol, I copied the example in the linked Stack Overflow question. – user2047296 Mar 30 '21 at 19:31

1 Answers1

2

I think that this can be done easier. Of course I do not have details of whole system, however looking at this particular problem I would add to every userPosts document new field followedBy. The field would be an array where all following the post user ids (uid) will be stored.

Now checking, if particular posts is followed by particular user can be done by checking if this followedBy array contains this user id. Then it will be possible to use two Firestore features: collectionGroup and arrayContains filter

Then getting list of posts followed by particular user will be extremely simple like this:

db.collectionGroup("userPosts")
            .whereField("followedBy", arrayContains: uid)
            .getDocuments() { (snapshot, error) in
            // ...

No looping and less document got from the Firestore. This should be more effective when it comes to usage cost as well.

vitooh
  • 4,132
  • 1
  • 5
  • 16
  • Thank you vitooh, I didn't think about this approach before but definitely makes more sense. I've implemented it now and is working fine. For anyone else who may read this, you will need to set up indexes in firebase, but when you first run the code you will be shown an error that gives you a link to use to generate the right index required. – user2047296 Mar 31 '21 at 12:09
  • Welcome, and Yea. True about the indexes, you can check this [question](https://stackoverflow.com/questions/66758747/) – vitooh Apr 01 '21 at 06:34