web-dev-qa-db-ja.com

Firestoreクエリから要素を除外する方法は?

ユーザーのコレクションがあり、データベースからすべてのユーザーにクエリを実行して、それらをRecyclerViewに1つを除いて表示しますmine。これは私のdbスキーマです:

users [collection]
  - uid [document]
     - uid: "fR5bih7SysccRu2Gu9990TeSSyg2"
     - username: "John"
     - age: 22
  - //other users

そのようにデータベースをクエリする方法:

String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
Query q = db.collection("users").whereNotEqualTo("uid", uid);

したがって、FirestoreRecyclerOptions内の他のすべてのユーザーを表示するには、このクエリオブジェクトをRecyclerViewオブジェクトに渡す必要があります。

これは可能ですか?そうでない場合、どうすればこれを解決できますか?ありがとう!

編集:

options = new FirestoreRecyclerOptions.Builder<UserModel>()
        .setQuery(query, new SnapshotParser<UserModel>() {
            @NonNull
            @Override
            public UserModel parseSnapshot(@NonNull DocumentSnapshot snapshot) {
                UserModel userModel = documentSnapshot.toObject(UserModel.class);
                if (!userModel.getUid().equals(uid)) {
                    return userModel;
                } else {
                    return new UserModel();
                }
            }
        }).build();
7
Ioana P.

何日もこの問題に取り組んできた後、ようやく答えを見つけました。 @Rajの助けがなければ、私はこれを解決できませんでした。忍耐とガイダンスをありがとう@Raj.

まず最初に、これからの回答で@Frank van Puffelenが提供した回答 post によると、2つのクエリを1つのアダプタに渡すのに役立つソリューションの検索をやめました。

この質問で私が達成したかったのは、データベースをクエリして、1人を除くすべてのユーザーを取得することだけでした。したがって、2つのクエリを1つのインスタンスに結合できないため、両方のクエリの結果を結合できることがわかりました。だから私は2つのクエリを作成しました:

_FirebaseFirestore db = FirebaseFirestore.getInstance();
Query firstQuery = db.collection("users").whereLessThan("uid", uid);
Query secondQuery = db.collection("users").whereGreaterThan("uid", uid);
_

ユーザーオブジェクトにUserModel(POJO)クラスがあります。 1つではないが見つかりましたが、2つの問題を解決する方法が見つかりました。 1つ目は、データベースにクエリを実行して、最初の基準に対応するすべてのユーザーオブジェクトを取得し、それらをリストに追加することです。その後、データベースを再度クエリし、2番目の基準に対応する他のユーザーオブジェクトを取得して、sameリストに追加します。これで、必要なすべてのユーザー(1つはクエリからの特定のIDを持つユーザー)を含むリストが作成されました。これは将来の訪問者のためのコードです:

_firstQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        List<UserModel> list = new ArrayList<>();
        if (task.isSuccessful()) {
            for (DocumentSnapshot document : task.getResult()) {
                UserModel userModel = document.toObject(UserModel.class);
                list.add(userModel);
            }

            secondQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        for (DocumentSnapshot document : task.getResult()) {
                            UserModel userModel = document.toObject(UserModel.class);
                            list.add(userModel);
                        }

                        //Use the list of users
                    }
                }
            });
        }
    }
});
_

2番目のアプローチは、次のようにTasks.whenAllSuccess()を使用するため、はるかに短くなります。

_Task firstTask = firstQuery.get();
Task secondTask = secondQuery.get();

Task combinedTask = Tasks.whenAllSuccess(firstTask, secondTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
        @Override
        public void onSuccess(List<Object> list) {
            //This is the list that I wanted
        }
});
_
6
Ioana P.

Firestoreはnot equal to操作をサポートしていません。したがって、クライアント側でデータをフィルタリングする必要があります。あなたの場合、あなたは1つの余分なアイテムしか持っていないので、あなたはそれを取り除くことができます。

そのため、リサイクラーアダプターデータレイヤーにデータを追加するときに、!=条件に一致するデータを制限する独自のリサイクラー実装を構築する必要がある場合があります。

提供されているリサイクラー実装firebaseについては調べていませんので、アダプターデータへのデータ操作をサポートしているかどうかはわかりません。

以下に、リサイクラービューの実装を開始するための適切なリソースを示します。 https://www.androidhive.info/2016/01/Android-working-with-recycler-view/

4
Umar Hussain

公式のファイヤーストアのドキュメントによると:-

Cloud Firestoreは、次のタイプのクエリをサポートしていません。

!=句を使用したクエリ。この場合、クエリを大なりクエリと小なりクエリに分割する必要があります。たとえば、クエリ句where( "age"、 "!="、 "30")はサポートされていませんが、where( "age"、 "<句を含むクエリを2つ組み合わせることで、同じ結果セットを取得できます。 "、" 30 ")とwhere(" age "、"> "、30)という句を含むもの。

FirestoreRecyclerAdapterを使用している場合、FirestoreRecyclerOptionsはsetQuery()メソッドを使用してクエリを直接受け入れるため、クライアント側のフィルタリングを実行できません。

リサイクラービューでアイテムが空になる可能性のあるデータを設定しているときにonBindViewHolder()でフィルターを適用しようとした場合。これを解決するには、方法2を参照してください。

したがって、問題の解決策として考えられるのは、すべてのドキュメントの下のユーザーコレクションに整数フィールドを作成することです。例えば:-

users [collection]
  - uid [document]
     - uid: "fR5bih7SysccRu2Gu9990TeSSyg2"
     - username: "John"
     - age: 22
     - check: 100

これで、値が100の「check」変数を作成しました。したがって、他のすべてのドキュメントに「check」の値を100未満に設定します。これで、check <100のドキュメントを検索するクエリを簡単に作成できます:-

