Eric Walters
Eric Walters

Reputation: 311

Structuring Firebase To Order By Time Last Liked And Two Most Recent Likes

I have a social app that I want display to the user the posts that were most recently liked and who were the two most recent people to like that post. Displaying something like "User A and User B liked and 31 others liked your post" where the 31 others liked the post less recently than User A and User B and this specific post was the most recent post of the user's to be liked.

I am trying to figure out the best way to structure firebase given these requirements. I was thinking I would store the lastLiked timestamp associated to the post in likeActivity and then use the timestamp as the key for the users who liked the post (see below).

  "likeActivity" : {
    "nIhx1SnChjapy4cbrD5sC1WIZXM2" : {
      "-M0egnxw7TqEJoEwA0gG" : {
        "lastLiked" : 1582337525620,
        "userLikes" : {
          "1582337525616" : "BmvRlWWuGRWApqFvtT8mXQlDWzz2",
          "1582337525620" : "Ff0CxZhQzYVHuqbnsiOKwRAB01D2"
        }
      },
      "-M0jMrisNrnB4usGeCaS" : {
        "lastLiked" : 1582337525630,
        "userLikes" : {
          "1582337525630" : "Ff0CxZhQzYVHuqbnsiOKwRAB01D2"
        }
      },
      "-M0jPBatN45YtfdyTXNM" : {
        "lastLiked" : 1582337525616,
        "userLikes" : {
          "1582337525610" : "bz2E02wmmXQjVkxRF23T2SPkzSf2",
          "1582337525616" : "WPOThFRRLDUCeJ8YVmyHFpGgnxI3"
        }
      }
    }
  }

Would this be the best way given my requirements from above? If so, I am having a ton of trouble retrieving these for my iOS app because when I call snapshot.children.allObjects I am not just getting the likes but also the lastLiked timestamp. How would I re-work the observeActivity function to pull data ordered by lastLiked and then ordered by timestamp. I read the documentation on ordering data in Firebase but I'm unsure how to apply it in my circumstance.

func observeActivity () {
    guard let uid = Auth.auth().currentUser?.uid else {
        return
    }

    let newActivity = DataService.ds.REF_LIKE_ACTIVITY.child("\(uid)")

    newActivity.observe(.value, with: {(snapshot) in

        self.posts = []

        if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {

            for snap in snapshot{

                let postKey = snap.key
                //self.posts.append(snap.key)

                let userLikeData = newActivity.child("\(snap.key)").child("userLikes")

                userLikeData.observe(.value, with:  {(snapshot) in

                    if let newSnap =  snapshot.children.allObjects as? [DataSnapshot] {

                        for valueSnap in newSnap {
                            //Users who have liked the post
                            print("SNAPTIVITY \(valueSnap.value!)")
                            let userId = valueSnap.value!
                            //self.userArray.append("\(userId)")

                            if self.activityDict["\(postKey)"] != nil {
                                self.activityDict["\(postKey)"]!.append("\(userId)")
                            } else {
                                self.activityDict["\(postKey)"] = ["\(userId)"]
                            }

                        }
                        let postData = DataService.ds.REF_POSTS.child(postKey)
                        postData.observe(.value, with: { (snapshot) in
                            if let postDict = snapshot.value as? Dictionary<String, AnyObject> {
                                let key = snapshot.key
                                let post = Post(postKey: key, postData: postDict, isLiked: 0)
                                self.posts.append(post)
                            }


                            //self.feedTableView.reloadData()
                            self.activityTableView.reloadData()
                        })
                    }

                })
            }
        }

    })


}

Upvotes: 0

Views: 53

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 598797

In most social network application each user can only like an individual post at most once. Your current data model for these likes is:

"userLikes" : {
  "1582337525616" : "BmvRlWWuGRWApqFvtT8mXQlDWzz2",
  "1582337525620" : "Ff0CxZhQzYVHuqbnsiOKwRAB01D2"
}

This model allows a single user to like the same post multiple times. And while you can prevent this from happening in your application code, there's no way to enforce it in security rules.

For this reason, a pretty hard rule when modeling data in Firebase&Firestore is: if something must be unique, it must be the key of the child node/document.

So based on that, I'd model this fragment as:

"userLikes" : {
  "BmvRlWWuGRWApqFvtT8mXQlDWzz2": 1582337525616,
  "Ff0CxZhQzYVHuqbnsiOKwRAB01D2": 1582337525620
}

You'll note that this now also allows you to store the timestamp as a number, which prevents all kind of possible sorting problems down the line.


I assume your data structure is:

likeActivity
  $uid
    $postId: ...

Since you're retrieving a list of posts for the current user, you can only order the resulting posts on values that are at a fixed path under each post.

So you can sort the posts on their lastLiked, but you can't then determine the order in which the userLikes under that are returned. You'll have to reorder those results in your application code to determine the most recent like(r).

Upvotes: 1

Related Questions