How to implement user feedback with FireBase?

I have an application where the user may like photos, comments, etc. Functionality like Instagram.

I want to realize users! feedback !, where the user can see the information, who liked his photos, who started to follow, etc. I do not know how to organize my database structure in this situation.

My custom node snapshot:

enter image description here

My posts node snapshot:

enter image description here

As I see it, I have the following option - I have to save all actions associated with the user with his node in the internal node Feedback . But how can I sync this? For example, someone can follow my user, I will add him to this node, the user will cancel the subscription, but the record will still remain. I think it's wrong.

I have no other idea, and I can not find anything about it.

Any suggestions and solutions are greatly appreciated.

EDIT: I need to understand how to implement this instagram-like application tab:

enter image description here

How to get data for it from nodes?

UPD: The DB architecture in my examples is bad (old question). Be careful (11/10/2017).

+7
ios swift swift3 firebase firebase-database
source share
2 answers

First, think about how we need to structure our database for this:

When structuring data for Firebase, two very important principles must be observed:

  • You must save your data the way you want to receive it.
  • You should keep the data structure as flat as possible - avoid nesting.

Point 1 is that Firebase is not a relational database. This means that we need to support simple queries to achieve performance. Creating complex queries may require many queries against Firebase.

Point 2 is due to how the Firebase query model works : if you observe a node, you also get all the children of that node. This means that if your data is deeply embedded, you can get a lot of data that you do not need.

So, with these principles in mind, let's take a look at your case. We have users who have photos . These are the two main objects of your database.

I see that you are currently saving your photos as user properties. If you want to quickly request photos by the user (remember Point 1 ), this is a good way to do this. However, if we want users to have the possibility of โ€œfavoriteโ€ photos, the photo should be more than just a reference to its location of the Firebase storage: it should also contain other properties, for example, which users prefer it. This property must be an array of user identifiers. In addition, for each user you want to save which photos are selected by the user. This may seem like data duplication, but when using Firebase, OK to duplicate some data if it leads to simpler queries .

So, using a data index such as in the example above, each of your photos should look like this:

 { id: /* some ID */, location: /* Firebase Storage URL */, favorited_by: { /* some user ID */: true /* this value doesn't matter */, /* another user ID */: true, }, /* other properties... */ } 

And your user must have favorites property id identifiers. Now, since each photo has a user who โ€œownsโ€ it, we do not need to have a unique identifier for each photo, we just need to make sure that the user does not have two photos with the same identifier. Thus, we can refer to the photo using a combination of its user ID and its photo ID.

Of course, remember Point 1 . If you want to receive information about a user without receiving user photos, you must have a different property on your root photo object, and not associate photos with users. However, for this answer, I will try to stick with your current model.

Based on what I said above, the user's favorites property will contain an array of values โ€‹โ€‹of the format 'userId/photoId' . For example, if a user selects a photo with the identifier "3A" user with the identifier "CN7v0A2" , their array of favorites would save the value 'CN7v0A2/3A' . This completes our select structure.

Now let's see what some of the operations mentioned in this structure will look like:

  • Custom photos :
    • We get the photo owner user ID
    • We get the identifier of the user who prefers the photo
    • We get the photo id
    • We add a user who is the favorite ID in the favorited_by array photo
    • We add photoOwnerID + "/" photoID to the user user favorites

If the user does not take pictures later, we simply do the opposite: we remove photoOwnerID + "/" + photoID from the user favorites and remove the user ID from the favorited_by property.

Such logic is sufficient for the implementation of preferences, selected and subsequent. Both the follower / liker / favoriter and followee / likee / favoritee must contain references to another member identifier, and you must encapsulate the like / favorite / follow and unlike / favorite / unfollow operations so that they keep this database (such thus, you will not encounter problems such as the case you mentioned when a user disconnects a user, but the database still contains the โ€œnextโ€ record).

Finally, here is a code on how you could perform the Favorites and Adverse operations if you have a User model class:

 extension User { func follow(_ otherUser: User) { let ref = FIRDatabase.database().reference() ref.child("users/\(otherUser.userId)/followers/") .child(self.userId).setValue(true) ref.child("user/\(self.userId)/following/") .child(otherUser.userId).setValue(true) } func unfollow(_ otherUser: User) { let ref = FIRDatabase.database().reference() ref.child("users/\(otherUser.userId)/followers/") .child(self.userId).remove() ref.child("user/\(self.userId)/following/") .child(otherUser.userId).remove() } } 

Using this model, you can get all the follower user identifiers for the user requesting this followers property of the user, and using the .keys() method in the resulting snapshot , and vice versa, for the users that this user follows.

