Abfrage mit Bereichs- und Ungleichheitsfiltern für mehrere Felder – Übersicht

In Firestore können Bereichs- und Ungleichheitsfilter für mehrere Felder in einer einzelnen Abfrage verwendet werden. Sie können Bereichs- und Ungleichheitsbedingungen für mehrere Felder festlegen und die Anwendungsentwicklung vereinfachen, indem Sie die Implementierung der Post-Filter-Logik an Firestore delegieren.

Bereichs- und Ungleichheitsfilter für mehrere Felder

In der folgenden Abfrage werden Bereichsfilter für Bevölkerung und Bevölkerungsdichte verwendet, um alle Städte zurückzugeben, in denen die Bevölkerung mehr als 1.000.000 Personen und die Bevölkerungsdichte weniger als 10.000 Personen pro Flächeneinheit beträgt.

Webversion 9 (modular)

const q = query(
    collection(db, "cities"),
    where('population', '>', 1000000),
    where('density', '<', 10000),
  );

Swift

let query = db.collection("cities")
  .whereField("population", isGreaterThan: 1000000)
  .whereField("density", isLessThan: 10000)

Objective-C

FIRQuery *query =
 [[[[self.db collectionWithPath:@"cities"]
queryWhereField:@"population" isGreaterThan:@1000000]
   queryWhereField:@"density" isLessThan:@10000];

Java Android

Query query = db.collection("cities")
 .whereGreaterThan("population", 1000000)
 .whereLessThan("density", 10000);

Kotlin+KTX Android

val query = db.collection("cities")
 .whereGreaterThan("population", 1000000)
 .whereLessThan("density", 10000)

Go

   query := client.Collection("cities").
      Where("population", ">", 1000000).
      Where("density", "<", 10000)

Java

db.collection("cities")
  .whereGreaterThan("population", 1000000)
  .whereLessThan("density", 10000);

Node.js

db.collection("cities")
  .where('population', '>', 1000000),
  .where('density', '<', 10000)

Python

from google.cloud import firestore

db = firestore.Client()
query = db.collection("cities")
.where("population", ">", 1000000)
.where("density", "<", 10000)

PHP

$collection = $db->collection('samples/php/cities');
$chainedQuery = $collection
    ->where('population', '>', 1000000)
    ->where('density', '<', 10000);

C#

CollectionReference citiesRef = db.Collection("cities");
Query query = citiesRef
    .WhereGreaterThan("Population", 1000000)
    .WhereLessThan("Density", 10000);
QuerySnapshot querySnapshot = await query.GetSnapshotAsync();
foreach (DocumentSnapshot documentSnapshot in querySnapshot)
{
    var name = documentSnapshot.GetValue<string>("Name");
    var population = documentSnapshot.GetValue<int>("Population");
    var density = documentSnapshot.GetValue<int>("Density");
    Console.WriteLine($"City '{name}' returned by query. Population={population}; Density={density}");
}

Ruby

query = cities_ref.where("population", ">", "1000000")
                  .where("density", "<", 10000)

C++

CollectionReference cities_ref = db->Collection("cities");
Query query = cities_ref.WhereGreaterThan("population", FieldValue::Integer(1000000))
                       .WhereLessThan("density", FieldValue::Integer(10000));

Einheit

CollectionReference citiesRef = db.Collection("cities");
Query query = citiesRef.WhereGreaterThan("population", 1000000)
                      .WhereLessThan("density", 10000);

Dart

final citiesRef = FirebaseFirestore.instance.collection('cities')
final query = citiesRef.where("population", isGreaterThan: 1000000)
                  .where("density", isLessThan: 10000);

Überlegungen zur Indexierung

Bevor Sie Ihre Abfragen ausführen, sollten Sie die Artikel zu Abfragen und zum Firestore-Datenmodell lesen.

In Firestore wird durch die ORDER BY-Klausel einer Abfrage festgelegt, welche Indexe zum Ausführen der Abfrage verwendet werden können. Für eine ORDER BY a ASC, b ASC-Abfrage ist beispielsweise ein zusammengesetzter Index für die Felder a ASC, b ASC erforderlich.

Um die Leistung und Kosten von Firestore-Abfragen zu optimieren, müssen Sie die Reihenfolge der Felder im Index optimieren. Dazu muss Ihr Index von links nach rechts so sortiert sein, dass die Abfrage auf ein Dataset reduziert wird, das das Scannen unnötiger Indexeinträge verhindert.

Angenommen, Sie möchten eine Sammlung von Mitarbeitern durchsuchen und Mitarbeiter in den USA finden, deren Gehalt mehr als 100.000 $beträgt und deren Berufserfahrung mehr als 0 Jahre beträgt. Auf Grundlage Ihres Verständnisses des Datasets wissen Sie, dass die Gehaltsbeschränkung selektiver ist als die Beschränkung der Berufserfahrung. Der ideale Index, der die Anzahl der Indexscans reduzieren würde, ist der (salary [...], experience [...]). Die schnelle und kostengünstige Abfrage würde salary vor experience anordnen und so aussehen:

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .whereGreaterThan("experience", 0)
  .orderBy("salary")
  .orderBy("experience");

Node.js

db.collection("employees")
  .where("salary", ">", 100000)
  .where("experience", ">", 0)
  .orderBy("salary")
  .orderBy("experience");

Python

db.collection("employees")
  .where("salary", ">", 100000)
  .where("experience", ">", 0)
  .order_by("salary")
  .order_by("experience");

Best Practices für die Optimierung von Indexen

Beachten Sie beim Optimieren von Indexen die folgenden Best Practices.

Indexfelder nach Gleichheitsbedingungen und dann nach dem selektivsten Bereichs- oder Ungleichheitsfeld sortieren

Firestore verwendet die am weitesten links stehenden Felder eines zusammengesetzten Index, um die Gleichheitsbedingungen und die Bereichs- oder Ungleichheitsbedingung (falls vorhanden) für das erste Feld der orderBy()-Abfrage zu erfüllen. Durch diese Einschränkungen kann die Anzahl der Indexeinträge, die von Firestore gescannt werden, reduziert werden. Firestore verwendet die verbleibenden Felder des Index, um andere Bereichs- oder Ungleichheitsbedingungen der Abfrage zu erfüllen. Diese Einschränkungen reduzieren nicht die Anzahl der Indexeinträge, die von Firestore gescannt werden, sondern filtern nicht übereinstimmende Dokumente heraus, sodass die Anzahl der Dokumente, die an die Clients zurückgegeben werden, reduziert wird.

Weitere Informationen zum Erstellen effizienter Indexe finden Sie unter Indexeigenschaften.

Felder in absteigender Reihenfolge der Selektivität der Abfragebeschränkung anordnen

Damit Firestore den optimalen Index für Ihre Abfrage auswählt, geben Sie eine orderBy()-Klausel an, in der Felder in absteigender Reihenfolge der Selektivität der Abfragebeschränkung angeordnet sind. Eine höhere Selektivität entspricht einer kleineren Teilmenge von Dokumenten, während eine niedrigere Selektivität einer größeren Teilmenge von Dokumenten entspricht. Achten Sie darauf, dass Sie Bereichs- oder Ungleichheitsfelder mit höherer Selektivität früher in der Indexreihenfolge auswählen als Felder mit niedrigerer Selektivität.

Um die Anzahl der Dokumente zu minimieren, die Firestore scannt und über das Netzwerk zurückgibt, sollten Sie Felder immer in absteigender Reihenfolge der Selektivität der Abfragebeschränkung anordnen. Wenn der Ergebnissatz nicht in der erforderlichen Reihenfolge ist und voraussichtlich klein sein wird, können Sie clientseitige Logik implementieren, um ihn entsprechend Ihren Erwartungen neu zu sortieren.

Angenommen, Sie möchten in einer Sammlung von Mitarbeitern nach Mitarbeitern in den USA suchen, deren Gehalt mehr als 100.000 $beträgt, und die Ergebnisse nach dem Jahr der Berufserfahrung des Mitarbeiters sortieren. Wenn Sie davon ausgehen, dass nur wenige Mitarbeiter ein Gehalt von mehr als 100.000 $haben, ist die effizienteste Art, die Abfrage zu schreiben, folgende:

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .orderBy("salary")
  .get()
  .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
          // Order results by `experience`
        }
    });;

