Sous-collection

Description

L'étape d'entrée subcollection(...) permet d'effectuer facilement des jointures parent-enfant à l'aide du champ __name__ intégré.

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 les 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. Il utilise le __name__ (référence du document) du document actuel dans le champ parent pour déterminer la sous-collection à récupérer. Par exemple, si le document parent est /restaurants/pizza-place, subcollection("reviews") renvoie tous les documents de la collection /restaurants/pizza-place/reviews.

Si la référence du document a été renommée ou s'il n'est pas possible de définir un champ avec __name__, écrivez 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"))

est toujours possible, car cette étape n'est fondamentalement qu'un sucre syntaxique par rapport à 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 } },