Beschreibung
Mit der subcollection(...)-Eingabephase lassen sich ganz einfach Parent-Child-Joins mit dem integrierten Feld __name__ ausführen.
An die subcollection(...)-Phase können weitere Phasen angehängt werden, um Filterung oder Aggregation für die verschachtelten Dokumente durchzuführen. Beachten Sie, dass sich alle Feldverweise, die in nachfolgenden Phasen verwendet werden, auf die Dokumente aus der verschachtelten Sammlung und nicht auf das übergeordnete Dokument beziehen. Wenn Sie auf Felder im übergeordneten Bereich verweisen möchten, verwenden Sie zuerst die Phase let(...), um Variablen zu definieren, und verweisen Sie dann im lokalen Bereich auf diese Variablen.
Beispiele
Node.js
db.pipeline()
.collection("/restaurants")
.add_fields(subcollection("reviews")
.aggregate(average("rating").as("avg_rating"))
.toScalarExpression()
.as("avg_rating"))
Verhalten
Die Phase subcollection(...) muss im Kontext einer Unterabfrage verwendet werden. Dabei wird die __name__ (die Dokumentreferenz) des aktuellen Dokuments im übergeordneten Bereich verwendet, um zu bestimmen, welche untergeordnete Sammlung abgerufen werden soll. Wenn das übergeordnete Dokument beispielsweise /restaurants/pizza-place ist, gibt subcollection("reviews") alle Dokumente aus der Sammlung /restaurants/pizza-place/reviews zurück.
Wenn die Dokumentreferenz umbenannt wurde oder es nicht möglich ist, ein Feld mit __name__ zu definieren, schreiben Sie den Join manuell, z. B. so:
Node.js
db.pipeline()
.collection("/restaurants")
.let(field("__name__").as("restaurant_name"))
.add_fields(db.pipeline()
.collectionGroup("reviews")
.where(field("__name__").parent().equals(variable("restaurant_name")))
.aggregate(average("rating").as("avg_rating"))
.toScalarExpression()
.as("avg_rating"))
ist weiterhin möglich, da diese Phase im Grunde nur syntaktischer Zucker für dieses komplexere Join-Format ist.
Für die folgenden Dokumente:
Node.js
const restaurant1 = db.collection("restaurants").document("pizza-place");
const restaurant2 = db.collection("restaurants").document("urban-bite");
const restaurant3 = db.collection("restaurants").document("nacho-house");
await restaurant1.create({ name: "Pizza Place" });
await restaurant2.create({ name: "Urban Bite" });
await restaurant3.create({ name: "Nacho House" });
await restaurant1.collection("reviews").doc("1").create({ rating: 5 });
await restaurant1.collection("reviews").doc("1").create({ rating: 2 });
await restaurant2.collection("reviews").doc("1").create({ rating: 3 });
await restaurant2.collection("reviews").doc("1").create({ rating: 4 });
await restaurant2.collection("reviews").doc("1").create({ rating: 5 });
Mit der folgenden Abfrage wird jedes Restaurant abgerufen und seine Rezensionen werden in einem neuen Feld review_summary zusammengefasst:
Node.js
const results = await db.pipeline()
.collectionGroup("restaurants")
.add_fields(subcollection("reviews")
.aggregate(
countAll().as("review_count"),
average("rating").as("avg_rating"))
.asScalarExpression()
.as("review_summary"))
.execute();
und liefert die folgenden Ergebnisse:
{ name: "Pizza Place", review_summary: { review_count: 2, avg_rating: 3.5 } },
{ name: "Urban Bite", review_summary: { review_count: 3, avg_rating: 4.0 } },
{ name: "Nacho House", review_summary: { review_count: 0, avg_rating: null } },