Distinct

Beschreibung

Alle eindeutigen Kombinationen von Werten für eine Reihe von Ausdrücken finden.

Die Phase distinct(...) hat eine ähnliche Syntax wie select(...), da sie einen oder mehr auswählbare Ausdrücke akzeptiert. Strings können verwendet werden, wenn der Ausdruck nur eine Feldreferenz ist:

Beispiele

Node.js
let cities = await db.pipeline()
  .collection("cities")
  .distinct("country")
  .execute();

cities = await db.pipeline()
  .collection("cities")
  .distinct(
    field("state").toLower().as("normalizedState"),
    field("country"))
  .execute();

Web

let cities = await execute(db.pipeline()
  .collection("cities")
  .distinct("country"));

cities = await execute(db.pipeline()
  .collection("cities")
  .distinct(
    field("state").toLower().as("normalizedState"),
    field("country")));
Swift
let results = try await db.pipeline()
  .collection("books")
  .distinct([
    Field("author").toUpper().as("author"),
    Field("genre")
  ])
  .execute()

Kotlin

var cities = db.pipeline()
    .collection("cities")
    .distinct("country")
    .execute()

cities = db.pipeline()
    .collection("cities")
    .distinct(
        field("state").toLower().alias("normalizedState"),
        field("country")
    )
    .execute()

Java

Task<Pipeline.Snapshot> cities;
cities = db.pipeline()
        .collection("cities")
        .distinct("country")
        .execute();

cities = db.pipeline()
        .collection("cities")
        .distinct(
                field("state").toLower().alias("normalizedState"),
                field("country"))
        .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

cities = client.pipeline().collection("cities").distinct("country").execute()

cities = (
    client.pipeline()
    .collection("cities")
    .distinct(Field.of("state").to_lower().as_("normalizedState"), "country")
    .execute()
)
Java
Pipeline.Snapshot cities1 =
    firestore.pipeline().collection("cities").distinct("country").execute().get();

Pipeline.Snapshot cities2 =
    firestore
        .pipeline()
        .collection("cities")
        .distinct(toLower(field("state")).as("normalizedState"), field("country"))
        .execute()
        .get();
Go
cities1, err := client.Pipeline().Collection("cities").Distinct(firestore.Fields("country")).Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

cities2, err := client.Pipeline().Collection("cities").
	Distinct(firestore.Fields(
		firestore.ToLower(firestore.FieldOf("state")).As("normalizedState"),
		firestore.FieldOf("country"),
	)).
	Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

Verhalten

Die distinct(...) Phase funktioniert ähnlich wie eine aggregate(...) Phase ohne Gruppen. Weitere Informationen finden Sie unter aggregate(...) und select(...).

Eindeutige Feldwerte finden

Wenn Sie beispielsweise eine Liste aller Länder in der folgenden cities-Collection abrufen möchten:

Node.js

await db.collection("cities").doc("SF").set({name: "San Francisco", state: "CA", country: "USA"});
await db.collection("cities").doc("LA").set({name: "Los Angeles", state: "CA", country: "USA"});
await db.collection("cities").doc("NY").set({name: "New York", state: "NY", country: "USA"});
await db.collection("cities").doc("TOR").set({name: "Toronto", state: null, country: "Canada"});
await db.collection("cities").doc("MEX").set({name: "Mexico City", state: null, country: "Mexico"});

Eindeutige Länder können mit folgender Abfrage gefunden werden:

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .distinct("country")
  .execute();

Dadurch wird das folgende Ergebnis generiert:

{ country: "USA" }
{ country: "Canada" }
{ country: "Mexico" }

Eindeutige Ausgabe von Ausdrücken

Sie können auch die eindeutigen Kombinationen mehrerer Felder oder komplizierterer Ausdrücke finden. Beispiel:

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .distinct(
    field("state").toLower().as("normalized_state"),
    field("country"))
  .execute();

Ergebnis:

{ country: "USA", normalized_state: "ca" }
{ country: "USA", normalized_state: "ny" }
{ country: "Canada", normalized_state: null }
{ country: "Mexico", normalized_state: null }

Verhalten bei Äquivalenz

Das Verhalten bei der Äquivalenz von eindeutigen Werten folgt derselben Semantik wie Gleichheitsvergleiche.

Das bedeutet, dass äquivalente Werte, z. B. mathematisch äquivalente numerische Werte, unabhängig von den ursprünglichen Typen (32-Bit-Ganzzahl, 64-Bit-Ganzzahl, Gleitkommazahlen, Dezimalzahlen usw.) als derselbe eindeutige Wert betrachtet werden.

In einer Collection numerics mit verschiedenen Dokumenten, die foo Werte vom Typ 32-Bit-Ganzzahl 1, 64-Bit-Ganzzahl 1L und Gleitkommazahl 1.0 enthalten, gibt distinct(...) beispielsweise nur ein Ergebnis zurück.

In solchen Fällen, in denen verschiedene äquivalente Werte im Dataset vorhanden sind, kann der Ausgabewert der Gruppe jeder dieser äquivalenten Werte sein. In diesem Beispiel kann der Wert von foo als 1, 1L oder 1.0 zurückgegeben werden.

Auch wenn es deterministisch erscheint, sollten Sie sich nicht darauf verlassen, dass ein bestimmter Wert ausgewählt wird.

Arbeitsspeichernutzung

Wie die Phase distinct(...) ausgeführt wird, hängt von den verfügbaren Indexen ab. Wenn der Abfrageoptimierer keinen geeigneten Index auswählt, müssen bei distinct(...) alle eindeutigen Werte im Arbeitsspeicher gepuffert werden.

Wenn eine sehr große Anzahl eindeutiger Werte vorhanden ist oder die Werte sehr groß sind (z.B. eindeutige Werte für sehr große Werte), kann für diese Phase ein Out of Memory-Fehler auftreten.

In solchen Fällen sollten Sie Filter anwenden, um das Dataset zu beschränken, auf das distinct(...) angewendet wird, oder Indexe wie empfohlen erstellen, um eine hohe Arbeitsspeichernutzung zu vermeiden.

Mit Query Explain erhalten Sie Informationen zum tatsächlichen Abfrageausführungsplan und Profiling-Daten, die bei der Fehlerbehebung helfen können.