Content Added. We can continue to work on this structure to add simple logging of actions, which, in your opinion, is available to the user on the Feedback tab. Suppose we have a set of actions, such as liking, favorites and following, which we want to show feedback.

We will again follow point 1: To structure feedback data, it is better to store this data in the same way as we want to receive it. In this case, we will most often show users our own feedback data. This means that we must store feedback data by user ID. In addition, following paragraph 2, we must store the feedback data as our own table, and not add it to the user records. Therefore, we must create a new table for our root object, where for each user ID we save a list of feedback records.

It should look something like this:

 { feedback: { userId1: /* this would be an actual user ID */ { autoId1: /* generated using Firebase childByAutoId */ { type: 'follow', from: /* follower ID */, timestamp: /* Unix time */, }, autoId2: { type: 'favorite', from: /* ID of the user who favorited the photo */ on: /* photo ID */ timestamp: /* Unix time */ }, /* ...other feedback items */ }, userId2: { /* ...feedback items for other user */ }, /* ...other user entries */ }, /* other top-level tables */ } 

In addition, we will need to change the favorites / likes / follows tables. Previously, we simply kept true to signal that someone loved or loved the photo or was following the user. But since the value that we use does not matter, since we only check the keys to find what the user liked or liked and who they observed, we can start using the record identifier for the name / favorite / next. Therefore, we would replace our follow logic with the following:

 extension User { func makeFollowFeedbackEntry() -> [String: Any] { return [ "type": "follow", "from": self.userId, "timestamp": UInt64(Date().timeIntervalSince1970) ] } func follow(_ otherUser: User) { let otherId = otherUser.userId let ref = FIRDatabase.database().reference() let feedbackRef = ref.child("feedback/\(otherId)").childByAutoId() let feedbackEntry = makeFollowFeedbackEntry(for: otherId) feedbackRef.setValue(feedbackEntry) feedbackRef.setPriority(UInt64.max - feedbackEntry["timestamp"]) let feedbackKey = feedbackRef.key ref.child("users/\(otherUser.userId)/followers/") .child(self.userId).setValue(feedbackKey) ref.child("user/\(self.userId)/following/") .child(otherUser.userId).setValue(feedbackKey) } func unfollow(_ otherUser: User, completionHandler: () -> ()) { let ref = FIRDatabase.database().reference() let followerRef = ref.child("users/\(otherUser.userId)/followers/") .child(self.userId) let followingRef = ref.child("user/\(self.userId)/following/") .child(otherUser.userId) followerRef.observeSingleEvent(of: .value, with: { snapshot in if let followFeedbackKey = snapshot.value! as? String { // we have an associated follow entry, delete it ref.child("feedback").child(otherUser.userId + "/" + followFeedbackKey).remove() } // if the key wasn't a string, there is no follow entry followerRef.remove() followingRef.remove() completionHandler() }) } } 

Thus, we can get the user's feedback simply by reading the record of the feedback table with this user ID, and since we used setPriority , it will be sorted by the last records first, which means that we can use Firebase queryLimited(toFirst:) to get only the latest feedback. When a user unsubscribes, we can easily delete the feedback entry that informed the user that they were being followed. You can also easily add additional fields to save whether a feedback entry has been read, etc.

And even if you used a different model before (setting "followerId" to true ), you can use feedback records for new records, just check if the value of "followerId" is a string, as I did above :)

You can use the same logic, only with different fields in the record, for processing favorites and loved ones. When you process it to show data to the user, just check the line in the "type" field to see what kind of feedback to show. And finally, it should be easy to add additional fields for each feedback record in order to save, for example, the user has already seen the feedback.

+8
source share

You can use the tool you want using the Firebase features. Here's roughly how I would like to implement it:

  • All user reviews will be stored in /Feedback/userID/ , located in the root.
  • Inside this node there is a subrange called eventStream .
  • Whenever an action occurs, this can be directly added to the eventStream user.

    This action may look like this: pushID: { actionType:"liked", post:"somePostID", byUser:"someUserId" }

  • Also enable the anti-action subcode (under /Feedback/userID/ ). Whenever one of these anti-action events occurs (for example: unlike, unsubscribe, etc.), Save it under the anti-action node for the corresponding user. This node will essentially act as a buffer for reading our function.

    This opposition can be almost identical in form: pushID: { actionType:"unliked", post:"somePostID", byUser:"someUserId" }

  • Now for the function.

    Whenever an anti-action node is added to an anti-action node, the function removes it from the anti-action node, finds the corresponding action in the eventStream and removes it. This can be achieved using the first request "actionType" , then "someUserId" and then "somePostID" .

This ensures that the eventStream user eventStream always up to date with the latest events.

Hope this helps! :)

+1
source share

All Articles