Populate Androidx RecyclerView with CardView with data of two collections from Firebase Firestore

Mar1o :

I have 2 different collections in Cloud Firestore:

  • accounts (identifier: userId)
    • username
    • pictureURL
  • posts (identifier: id)
    • userId
    • text
    • likes

Aim: I want to have these two collections in Fragment with a Androidx RecyclerView to show the posts with the username and pictureURL.

I used "androidx.recyclerview:recyclerview:1.1.0" in my build.gradle.

I use the following method for getting the Posts from Firebase and store them in the model "FirebasePost":

/**
     * Get all FirebasePost objects from Firebase Firestore collection
     * @return
     */
    public FirestoreRecyclerOptions<FirebasePost> getPosts() {
        Query mQuery = mFirestore.collection("posts").orderBy("createdAt", Query.Direction.DESCENDING);
        // Get the response elements
        FirestoreRecyclerOptions<FirebasePost> response = new FirestoreRecyclerOptions.Builder<FirebasePost>()
                .setQuery(mQuery, FirebasePost.class)
                .build();
        return response;
    }

I populate the RecyclerView by the following code:

/**
     * Prepares the Recycler View for showing the Posts data
     * @param mView Gets the current view
     */
    private void prepareRecyclerView(View mView, FirestoreRecyclerOptions<FirebasePost> response) {
        adapter = new FirestoreRecyclerAdapter<FirebasePost, PostHolder>(response) {
            @Override
            public void onBindViewHolder(@NonNull PostHolder holder, int position, @NonNull FirebasePost model) {
                // Bind the FirebasePost object to the PostHolder
                holder.setPostText_to_UI(model.getText());
                holder.setTimestamp_to_UI(model.getCreatedAt());
                holder.setAuthorDisplayName_to_UI(model.getAuthorUserId());
            }

            @NonNull
            @Override
            public PostHolder onCreateViewHolder(@NonNull ViewGroup group, int i) {
                // Create a new instance of the ViewHolder, in this case we are using a custom
                // layout called R.layout.message for each item
                View view = LayoutInflater.from(group.getContext())
                        .inflate(R.layout.posts_item_cardview, group, false);

                return new PostHolder(view);
            }
        };
        mPostsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        mPostsRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mPostsRecyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }

How can I implement a second request to Firebase to get the user information and add these into the right places of the RecyclerView? The problem is, that I get the AuthorUserId but not the displayName of the User because I cannot perform another request to Firestore, before giving the model to the RecyclerView.

Alex Mamo :

I want to have these two collections in Fragment with an AndroidX RecyclerView to show the posts with the username and pictureURL.

Queries in Firestore are shallow, meaning that you can only get items from the collection that the query is run against. There is no way to get documents from different collections in a single query. In the NoSQL world, there is no concept of INNER JOIN based on an id that exist within two tables. Firestore doesn't support queries across different collections in one go unless you are using Firestore collection group queries, but it's not the case. A single query may only use the properties of documents in a single collection.

The simplest solution I can think of would be to store under each post, user's data (username and pictureURL). This practice is called denormalization and is a common practice when it comes to Firebase. If you are new to NoSQL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase Realtime Database but the same rules apply to Cloud Firestore.

For more information I recommend you also read my answer from the following post:

Edit:

When you are duplicating data, there is one thing that you need to keep in mind. In the same way, you are adding data, you need to maintain it. In other words, if you want to update/delete an item, you need to do it in every place that it exists. This is how this technique works.

I want to avoid to iterate over all (maybe millions of) posts and delete the duplicated user data there.

For heavy updates/deletes in your database, every time is a trade-off, but it always depends on your exact use-case and requirements. Another solution might be to query the database twice, once to get the posts and right after that, the corresponding user details. This technique is not so slow as you might think. So you should consider that as an option too.

Another solution that you also should consider is to augment your data to accept a reverse look-up. That being said, you can add under each user object, the corresponding post in a subcollection. If you need to query all posts of all users, then a collection group query will be required.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=345777&siteId=1