Node.js

const querySnapshot = await db.collection('employees')
                              .where("salary", ">", 100000)
                              .orderBy("salary")
                              .get();

// Order results by `experience`

Python

results = db.collection("employees")
            .where("salary", ">", 100000)
            .order_by("salary")
            .stream()

// Order results by `experience`

Wenn Sie der Abfrage eine Sortierung für experience hinzufügen, erhalten Sie zwar dieselben Dokumente und müssen die Ergebnisse nicht noch einmal auf den Clients sortieren, aber die Abfrage liest möglicherweise viel mehr irrelevante Indexeinträge als die vorherige Abfrage. Das liegt daran, dass Firestore immer einen Index bevorzugt, dessen Indexfelder mit dem Präfix der ORDER BY-Klausel der Abfrage übereinstimmen. Wenn experience der ORDER BY-Klausel hinzugefügt wurde, wählt Firestore den (experience [...], salary [...])-Index aus, um die Abfrageergebnisse zu berechnen. Da es keine weiteren Einschränkungen für experience gibt, liest Firestore alle Indexeinträge der Sammlung employees, bevor der Filter salary angewendet wird, um das endgültige Ergebnis zu ermitteln. Das bedeutet, dass Indexeinträge, die nicht dem salary-Filter entsprechen, trotzdem gelesen werden, was die Latenz und die Kosten der Abfrage erhöht.

Preise

Abfragen mit Bereichs- und Ungleichheitsfiltern für mehrere Felder werden basierend auf den gelesenen Dokumenten und Indexeinträgen abgerechnet.

Weitere Informationen finden Sie auf der Seite Preise.

Beschränkungen

Beachten Sie neben den Abfragebeschränkungen die folgenden Einschränkungen, bevor Sie Abfragen mit Bereichs- und Ungleichheitsfiltern für mehrere Felder verwenden:

  • Abfragen mit Bereichs- oder Ungleichheitsfiltern für Dokumentfelder und nur Gleichheitsbeschränkungen für den Dokumentschlüssel (__name__) werden nicht unterstützt.
  • In Firestore ist die Anzahl der Bereichs- oder Ungleichheitsfelder auf 10 begrenzt. So soll verhindert werden, dass die Ausführung von Abfragen zu teuer wird.

Nächste Schritte