Home > Enterprise >  Getting Information from Two Firebase Root Collections
Getting Information from Two Firebase Root Collections

Time:07-17

I am using Firebase Firestore with Swift and SwiftUI. I have two collections Users and Tweets. Each Tweet store the userId of the user who tweeted. I want to fetch all the tweets along with the user information associated with tweet. Since both are stored in different collections at the root level, this poses a problem. Here is one of the solutions.

 private func populateUserInfoIntoTweets(tweets: [Tweet]) {
        
        var results = [Tweet]()
        
        tweets.forEach { tweet in
            var tweetCopy = tweet
            self.db.collection("users").document(tweetCopy.userId).getDocument(as: UserInfo.self) { result in
                switch result {
                    case .success(let userInfo):
                        tweetCopy.userInfo = userInfo
                        results.append(tweetCopy)
                        
                        if results.count == tweets.count {
                            DispatchQueue.main.async {
                                self.tweets = results.sorted(by: { t1, t2 in
                                    t1.dateUpdated > t2.dateUpdated
                                })
                            }
                        }
                        
                    case .failure(let error):
                        print(error.localizedDescription)
                }
            }
        }
    }

Kinda messy!

Another way is to store the documentRef of the User doc instead of the userId.

If I store the User document reference then I am using the following code:

  init() {
        
        db.collection("tweets").order(by: "dateUpdated", descending: true)
            .addSnapshotListener { snapshot, error in
                if let error {
                    print(error.localizedDescription)
                    return
                }
                
                snapshot?.documents.compactMap { document in
                    try? document.data(as: Tweet.self)
                }.forEach { tweet in
                    var tweetCopy = tweet
                    tweetCopy.userDocRef?.getDocument(as: UserInfo.self, completion: { result in
                        switch result {
                            case .success(let userInfo):
                                tweetCopy.userInfo = userInfo
                                self.tweets.append(tweetCopy)
                            case .failure(let error):
                                print(error.localizedDescription)
                        }
                    })
                }
                
            }
    }

The above code does not work as expected. I am wondering there must be a better way. I don't want to store all the user information with the Tweet because it will be storing a lot of extra data.

Recommendations?

CodePudding user response:

The common alternative is to duplicate the necessary information of the user in their tweets, a process known as denormalization. If you come from a background in relational databases this may sounds like blasphemy, but it's actually quite common in NoSQL data modeling (and one of the reasons these databases scale to such massive numbers of readers).

If you want to learn more about such data modeling considerations, I recommend checking out NoSQL data modeling and watching Todd's excellent Get to know Cloud Firestore series.

If you're wondering how to keep the duplicated data up to date, this is probably also a good read: How to write denormalized data in Firebase

  • Related