Описание
Этап ввода subcollection(...) упрощает выполнение объединений типа "родитель-потомок" с использованием встроенного поля __name__ .
К этапу subcollection(...) можно добавить дополнительные этапы для выполнения фильтрации или агрегирования вложенных документов. Обратите внимание, что любые ссылки на поля, используемые на последующих этапах, относятся к документам из вложенной коллекции, а не к родительскому документу. Чтобы обратиться к полям в родительской области видимости, сначала используйте этап let(...) для определения переменных, а затем ссылайтесь на эти переменные в локальной области видимости.
Примеры
Node.js
db.pipeline()
.collection("/restaurants")
.add_fields(subcollection("reviews")
.aggregate(average("rating").as("avg_rating"))
.toScalarExpression()
.as("avg_rating"))
Поведение
Этап subcollection(...) должен использоваться в контексте подзапроса. Он использует __name__ (ссылку на документ) текущего документа в родительской области видимости, чтобы определить, какую подколлекцию следует получить. Например, если родительский документ — /restaurants/pizza-place , то subcollection("reviews") возвращает все документы из коллекции /restaurants/pizza-place/reviews .
Если ссылка на документ была переименована или невозможно определить поле с __name__ то следует вручную написать оператор объединения следующим образом:
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"))
Это по-прежнему возможно, поскольку по сути этот этап представляет собой лишь синтаксический сахар поверх более сложного формата объединения.
Для следующих документов:
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 });
Следующий запрос извлекает информацию о каждом ресторане и суммирует отзывы о нем в новом поле 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();
и приводит к следующим результатам:
{ 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 } },