Description
L'étape d'entrée subcollection(...) facilite l'exécution de jointures parent-enfant à l'aide du champ intégré __name__.
Des étapes supplémentaires peuvent être enchaînées à l'étape subcollection(...) pour effectuer un filtrage ou une agrégation sur les documents imbriqués. Notez que toutes les références de champ utilisées dans les étapes suivantes font référence aux documents de la collection imbriquée, et non au document parent. Pour faire référence à des champs dans le champ parent,
utilisez d'abord l'étape let(...) pour définir des variables, puis
faites référence à ces variables dans le champ local.
Exemples
Node.js
db.pipeline()
.collection("/restaurants")
.add_fields(subcollection("reviews")
.aggregate(average("rating").as("avg_rating"))
.toScalarExpression()
.as("avg_rating"))
Comportement
L'étape subcollection(...) doit être utilisée dans le contexte d'une sous-requête. Elle utilise le __name__ (la référence de document) du document actuel dans le champ parent pour déterminer la sous-collection à extraire. Par
exemple, si le document parent est /restaurants/pizza-place, alors
subcollection("reviews") renvoie tous les documents de la
/restaurants/pizza-place/reviews collection.
Si la référence de document a été renommée ou s'il n'est pas possible de définir un champ avec __name__, vous pouvez toujours écrire manuellement la jointure comme suit :
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"))
En effet, cette étape n'est qu'une simplification syntaxique de ce format de jointure plus complexe.
Pour les documents suivants :
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 });
La requête suivante récupère chaque restaurant et résume ses avis dans un nouveau champ review_summary :
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();
et produit les résultats suivants :
{ 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 } },