web-dev-qa-db-de.com

Wie kann ich ein Element von einer Firestore-Abfrage ausschließen?

Ich habe eine Sammlung von Benutzern, und ich möchte alle Benutzer aus der Datenbank abfragen und sie in einer RecyclerView anzeigen, außer einem, meinem . Das ist mein Datenbankschema:

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

So fragen Sie die Datenbank ab:

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

Daher muss dieses Abfrageobjekt an ein FirestoreRecyclerOptions-Objekt übergeben werden, um alle anderen Benutzer in RecyclerView anzuzeigen.

Ist das überhaupt möglich? Wenn nicht, wie kann ich das lösen? Vielen Dank!

Bearbeiten:

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();
6
Ioana P.

Nach Tagen und Tagen, in denen ich mich mit diesem Thema auskämpfte, fand ich endlich die Antwort. Ich könnte das nicht ohne die Hilfe von @Raj lösen. Vielen Dank @Raj für die Geduld und Anleitung.

Zunächst einmal habe ich laut der Antwort von @Frank van Puffelen in seiner Antwort aus diesem post aufgehört, nach einer Lösung zu suchen, die mir dabei helfen kann, zwei Abfragen an einen einzigen Adapter zu übergeben.

In dieser Frage wollte ich nur die Datenbank abfragen, um alle Benutzer außer einem, mir, zu erhalten. Da wir also nicht zwei Abfragen in einer einzigen Instanz kombinieren können, habe ich festgestellt, dass wir das Ergebnis beider Abfragen kombinieren können. Ich habe also zwei Abfragen erstellt:

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

Ich habe eine UserModel (POJO) -Klasse für mein Benutzerobjekt. Ich habe keine gefunden, aber zwei Wege, um das Problem zu lösen. Die erste wäre, die Datenbank abzufragen, um alle Benutzerobjekte abzurufen, die den ersten Kriterien entsprechen, und sie einer Liste hinzuzufügen. Fragen Sie danach die Datenbank erneut ab, rufen Sie die anderen Benutzerobjekte ab, die den zweiten Kriterien entsprechen, und fügen Sie sie der Liste same hinzu. Jetzt habe ich eine Liste, die alle Benutzer enthält, die ich bis auf einen brauche, die mit dieser bestimmten ID aus den Abfragen. Dies ist der Code für zukünftige Besucher:

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
                    }
                }
            });
        }
    }
});

Der zweite Ansatz wäre viel kürzer, weil ich Tasks.whenAllSuccess() so benutze:

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
        }
});
5
Ioana P.

Firestore unterstützt keine not equal to-Operation. Sie müssen also die Daten auf der Clientseite filtern. Da Sie nur einen zusätzlichen Artikel haben, können Sie ihn herausfiltern.

Zu diesem Zweck müssen Sie möglicherweise eine eigene Recycler-Implementierung erstellen, bei der Sie beim Hinzufügen von Daten zur Recycler-Adapter-Datenschicht die Daten einschränken, wenn sie Ihrer !=-Bedingung entsprechen.

Ich habe noch nicht über die Implementierung der Implementierung von Recyclingherstellern nach Firebase nachgeforscht, daher kann ich nicht sagen, dass sie die Datenmanipulation an Adapterdaten unterstützt oder nicht.

Hier finden Sie eine gute Ressource, um die Recycler-Ansicht zu implementieren: https://www.androidhive.info/2016/01/Android-working-with-recycler-view/

4
Umar Hussain

Laut der offiziellen Firestore-Dokumentation: -

Cloud Firestore unterstützt den folgenden Abfragetyp nicht:

Abfragen mit einer! = -Klausel. In diesem Fall sollten Sie die Abfrage teilen in eine Abfrage "größer als" und eine Abfrage "kleiner als". Zum Beispiel obwohl Die Abfrageklausel wo ("Alter", "! =", "30") nicht unterstützt wird, können Sie Holen Sie sich dieselbe Ergebnismenge, indem Sie zwei Abfragen kombinieren, eine mit der Klausel wo ("Alter", "<", "30") und eine mit der Klausel wo ("Alter", ">", 30).

Wenn Sie FirestoreRecyclerAdapter verwenden, akzeptiert FirestoreRecyclerOptions die Abfrage direkt mit der setQuery () - Methode und ermöglicht daher keine clientseitige Filterung. 

Wenn Sie versuchen, Filter in onBindViewHolder () anzuwenden, während Sie die Daten festlegen, die möglicherweise leere Elemente in der Recycling-Ansicht ergeben. Um dies zu beheben, verweisen Sie auf Methode 2.

Die mögliche Lösung für Ihr Problem wäre also, ein ganzzahliges Feld in Ihrer Benutzersammlung unter jedem Dokument zu erstellen. Z.B:-

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

In diesem Fall habe ich eine 'check'-Variable mit einem Wert von 100 erstellt. Setzen Sie also den Wert von' check 'in allen anderen Dokumenten auf weniger als 100. Nun können Sie problemlos eine Abfrage durchführen, die Dokumente mit check <100 findet wie:-

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

Dadurch werden alle Ihre Dokumente mit Ausnahme der Dokumente abgerufen, die Sie nicht möchten. Und während Sie die Daten einstellen, können Sie andere Parameter einstellen, wobei die Prüfvariable übersprungen wird.

Methode 2 (clientseitige Filterung)

Wir können eine Prüfung in der Methode onBindViewHolder () durchführen, die festlegt, dass die abgerufene Benutzer-ID mit der aktuellen Benutzer-Benutzer-ID übereinstimmt und die Höhe der Recycler-Ansicht auf 0dp setzt. Wie:-

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

Die einfachste Lösung wäre, einen PagedListAdapter zu verwenden und eine benutzerdefinierte DataSource für die Firestore-Abfragen zu erstellen. In der DataSource kann die Query in ein Array oder eine ArrayList umgewandelt werden, in der Sie Ihr Element leicht entfernen können, bevor Sie die Daten zur Methode callback.onResult(...) hinzufügen. 

Ich habe eine ähnliche Lösung verwendet, um Daten nach einer Firestore-Abfrage zu verarbeiten, um nach einem Zeitattribut zu filtern und zu sortieren und dann nach einem Qualitätsbewertungsattribut im Client neu zu sortieren, bevor die Daten wieder an callback.onResult(...) übergeben werden.

Dokumentation

Datenquellenbeispiel

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

Einfachere und frühere clientseitige Filterung (wenn Sie der Liste Elemente hinzufügen):

  1. Rufen Sie die aktuelle Benutzer-ID mithilfe der Standardmethode von Firestore ab.
  2. Rufen Sie den Namen des Dokuments für alle Benutzer in Ihrer Benutzersammlung ab.
  3. Bevor Sie den Benutzer zu Ihrer RecyclerView-Liste hinzufügen, überprüfen Sie, ob der Benutzer, den er gerade Ihrer Liste hinzufügen soll, nicht der aktuelle Benutzer ist. 

Wenn dies auf diese Weise erledigt ist, können Sie auf Client-Seite die Methode "nicht gleich" verwenden und keine Probleme mit Firestore feststellen. Ein weiterer Vorteil besteht darin, dass Sie sich nicht mit Ihrem Adapter verwechseln oder die Ansicht vor einem Listeneintrag ausblenden müssen, den Sie nicht im Recyclingprogramm haben wollten.

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