Query q = db.collection("users").whereLessThan("check", 100);

これにより、不要なドキュメントを除くすべてのドキュメントが取得されます。また、データを設定するときに、チェック変数をスキップして他のパラメーターを設定できます。

方法2(クライアント側のフィルタリング)

OnBindViewHolder()メソッドでチェックを適用して、取得したuidが現在のユーザーuidと一致する場合、Recyclerビューの高さを0dpに設定できます。なので:-

ViewUserAdapter.Java

public class ViewUserAdapter extends FirestoreRecyclerAdapter<User, ViewUserAdapter.ViewUserHolder>
{
    String uid;
    FirebaseAuth auth;

    public ViewUserAdapter(@NonNull FirestoreRecyclerOptions<User> options)
    {
        super(options);
        auth = FirebaseAuth.getInstance();
        uid = auth.getCurrentUser().getUid();
    }

    @Override
    protected void onBindViewHolder(@NonNull ViewUserHolder holder, int position, @NonNull User model)
    {
        DocumentSnapshot snapshot =  getSnapshots().getSnapshot(position);
        String id = snapshot.getId();

        if(uid.equals(id))
        {
            RecyclerView.LayoutParams param = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
            param.height = 0;
            param.width = LinearLayout.LayoutParams.MATCH_PARENT;
            holder.itemView.setVisibility(View.VISIBLE);

        }
        else
        {
            holder.tvName.setText(model.name);
            holder.tvEmail.setText(model.email);
            holder.tvAge.setText(String.valueOf(model.age));
        }
    }
}
3
Raj

最も簡単な解決策は、PagedListAdapterを使用してカスタムDataSourceを作成することです。 Firestoreクエリ用。 DataSourceでは、Queryを配列またはArrayListに変換できますメソッドcallback.onResult(...)にデータを追加する前に、アイテムを簡単に削除できます。

同様のソリューションを使用して、Firestoreクエリの後でデータを処理し、時間属性でフィルターして並べ替え、クライアントの品質スコア属性で再度並べ替えてから、データをcallback.onResult(...)に返しました。 。

ドキュメンテーション

データソースのサンプル

class ContentFeedDataSource() : ItemKeyedDataSource<Date, Content>() {

override fun loadBefore(params: LoadParams<Date>, callback: LoadCallback<Content>) {}

override fun getKey(item: Content) = item.timestamp

override fun loadInitial(params: LoadInitialParams<Date>, callback: LoadInitialCallback<Content>) {
    FirestoreCollections.contentCollection
            .collection(FirestoreCollections.ALL_COLLECTION)
            .orderBy(Constants.TIMESTAMP, Query.Direction.DESCENDING)
            .whereGreaterThanOrEqualTo(Constants.TIMESTAMP, DateAndTime.getTimeframe(WEEK))
            .limit(params.requestedLoadSize.toLong())
            .get().addOnCompleteListener {
                val items = arrayListOf<Content?>()
                for (document in it.result.documents) {
                    val content = document.toObject(Content::class.Java)
                    items.add(content)
                }
                callback.onResult(items.sortedByDescending { it?.qualityScore })
            }
}

override fun loadAfter(params: LoadParams<Date>, callback: LoadCallback<Content>) {
    FirestoreCollections.contentCollection
            .collection(FirestoreCollections.ALL_COLLECTION)
            .orderBy(Constants.TIMESTAMP, Query.Direction.DESCENDING)
            .startAt(params.key)
            .whereGreaterThanOrEqualTo(Constants.TIMESTAMP, DateAndTime.getTimeframe(WEEK))
            .limit(params.requestedLoadSize.toLong())
            .get().addOnCompleteListener {
                val items = arrayListOf<Content?>()
                for (document in it.result.documents) {
                    val content = document.toObject(Content::class.Java)
                    items.add(content)
                }
                val sortedByQualityScore = ArrayList(items.sortedByDescending { it?.qualityScore })
                callback.onResult(sortedByQualityScore)
                sortedByQualityScore.clear()
            }
}
}
0
Adam Hurwitz

よりシンプルで以前のクライアント側フィルタリング(リストにアイテムを追加する場合):

  1. Firestoreの標準メソッドを使用して、現在のユーザーのIDを取得します。
  2. ユーザーコレクション内のすべてのユーザーのドキュメントの名前を取得します。
  3. ユーザーをRecyclerViewリストに追加する前に、リストに追加しようとしているユーザーが現在のユーザーでないことを確認してください。

このようにすると、クライアント側で「等しくない」メソッドを使用して、Firestoreの問題に陥ることがなくなります。別の利点は、アダプターをいじったり、リサイクラーに必要のないリスト項目からビューを非表示にする必要がないことです。

public void getUsers(final ArrayList<Users> usersArrayList, final Adapter adapter) {

    CollectionReference usersCollectionRef = db.collection("users");

    Query query = usersCollectionRef
            .whereEqualTo("is_onboarded", true);

    query.get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        for (QueryDocumentSnapshot document : task.getResult()) {

                            final String otherUserID = document.getId();

                             FirebaseUser user = mAuth.getCurrentUser();
                             String currentUserID = user.getUid();

                            if (!otherUserID.equals(currentUserId)) {

                              usersArrayList.add(new User(otherUserID));
                              adapter.notifyDataSetChanged(); //Ensures users are visible immediately
                                            }
                                        } else {
                                            Log.d(TAG, "get failed with ", task.getException());
                                        }
                                    }
                                });
                            }

                        }
                    } else {
                        Log.d(TAG, "Error getting documents: ", task.getException());
                    }
                }
            });
}
0