How to retrieve Users' comment with their profile image and name?

498 views Asked by At

Currently, I am make a social media app and I am using firebase firestore and cloud storage for my project. There is a comment button and if the user clicks the button he should be able to see every users' comments with their name and profile image.

This is my firestore database structure,

Users(Root collection)
                     |---- UID1([Document]User ID which generate by authentication)---[Fields-Name,Image,Age]
                     |---- UID2([Document]User ID which generate by authentication)---[Fields-Name,Image,Age]

Posts(Root collection)
                     |-----DOCID1(Fields-Post Title,Posted_UID,Post_Image)
                                                                         |----Comments(Sub-collection)
                                                                                                     |----1RANDOMDOCID(Fields-Commented_User_Id,Commented_Date,Comment)
                                                                                                     |----2RANDOMDOCID(Fields-Commented_User_Id,Commented_Date,Comment)

Alright, I need to populate these comments to a recycleview. I ll add my approach below,

  Query query = db.collection("Posts").document(CURRENT_SELECTED_DOC_ID).collection("Comments");
  PagedList.Config config = new PagedList.Config.Builder()
                .setEnablePlaceholders(false)
                .setInitialLoadSizeHint(10)
                .setPageSize(20)
                .build();
FirestorePagingOptions<CommentsI> options = new FirestorePagingOptions.Builder<CommentsI>()
                .setQuery(query, config, CommentsI.class)
                .build();

Constructor

public class CommentI {
String Commented_User_Id;
Date Commented_Date;
String Comment;
public CommentI(){
 //Empty constructor
}

public class CommentI(String commented_User_Id,Date commented_Date,String comment){
Commented_User_Id = commented_User_Id;
Commented_Date = commented_Date;
Comment = comment;
//------------------------------------------
public String getCommented_User_Id(){
return Commented_User_Id;
}
public Date getCommented_Date(){
return Commented_Date;
}
public String getComment(){
return Comment;
}

This is the usual way to populate items in recycleview. I can get these comments and date without any doubt, But I need to set the users' Image and name same time. this is the point where I had to stop my work until solve this. Please someone help :)


Edit

Here is my adapter and when we trying retrieve user info like this, it always reads the firestore document while scrolling up and down. For example - When I scrolling up and down(Just think i got 10 items in recyleview), I can see name and image refreshing everytime when scrolling from item 1 to 10 and from item 10 to 1. How to fix this?

@Override
protected void onBindViewHolder(@NonNull final CommentIViewHolder holder, int position, @NonNull CommentI model) {
    holder.comment_textview.setText(model.getComment());
    holder.date_textview.setText(DateFormat.format("(yyyy-MM-dd)", model.getCommented_Date()));
    //getting UserID-------------
     String UserId = model.getCommented_User_Id();

    final FirebaseFirestore db = FirebaseFirestore.getInstance();
    DocumentReference documentReference = rootRef.collection("Users").document(UserId);

     documentReference.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            if (task.isSuccessful()){
                DocumentSnapshot doc = task.getResult();
                   if (doc.exists()) {
                       String UserName = doc.getString("Name");
                       String UserImage = doc.getString("Image");
                       
           holder.commentor_name.setText(UserName);
                       holder.setProfile_image_view(UserImage);
                   }
               
            }
        }
    });





                
                
2

There are 2 answers

0
s_o_m_m_y_e_e On BEST ANSWER

Okay, my bad. I suggested you to use the Firebase User object to store the image URL but you can only get the image of the current user (which is not what you want).

So storing in the database is needed. Now, as @Greg said that you have two approaches and I would also recommend having the second but if your use case is such that you need to have the first approach then here's what you do:

1. Store the user's UID in the comments documents (which you have already done).
2. Get the comments of the particular post (which too, you have already done).

Now, after this, you are getting the user's document in onBindViewHolder which is strongly discouraged as because your onBindViewHolder would be called many times in your recyclerView which would have performance issues and cost uprising.

By seeing your code, I am assuming that you are using FirebaseUI (with which I have no experience) but I think you should do this approach on your own like this (I recently had such case and did like this):

3. Get the comments of the particular post and store it in something like an ArrayList.
4. Loop over the comments, get each user's documents and store them also in Arraylist.
5. Pass both the ArrayLists in your constructor of the adapter and then just set them up in onBindViewHolder.

Now, when I was doing something similar like this, I got an unusual behaviour which you should see here (if you are following this answer) in my question (with the answer given by myself): Running Firebase get() in a loop in android

Although, this would cost you a lot!

10
Greg Fenton On

TL;DR - add the Image & Name data to your Comments document (and still keep it in the Users document)


From the info above, I see two possible approaches:

  1. Loop over the comments gathering the commenter's UID and then get the Users document for that UID and display the image within
  2. When you create the Comments document in the first place, copy all relevant information about the User making the comment into the Comments document

Of the 2 approaches, I would strongly encourage #2.

When designing the data model for NoSQL (essentially - when determining what data to put into a document), think about your app and what you want to display on a given screen. Try to put as much data in a single document for that given screen as possible to avoid reading additional documents.

This approach will feel "icky" to people coming from a SQL background. In SQL land, we are all trained to eliminate data duplication via "data normalization". Much of this stems from the origins of SQL in a world where storage space was "expensive".

In NoSQL land - such as Firestore - the base assumptions are

  • storage is cheap
  • writes (create/update/delete) are infrequent and "expensive"
  • you fetch "documents" that are atomic >> corollary: there are no "joins"

In NoSQL land, we really don't mind data duplication. We want to avoid fetching one document (Comment) and be required to do additional document fetches (Users).