Unexpected Behavior When Query by .whereArrayConta

2020-01-29 21:54发布

问题:

I have a RecyclerView that utilizes the FireaseUI and orders all objects from the "Polls" node by the "timestamp" field (sequentially).

New Fragment - .onViewCreated()

 Query queryStore = FirebaseFirestore.getInstance()
            .collection(POLLS_LABEL)
            .orderBy("timestamp", Query.Direction.ASCENDING);
    //Cloud Firestore does not have any ordering; must implement a timestampe to order sequentially

    FirestoreRecyclerOptions<Poll> storeOptions = new FirestoreRecyclerOptions.Builder<Poll>()
            .setQuery(queryStore, Poll.class)
            .build();
    mFirestoreAdaper = new FirestoreRecyclerAdapter<Poll, PollHolder>(storeOptions) {
        @Override
        protected void onBindViewHolder(@NonNull final PollHolder holder, final int position, @NonNull Poll model) {
            holder.mPollQuestion.setText(model.getQuestion());
            String voteCount = String.valueOf(model.getVote_count());
            //TODO: Investigate formatting of vote count for thousands
            holder.mVoteCount.setText(voteCount);
            Picasso.get()
                    .load(model.getImage_URL())
                    .fit()
                    .into(holder.mPollImage);
            holder.mView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent toClickedPoll = new Intent(getActivity(), PollHostActivity.class);
                    String recyclerPosition = getSnapshots().getSnapshot(position).getId();
                    Log.v("Firestore ID", recyclerPosition);
                    toClickedPoll.putExtra("POLL_ID", recyclerPosition);
                    startActivity(toClickedPoll);

                }
            });
        }

I have another layout in my app that subscribes to this same node, but instead queries by "followers" and then by "timestamp.:

Following Fragment - .onViewCreated()

  Query queryStore = FirebaseFirestore.getInstance()
            .collection(POLLS_LABEL)
            .whereArrayContains("followers", mUserId)
            .orderBy("timestamp", Query.Direction.ASCENDING);

    FirestoreRecyclerOptions<Poll> storeOptions = new FirestoreRecyclerOptions.Builder<Poll>()
            .setQuery(queryStore, Poll.class)
            .build();
    mFirestoreAdaper = new FirestoreRecyclerAdapter<Poll, PollHolder>(storeOptions) {
        @Override
        protected void onBindViewHolder(@NonNull final PollHolder holder, final int position, @NonNull Poll model) {
            holder.mPollQuestion.setText(model.getQuestion());
            String voteCount = String.valueOf(model.getVote_count());
            //TODO: Investigate formatting of vote count for thousands
            holder.mVoteCount.setText(voteCount);
            Picasso.get()
                    .load(model.getImage_URL())
                    .fit()
                    .into(holder.mPollImage);
            holder.mView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent toClickedPoll = new Intent(getActivity(), PollHostActivity.class);
                    String recyclerPosition = getSnapshots().getSnapshot(position).getId();
                    Log.v("Firestore ID", recyclerPosition);
                    toClickedPoll.putExtra("POLL_ID", recyclerPosition);
                    startActivity(toClickedPoll);

                }
            });
        }

In the first scenario, UI items populate, in real-time, into my RecyclerView as they are added to Firebase. However, when I query by ".whereArrayContains," I do not get this same behavior, and I was curious as to why. The items only reappear when I restart the application:

Edit:

I commented out the below line:

// .whereArrayContains("followers", mUserId)

and the behavior performed as expected, therefore I can isolate the issue to the .whereArrayContains() query. It is the only difference between each Fragment.

回答1:

This is happening because when you are using whereArrayContains() and orderBy() methods in the same query, an index is required. To use one, go to your Firebase Console and create it manually or if you are using Android Studio, you'll find in your logcat a message that sounds like this:

W/Firestore: (0.6.6-dev) [Firestore]: Listen for Query(products where array array_contains YourItem order by timestamp) failed: Status{code=FAILED_PRECONDITION, description=The query requires an index. You can create it here: ...

You can simply click on that link or copy and paste the url into a web broswer and you index will be created automatically.

Why is this index needed?

As you probably noticed, queries in Cloud Firestore are very fast and this is because Firestore automatically creates an indexes for any fields you have in your document. When you need to order your items, a particular index is required that should be created as explained above. However, if you intend to create the index manually, please also select from the dropdown the corresponding Array contains option, as in the below image:

share|improve this answer