Home > Net >  Firebase Query to fetch data from two nodes together
Firebase Query to fetch data from two nodes together

Time:11-03

I have a root node named "Posts" in the Firebase Realtime Database. Inside that, I have two nodes called "ImagePosts" and "TextPosts". And inside "ImagePosts" (and "TextPosts"), I have postIds of various posts. And inside a postID, I have all the details of that particular post including postedAt (post time).

What I want to do is that write a query to fetch data from "ImagePosts" and "TextPosts" TOGETHER AND display all the posts in descending/reverse order (that is, the post which is posted last/recently should show up at the top in my Recycler View according to "postedAt").

Please click here to see database structure

To achieve this, I created a single model named Post and two adapters named "PostAdapter" and "TextPostAdapter". And my Recycler View is "DashboardRV". What have I tried so far:

Code of Home Fragment:

public class HomeFragment extends Fragment {

ShimmerRecyclerView dashboardRV;
ArrayList<Post> postList;

public HomeFragment() {

}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_home, container, false);

    dashboardRV = view.findViewById(R.id.dashboardRv);
    dashboardRV.showShimmerAdapter();
    postList = new ArrayList<>();
    PostAdapter postAdapter = new PostAdapter(postList, getContext());
    LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
    dashboardRV.setLayoutManager(layoutManager);
    dashboardRV.addItemDecoration(new DividerItemDecoration(dashboardRV.getContext(), DividerItemDecoration.VERTICAL));
    dashboardRV.setNestedScrollingEnabled(false);

    dashboardRV.setAdapter(postAdapter);
    postList.clear();

database.getReference()
            .child("Posts")
            .child("ImagePosts")
            .addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {
                  
                    for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    }
                    Collections.reverse(postList);
                    dashboardRV.hideShimmerAdapter();
                    postAdapter.notifyDataSetChanged();
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });

    database.getReference()
            .child("Posts")
            .child("TextPosts")
            .addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {
                    postList.clear();
                    for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    }
                    Collections.reverse(postList);
                    dashboardRV.hideShimmerAdapter();
                    textPostAdapter.notifyDataSetChanged();
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });

But the problem with this approach is that it doesn't display all the "TextPosts" and "ImagePosts" together. It only shows all the image posts on the opening app, then when I change fragment and come back, then it displays all text posts. I am just stuck here.

CodePudding user response:

Use only one adapter for one recycler view at a time:

Here is the code for a single adapter with both (image and text posts):

postList = new ArrayList<>();

PostAdapter postAdapter = new PostAdapter(postList, getContext());
dashboardRV.setAdapter(postAdapter);

// call clear before refreshing the list
postList.clear();

database.getReference()
            .child("Posts")
            .child("ImagePosts")
            .addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {

                    for (DataSnapshot dataSnapshot : snapshot.getChildren()){
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    }
                    Collections.sort(postList);
                    dashboardRV.hideShimmerAdapter();
                    postAdapter.notifyDataSetChanged();
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });

    database.getReference()
            .child("Posts")
            .child("TextPosts")
            .addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {

                    for (DataSnapshot dataSnapshot : snapshot.getChildren())              
                        Post post = dataSnapshot.getValue(Post.class);
                        post.setPostId(dataSnapshot.getKey());
                        postList.add(post);
                    }
                    Collections.sort(postList);
                    dashboardRV.hideShimmerAdapter();
                    postAdapter.notifyDataSetChanged();
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });

// make Post class implemented comparable to sort the list after fetching:

// A class 'Post' that implements Comparable
class Post implements Comparable<Post>
{
    ...
 
    // Used to sort posts by their postedAt
    public int compareTo(Post p)
    {
        return this.postedAt - p.postedAt;
    }
 
    ...
}

This link will help to explore how to sort according to postedAt: Using Comparable

CodePudding user response:

To merge 2 separate Firebase Realtime Database requests locally, I recommend you to use Tasks.whenAllSuccess() method. You can achieve this, using the following lines of code:

DatabaseReference imagePostsRef = database.getReference()
        .child("Posts")
        .child("ImagePosts");
DatabaseReference textPostsRef = database.getReference()
        .child("Posts")
        .child("TextPosts");

Task firstTask = imagePostsRef.get();
Task secondTask = textPostsRef.get();

Task combinedTask = Tasks.whenAllSuccess(firstTask, secondTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
    @Override
    public void onSuccess(List<Object> list) {
         //Do what you need to do with your list
    }
});

As you can see, when overriding the "onSuccess()" method the result is a list of objects. In the end, simply map each object from the list into an object of type Post, and pass the new list to a single adapter.

  • Related