在 MongoDB Atlas 中聚合数据 — 函数
Overview
本页上的示例演示了如何在函数中使用 MongoDB 查询 API 来聚合 Atlas 集群中的文档。
MongoDB 聚合管道通过一系列数据聚合阶段运行集合中的所有文档,这些阶段允许您对文档进行过滤和调整,以及收集有关相关文档组的摘要数据。
数据模型
此页面上的示例使用名为 store.purchases
的集合,其中包含有关在线商店中的历史商品销售的信息。每个文档包含已购买的 items
的列表,其中包括每一商品的 name
和所购 quantity
以及已购买这些商品的客户的唯一 ID 值。
{ "title": "Purchase", "required": ["_id", "customerId", "items"], "properties": { "_id": { "bsonType": "objectId" }, "customerId": { "bsonType": "objectId" }, "items": { "bsonType": "array", "items": { "bsonType": "object", "required": ["name", "quantity"], "properties": { "name": { "bsonType": "string" }, "quantity": { "bsonType": "int" } } } } } }
片段设置
要在函数中使用代码片段,您必须先实例化一个 MongoDB 集合句柄:
exports = function() { const mongodb = context.services.get("mongodb-atlas"); const itemsCollection = mongodb.db("store").collection("items"); const purchasesCollection = mongodb.db("store").collection("purchases"); // ... paste snippet here ... }
运行聚合管道
可以使用 collection.aggregate()
方法执行聚合管道。
以下函数代码片段按 purchases
集合中的所有文档的 customerId
值对其进行分组,并汇总每个客户购买的商品数量以及他们进行的购买总数。对文档进行分组后,管道将一个新字段添加到每个客户的文档中,该字段计算每个客户一次购买的平均商品数量 averageNumItemsPurchased
:
const pipeline = [ { "$group": { "_id": "$customerId", "numPurchases": { "$sum": 1 }, "numItemsPurchased": { "$sum": { "$size": "$items" } } } }, { "$addFields": { "averageNumItemsPurchased": { "$divide": ["$numItemsPurchased", "$numPurchases"] } } } ] return purchasesCollection.aggregate(pipeline).toArray() .then(customers => { console.log(`Successfully grouped purchases for ${customers.length} customers.`) for(const customer of customers) { console.log(`customer: ${customer._id}`) console.log(`num purchases: ${customer.numPurchases}`) console.log(`total items purchased: ${customer.numItemsPurchased}`) console.log(`average items per purchase: ${customer.averageNumItemsPurchased}`) } return customers }) .catch(err => console.error(`Failed to group purchases by customer: ${err}`))
使用 Atlas Search 查找数据
您可以使用 和collection.aggregate()
聚合阶段在集合上运行 Atlas Search$search
查询。
重要
App Services 以系统用户身份执行 $search
操作,并对返回的搜索结果强制执行字段级规则。这意味着用户可以搜索他们没有读取权限的字段。在这种情况下,搜索基于指定字段,但返回的文档不包括该字段。
exports = async function searchMoviesAboutBaseball() { // 1. Get a reference to the collection you want to search. const movies = context.services .get("mongodb-atlas") .db("sample_mflix") .collection("movies"); // 2. Run an aggregation with $search as the first stage. const baseballMovies = await movies .aggregate([ { $search: { text: { query: "baseball", path: "plot", }, }, }, { $limit: 5, }, { $project: { _id: 0, title: 1, plot: 1, }, }, ]) .toArray(); return baseballMovies; };
{ "plot" : "A trio of guys try and make up for missed opportunities in childhood by forming a three-player baseball team to compete against standard children baseball squads.", "title" : "The Benchwarmers" } { "plot" : "A young boy is bequeathed the ownership of a professional baseball team.", "title" : "Little Big League" } { "plot" : "A trained chimpanzee plays third base for a minor-league baseball team.", "title" : "Ed" } { "plot" : "The story of the life and career of the famed baseball player, Lou Gehrig.", "title" : "The Pride of the Yankees" } { "plot" : "Babe Ruth becomes a baseball legend but is unheroic to those who know him.", "title" : "The Babe" }
注意
$$SEARCH_META 变量可用性
$$SEARCH_META聚合变量仅适用于作为系统运行的函数,或者搜索集合上的第一个角色将其apply_when
和read
表达式设立为true
的函数。
如果这两种情况都不存在,则 $$SEARCH_META
未定义,聚合将失败。
聚合阶段
筛选文档
您可以使用$match阶段,使用标准MongoDB查询语法过滤传入文档。
{ "$match": { "<Field Name>": <Query Expression>, ... } }
例子
以下 $match
阶段筛选传入文档,以便仅包含 graduation_year
字段的值介于 2019
至2024
(含)之间的文档。
{ "$match": { "graduation_year": { "$gte": 2019, "$lte": 2024 }, } }
对文档分组
您可以使用$ 群组阶段聚合一个或多个文档群组的摘要数据。 MongoDB根据_id
表达式对文档进行分组。
注意
可以通过在字段名称前添加 $
前缀来引用特定文档字段。
{ "$group": { "_id": <Group By Expression>, "<Field Name>": <Aggregation Expression>, ... } }
例子
以下 $group
阶段按文档的 customerId
字段的值对文档进行分组,并计算每个 customerId
出现的采购文档的数量。
{ "$group": { "_id": "$customerId", "numPurchases": { "$sum": 1 } } }
项目文档字段
您可以使用$ 项目阶段包含或省略文档中的特定字段,或使用聚合操作符计算新字段。 要包含字段,请将其值设立为1
。 要省略字段,请将其值设立为0
。
注意
不能同时省略和包含 _id
以外的字段。如果显式包含 _id
以外的字段,则未显式包含的任何字段都将被自动省略(反之亦然)。
{ "$project": { "<Field Name>": <0 | 1 | Expression>, ... } }
例子
以下 $project
阶段省略了 _id
字段,包括 customerId
字段并创建了一个名为 numItems
的新字段,其值是 items
数组中的文档数:
{ "$project": { "_id": 0, "customerId": 1, "numItems": { "$sum": { "$size": "$items" } } } }
向文档添加字段
您可以在$addFields阶段通过聚合操作符添加具有计算值的新字段。
注意
$addFields
与 $project 类似,但不允许包含或省略字段。
例子
以下 $addFields
阶段创建了一个名为 numItems
的新字段,其值是 items
数组中的文档数:
{ "$addFields": { "numItems": { "$sum": { "$size": "$items" } } } }
Unwind Array Values
您可以使用 $unwind 阶段来聚合数组字段的各个元素。当您展开数组字段时,MongoDB 会为该数组字段的每个元素复制每个文档一次,但同时还会在每个副本中用数组元素替换数组值。
{ $unwind: { path: <Array Field Path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
例子
下面的 $unwind
阶段为每个文档中 items
数组的每个元素创建一个新文档。该阶段还会在每个新文档中添加一个名为 itemIndex
的字段,指定元素在原始数组中的位置索引:
{ "$unwind": { "path": "$items", "includeArrayIndex": "itemIndex" } }
请考虑 purchases
集合中的以下文档:
{ _id: 123, customerId: 24601, items: [ { name: "Baseball", quantity: 5 }, { name: "Baseball Mitt", quantity: 1 }, { name: "Baseball Bat", quantity: 1 }, ] }
如果我们将示例 $unwind
阶段应用于此文档,该阶段将输出以下三个文档:
{ _id: 123, customerId: 24601, itemIndex: 0, items: { name: "Baseball", quantity: 5 } }, { _id: 123, customerId: 24601, itemIndex: 1, items: { name: "Baseball Mitt", quantity: 1 } }, { _id: 123, customerId: 24601, itemIndex: 2, items: { name: "Baseball Bat", quantity: 1 } }