标签:lin spl 限制行 enc 查询条件 数据传递 var eth 处理过程
mongodb 的聚合框架是基于数据处理管道的概念建模的,文档通过一个多阶段管道处理,转换为聚合结果
聚合管道使用本地操作实现了高效的数据聚合操作,是mongodb首选的数据聚合方法
聚合管道可以对分片集合进行操作
在聚合管道的某些阶段,可以使用索引来提高性能。此外,聚合管道有一个内部优化阶段
聚合管道由多个阶段(stage)组成,每个阶段都会对输入文档进行处理转换。管道阶段不需要为每个输入文档生成一个输出文档,因为有些阶段会生成新的文档或过滤掉文档
管道阶段可以在管道中出现多次,但 $out、$merge、$geoNear 阶段只能出现一次
计算集合或视图中数据的聚合结果
游标
聚合返回的游标只支持对已计算的游标进行操作的方法
https://docs.mongodb.com/v4.2/reference/method/db.collection.aggregate/#cursor-behavior
Cursors returned from aggregation only supports cursor methods that operate on evaluated cursors (i.e. cursors whose first batch has been retrieved)
cursor.hasNext()
cursor.next()
cursor.toArray()
cursor.forEach()
cursor.map()
cursor.objsLeftInBatch()
cursor.itcount()
cursor.pretty()
在 mongo shell 中,如果 aggregate()
方法返回的游标没有使用 var 关键字分配给一个变量,那么 mongo shell 将自动迭代游标20次
会话
getMore
getMore
db.collection.aggregate(pipeline, options)
pipeline
options
类型:Document
描述:aggregate() 方法传递给 aggregate 命令的额外选项,仅当pipeline为数组时可用
explain
allowDiskUse
类型:布尔
描述:是否允许使用临时文件
true
聚合操作可以将数据写入临时文件,位于 dbPath
目录中的子目录_tmp
但 $graphLookup、$addToSet、$push 阶段除外
cursor
maxTimeMS
db.killOp()
方法相同的机制终止超时的操作。mongodb 只在一个指定的中断点终止一个操作bypassDocumentValidation
aggregate()
方法绕过文档数据校验,允许管道处理阶段插入不满足数据校验的文档readConcern
类型:Document
描述
指定读取策略
格式
readConcern: { level : <value> }
collation
hint
comment
可通过 database profiler、currentOp、logs 追踪
writeConcern
文档
db.orders.insertMany([
{ _id: 1, cust_id: "abc1", ord_date: ISODate("2012-11-02T17:04:11.102Z"), status: "A", amount: 50 },
{ _id: 2, cust_id: "xyz1", ord_date: ISODate("2013-10-01T17:04:11.102Z"), status: "A", amount: 100 },
{ _id: 3, cust_id: "xyz1", ord_date: ISODate("2013-10-12T17:04:11.102Z"), status: "D", amount: 25 },
{ _id: 4, cust_id: "xyz1", ord_date: ISODate("2013-10-11T17:04:11.102Z"), status: "D", amount: 125 },
{ _id: 5, cust_id: "abc1", ord_date: ISODate("2013-11-12T17:04:11.102Z"), status: "A", amount: 25 }
])
group and sum
> var res = db.orders.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } }
])
> res
{ "_id" : "xyz1", "total" : 100 }
{ "_id" : "abc1", "total" : 75 }
显示聚合管道执行计划的详细信息
db.orders.explain().aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } }
])
使用外部存储处理大数据集
var results = db.stocks.aggregate(
[
{ $project : { cusip: 1, date: 1, price: 1, _id: 0 } },
{ $sort : { cusip : 1, date: 1 } }
],
{
allowDiskUse: true
}
)
指定聚合操作使用的索引
db.foodColl.createIndex( { qty: 1, type: 1 } );
db.foodColl.createIndex( { qty: 1, category: 1 } );
db.foodColl.aggregate(
[ { $sort: { qty: 1 }}, { $match: { category: "cake", qty: 10 } }, { $sort: { type: -1 } } ],
{ hint: { qty: 1, category: 1 } }
)
管道表达式
https://docs.mongodb.com/v4.2/core/aggregation-pipeline/#pipeline-expressions
https://docs.mongodb.com/v4.2/tutorial/update-documents-with-aggregation-pipeline/
聚合管道对值类型和结果大小有一些限制
结果大小限制
aggregate 命令
聚合命令可以返回游标,也可以将结果存储在集合中,结果集中的每个文档都受BSON文档大小限制,当前为 16MB,如果某个文档大小超过BSON大小限制,则聚合命令将产生错误
- 仅适用于结果集中返回的文档,管道中的文档不受此限制
- MongoDB 3.6删除了聚合命令以单个文档的形式返回结果的选项
aggregate() 方法
内存大小限制
聚合命令对单个集合进行操作,逻辑上将整个集合传递给聚合管道,为了优化操作,应尽可能比表面扫描整个集合
利用索引
mongodb的查询规划器(query planner)分析聚合管道,以确定是否可以使用索引来提高某些阶段的性能
预先过滤
当聚合操作只需针对集合中数据的一个子集,则在管道开头使用 $match、$limit、$skip 等阶段,限制输入文档的数量
在管道开头使用 $match 和 $sort 阶段,逻辑上相当于一个带有排序的查询,并且可以使用索引,如果可能,尽量在管道开头使用 $match 阶段
按指定字段对集合中的文档进行分组,每组输出一个文档,输出文档的_id
字段包含唯一值
类似 sql 中的 GROUP BY
输出文档中还可以添加自定义字段,用于显示累加器表达式值
如果文档不包含分组字段,则忽略该文档
区别于包含分组字段,但是值为null
格式
db.collection.aggregate([
{
$group:
{
_id: <expression>,
<field>: { <accumulator> : <expression> },
...
}
}
])
_id
"$<分组字段>"
或 操作符表达式)field
<accumulator>
描述:累加器(聚合)操作符
常用聚合操作符
操作符 | 描述 |
---|---|
$sum | 利用 $group 分组后,对同组内的文档,对指定字段的数值进行求和 |
$avg | 利用 $group 分组后,对同组内的文档,对指定字段的数值求平均值 |
$first | 利用 $group 分组后,对同组内的文档,显示指定字段的第一个值 |
$last | 利用 $group 分组后,对同组内的文档,显示指定字段的最后一个值 |
$max | 利用 $group 分组后,对同组内的文档,显示指定字段的最大值 |
$min | 利用 $group 分组后,对同组内的文档,显示指定字段的最小值 |
$push | 利用 $group 分组后,对同组内的文档,以数组的方式显示指定字段 |
$addToSet | 利用 $group 分组后,对同组内的文档,以数组的方式显示字段不重复的值 |
<expression>
"$<计算的字段>"
或 操作符表达式)示例
文档
db.sales.insertMany([
{ "_id" : 1, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("2"), "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : NumberDecimal("20"), "quantity" : NumberInt("1"), "date" : ISODate("2014-03-01T09:00:00Z") },
{ "_id" : 3, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },
{ "_id" : 4, "item" : "xyz", "price" : NumberDecimal("5"), "quantity" : NumberInt("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },
{ "_id" : 5, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },
{ "_id" : 6, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") },
{ "_id" : 7, "item" : "def", "price" : NumberDecimal("7.5"), "quantity": NumberInt("10") , "date" : ISODate("2015-09-10T08:43:00Z") },
{ "_id" : 8, "item" : "abc", "price" : NumberDecimal("10"), "quantity" : NumberInt("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") },
])
计算集合中文档的数量
db.sales.aggregate( [
{
$group: {
_id: null,
count: { $sum: 1 }
}
}
] )
返回
{ "_id" : null, "count" : 8 }
_id
设为 null 或 常量
db.sales.aggregate( [
{
$group: {
_id: 404,
count: { $sum: 1 }
}
}
] )
返回
{ "_id" : 404, "count" : 8 }
检索某个字段的不同值
db.sales.aggregate( [ { $group : { _id : "$item" } } ] )
having
db.sales.aggregate(
[
// First Stage
{
$group :
{
_id : "$item",
totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }
}
},
// Second Stage
{
$match: { "totalSaleAmount": { $gte: 100 } }
}
]
)
多聚合操作符
db.sales.aggregate([
// First Stage
{
$match : { "date": { $gte: new ISODate("2014-01-01"), $lt: new ISODate("2015-01-01") } }
},
// Second Stage
{
$group : {
// 多个 key-value 键值对
_id : { $dateToString: { format: "%Y-%m-%d", date: "$date" } },
totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } },
averageQuantity: { $avg: "$quantity" },
count: { $sum: 1 }
}
},
// Third Stage
{
$sort : { totalSaleAmount: -1 }
}
])
显示并传递指定字段,包括文档中的现有字段或新计算的字段(新增)
对于嵌入式文档中的字段,可以通过 点表示法 或 嵌套字段表示法
"contact.address.country": <1 or 0 or expression>
contact: { address: { country: <1 or 0 or expression> } }
如果 $project 指定一个空文档则报错
格式
{ $project: { <specification(s)> } }
specifications
_id : <0 or false>
_id
字段<field> : <0 or 1>
1:包含指定字段
_id
字段默认显示和传递,其他字段默认不显示并排除0:排除指定字段
如果有条件的排除,需使用 REMOVE 变量
如果排除_id
以外的所有字段,则不能使用任何其他的规范表单
if you exclude fields, you cannot also specify the inclusion of fields, reset the value of existing fields, or add new fields.
除_id
字段外,其他字段默认不显示,但如果显式排除了某个字段,则其他所有字段将显示并传递
<field> : <expression>
常用操作符
示例
排除嵌入式文档中的指定字段
文档
db.books.insert({
"_id" : 1,
title: "abc123",
isbn: "0001122223334",
author: { last: "zzz", first: "aaa" },
copies: 5,
lastModified: "2016-07-28"
})
排除
db.books.aggregate( [ { $project : { "author.first" : 0, "lastModified" : 0 } } ] )
db.bookmarks.aggregate( [ { $project: { "author": { "first": 0}, "lastModified" : 0 } } ] )
包含嵌入式文档中的指定字段
文档
db.bookmarks.insertMany([
{ _id: 1, user: "1234", stop: { title: "book1", author: "xyz", page: 32 } },
{ _id: 2, user: "7890", stop: [ { title: "book2", author: "abc", page: 5 }, { title: "book3", author: "ijk", page: 100 } ] }
])
包含
db.bookmarks.aggregate( [ { $project: { "stop.title": 1 } } ] )
db.bookmarks.aggregate( [ { $project: { stop: { title: 1 } } } ] )
结果
{ "_id" : 1, "stop" : { "title" : "book1" } }
{ "_id" : 2, "stop" : [ { "title" : "book2" }, { "title" : "book3" } ] }
多个值将自动以数组的形式返回
新增字段
db.books.aggregate(
[
{
$project: {
title: 1,
isbn: {
prefix: { $substr: [ "$isbn", 0, 3 ] },
group: { $substr: [ "$isbn", 3, 2 ] }
},
lastName: "$author.last",
copiesSold: "$copies"
}
}
]
)
结果
{
"_id" : 1,
"title" : "abc123",
"isbn" : {
"prefix" : "000",
"group" : "11"
},
"lastName" : "zzz",
"copiesSold" : 5
}
新增数组
文档
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "x" : 1, "y" : 1 }
新增
db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y", "$someField" ] } } ] )
结果
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1, null ] }
如果数组中引用了不存在的字段,则用 null 代替
格式
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
优化
当 $sort 在 $limit 之前且中间没有改变文档数量的操作时,优化器可以将 $limit 合并到 $sort 中,即 $sort 操作在进行过程中只维护顶部的 n 个结果(n 为 $limit 限定的值),mongodb 只在内存中存储 n 个文档
$sort 阶段只能占用 100 MB 的内存,默认超过则报错。为了处理大型数据集,设置 allowDiskUse 为 true,允许 $sort 操作将数据写入临时文件
如果管道前面没有 $project、$unwind、$group 阶段,则$sort 可利用索引进行排序
格式
{ $skip: <positive integer> }
格式
{ $limit: <positive integer> }
find()
、findOne()
那样利用索引格式
{ $match: { <query> } }
限制
$match 查询语法与读取操作查询语法相同;例如:$match不接受原始聚合表达式。要在$match中包含聚合表达式,请使用$expr查询表达式
{ $match: { $expr: { <aggregation expression> } } }
要在 $match 中使用 $text,则必须作为管道的第一阶段
示例
相等匹配
db.articles.aggregate(
[ { $match : { author : "dave" } } ]
);
条件查询
db.articles.aggregate( [
{ $match: { $or: [ { score: { $gt: 70, $lt: 90 } }, { views: { $gte: 1000 } } ] } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
$match 选择分数大于70小于90的文档,或者视图大于等于1000的文档,这些文档通过管道传输给 $group 阶段
$group 统计文档数量
结果
{ "_id" : null, "count" : 5 }
对同一数据库中未分片的集合进行左外连接,用于查找当前集合中与另一集合条件匹配的文档
相当于关系数据库中的左外联查询
左外联:返回包括左表中的所有记录和右表中符合查询条件的记录
右外联:返回包括右表中的所有记录和左表中符合查询条件的记录
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
from
localField
foreignField
as
示例
localField为单一值
文档
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
])
db.inventory.insert([
{ "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 },
{ "_id" : 5, "sku": null, description: "Incomplete" },
{ "_id" : 6 }
])
通过 orders 集合中的 item 字段和 inventory 集合汇总的 sku 字段,将两个集合连接起来
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
查询 orders 集合中 item 字段的值与 inventory 集合中 sku 字段的值 相等的 文档
输出文档中的 inventory_docs 字段包含 inventory 集合中符合条件的文档
结果
{
"_id" : 1,
"item" : "almonds",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "pecans",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}
orders 集合中的
{ "_id" : 3 }
文档不包含 item 字段,则$lookup将其视为 null 匹配 inventory 集合中的{ "_id" : 5, "sku" : null, "description" : "Incomplete" }
和{ "_id" : 6 }
两个文档
localFIeld 为数组
文档
db.classes.insert( [
{ _id: 1, title: "Reading is ...", enrollmentlist: [ "giraffe2", "pandabear", "artie" ], days: ["M", "W", "F"] },
{ _id: 2, title: "But Writing ...", enrollmentlist: [ "giraffe1", "artie" ], days: ["T", "F"] }
])
db.members.insert( [
{ _id: 1, name: "artie", joined: new Date("2016-05-01"), status: "A" },
{ _id: 2, name: "giraffe", joined: new Date("2017-05-01"), status: "D" },
{ _id: 3, name: "giraffe1", joined: new Date("2017-10-01"), status: "A" },
{ _id: 4, name: "panda", joined: new Date("2018-10-11"), status: "A" },
{ _id: 5, name: "pandabear", joined: new Date("2018-12-01"), status: "A" },
{ _id: 6, name: "giraffe2", joined: new Date("2018-12-01"), status: "D" }
])
通过 classes 集合中的 enrollmentlist 字段 和 members 集合中的 name 字段,将两个集合连接起来
db.classes.aggregate([
{
$lookup:
{
from: "members",
localField: "enrollmentlist",
foreignField: "name",
as: "enrollee_info"
}
}
])
查询 classes 集合中 enrollmentlist 数组中的元素跟 members 集合中 name 字段值相等的文档
输出文档中的 enrollee_info 字段包含 members 集合中符合条件的文档
结果
{
"_id" : 1,
"title" : "Reading is ...",
"enrollmentlist" : [ "giraffe2", "pandabear", "artie" ],
"days" : [ "M", "W", "F" ],
"enrollee_info" : [
{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 5, "name" : "pandabear", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "A" },
{ "_id" : 6, "name" : "giraffe2", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "D" }
]
}
{
"_id" : 2,
"title" : "But Writing ...",
"enrollmentlist" : [ "giraffe1", "artie" ],
"days" : [ "T", "F" ],
"enrollee_info" : [
{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },
{ "_id" : 3, "name" : "giraffe1", "joined" : ISODate("2017-10-01T00:00:00Z"), "status" : "A" }
]
}
{
$lookup:
{
from: <collection to join>,
let: { <var_1>: <expression>, …, <var_n>: <expression> },
pipeline: [ <pipeline to execute on the collection to join> ],
as: <output array field>
}
}
from
left
let : { <引用变量名> : "$<输入文档中的字段>" }
pipeline
类型:数组
指定要在 from 集合上运行的管道,用以筛选符合条件的文档
如要返回所有的文档,则指定一个不含任何阶段的空管道
[ ]
pipeline 管道中不能包含 $out 和 $merge 阶段
pipeline 管道阶段 能 直接访问 from 集合中的文档字段,通过"$<from集合中的文档字段>"
pipeline 管道阶段 不能 直接访问输入文档中的字段,必须首先在 let 子句中定义中间变量,然后才能在 pipeline 管道的各个阶段中引用
$$<variable>
的形式引用变量as
示例
多条件查询
文档
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
])
db.warehouses.insert([
{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120 },
{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80 },
{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60 },
{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40 },
{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80 }
])
通过 item字段 以及 条件(库存数量是否满足订单数量),将orders集合和warehouses集合连接起来
db.orders.aggregate([
{
$lookup:
{
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] }
]
}
}
},
{ $project: { stock_item: 0, _id: 0 } }
],
as: "stockdata"
}
}
])
根据 orders 集合中的 name 字段,查询 warehouses 集合中库存数量满足订单数量的文档
输出文档中的 stockdata 字段包含符合条件的 warehouses 集合中的文档
结果
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2,
"stockdata" : [ { "warehouse" : "A", "instock" : 120 }, { "warehouse" : "B", "instock" : 60 } ] }
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1,
"stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60,
"stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }
不相关子查询
子查询或内部查询
- 嵌套在其它查询中的查询
主查询或外部查询
- 包含子查询的查询
不相关子查询
- 内部查询的执行独立于外部查询,内部查询只执行一次,然后将结果作为外部查询的条件
相关子查询
- 内部查询的执行依赖于外部查询的数据,外部查询每执行一次,内部查询也会执行一次。
- 每次都是外部查询先执行,将当前查询数据传递给内部查询,然后执行内部查询,根据内部查询的执行结果判断当前数据是否满足外部查询的where条件,若满足则当前数据是符合要求的记录
- 外部查询依次扫描每条记录,重复执行上述过程
https://blog.csdn.net/qiushisoftware/article/details/80874463
格式
{ $count: <string> }
$
开头,不能包含点.
字符示例
$count 行为等价于 $group + $project
db.collection.aggregate( [
{ $group: { _id: null, myCount: { $sum: 1 } } },
{ $project: { _id: 0 } }
] )
db.collection.aggregate([
{
$count:"myCount"
}
])
格式
{ $unwind: <field path> }
{
$unwind:
{
path: <field path>,
includeArrayIndex: <string>,
preserveNullAndEmptyArrays: <boolean>
}
}
path
includeArrayIndex
$
开头preserveNullAndEmptyArrays
preserve 保留
类型:布尔
描述:如果文档不包含 path 指定的字段,或字段值为null,或字段值为空数组[ ]
true
$unwind 原样输出该文档
false【默认】
$unwind 不输出该文档
示例
preserveNullAndEmptyArrays 默认false
db.inventory2.insertMany([
{ "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },
{ "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },
{ "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },
{ "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },
{ "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }
])
展开
db.inventory2.aggregate( [ { $unwind: "$sizes" } ] )
db.inventory2.aggregate( [ { $unwind: { path: "$sizes" } } ] )
两种语法效果一样
结果
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }
{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
记录索引,输出文档
db.inventory2.aggregate( [
{
$unwind:
{
path: "$sizes",
includeArrayIndex: "arrayIndex",
preserveNullAndEmptyArrays: true
}
}])
结果
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }
{ "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") }
{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
{ "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") }
{ "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }
解析嵌套数组
{
_id: "1",
"items" : [
{
"name" : "pens",
"tags" : [ "writing", "office", "school", "stationary" ],
"price" : NumberDecimal("12.00"),
"quantity" : NumberInt("5")
},
{
"name" : "envelopes",
"tags" : [ "stationary", "office" ],
"price" : NumberDecimal("1.95"),
"quantity" : NumberInt("8")
}
]
}
db.sales.aggregate([
// First Stage
{ $unwind: "$items" },
// Second Stage
{ $unwind: "$items.tags" }
])
?
将聚合操作的结果写入指定的集合
$out 必须是管道的最后一个阶段
不能写入固定集合中
如果指定的集合不存在,则在完成聚合操作后,会创建该集合
在聚合操作完成之前,该集合是不可见的。如果聚合操作失败,则不会创建
如果指定的集合已经存在,则在完成聚合操作后,会用聚合操作结果覆盖原有数据
$out 操作符不会改变原集合上建立的索引,如果聚合操作的结果文档违反任一唯一索引(包括原集合_id
字段上建立的索引),则写入失败
格式
{ $out: "<output-collection>" }
示例
db.books.insert([
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])
聚合
db.books.aggregate( [
{ $group : { _id : "$author", books: { $push: "$title" } } },
{ $out : "authors" }
] )
结果在当前数据库中新增 authors 集合
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
{ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
格式
{ $addFields: { <newField>: <expression>, ... } }
格式
{ $push: <expression> }
格式
{ $addToSet: <expression> }
"$<field>"
,则以数组的形式返回该字段不重复的值利用 $group 分组后,对同组内的文档,对指定字段的数值进行求和
{$sum:1}
表示计算文档数量总和,管道中的每个文档代表数值1
在 $group 阶段,分组过程中,计算每组文档的数量
3.2 版本及之前,仅用于 $group 阶段
格式
$group 阶段
{ $sum: <expression> }
其他阶段
{ $sum: <expression> }
{ $sum: [ <expression1>, <expression2> ... ] }
非数字或不存在字段
Example | Field Values | Results |
---|---|---|
{ $sum : <field> } |
Numeric |
Sum of Values |
{ $sum : <field> } |
Numeric and Non-Numeric |
Sum of Numeric Values |
{ $sum : <field> } |
Non-Numeric or Non-Existent |
0 |
数组字段
利用 $group 分组后,对同组内的文档,对指定字段的数值求平均值
3.2 版本及之前,仅用于 $group 阶段
格式
$group 阶段
{ $avg: <expression> }
其他阶段
{ $avg: <expression> }
{ $avg: [ <expression1>, <expression2> ... ] }
利用 $group 分组后,对同组内的文档,返回 指定字段 的第一个值
一般在文档排序后使用才有意义
当在 $group 阶段中使用$first时,$group 阶段应该在 $sort 阶段之后,以使输入文档按照已定义的顺序
尽管 $sort 阶段将有序的文档作为输入传递到 $group 阶段,但 $group 不能保证在其自己的输出中维护这种排序顺序。
只能应用于 $group 阶段
格式
{ $first: <expression> }
利用 $group 分组后,对同组内的文档,返回 指定字段 的最后一个值
一般在文档排序后使用才有意义
当在 $group 阶段中使用$first时,$group 阶段应该在 $sort 阶段之后,以使输入文档按照已定义的顺序
只能应用于 $group 阶段
格式
{ $last: <expression> }
格式
$group 阶段
{ $max: <expression> }
其他阶段
{ $max: <expression> }
{ $max: [ <expression1>, <expression2> ... ] }
格式
$group 阶段
{ $min: <expression> }
其他阶段
{ $min: <expression> }
{ $min: [ <expression1>, <expression2> ... ] }
格式
$switch: {
branches: [
{ case: <expression>, then: <expression> },
{ case: <expression>, then: <expression> },
...
],
default: <expression>
}
branches
类型:文档数组
描述:
控制分支,每个分支都必须具有 case 和 then 字段
case
值为可以解析为布尔值的任何有效的 表达式,如果不是则强制转换为布尔值
then
值为任何有效的表达式
必须至少包含一个条件分支
default【可选】
示例
文档
db.grades.insert([
{ "_id" : 1, "name" : "Susan Wilkes", "scores" : [ 87, 86, 78 ] },
{ "_id" : 2, "name" : "Bob Hanna", "scores" : [ 71, 64, 81 ] },
{ "_id" : 3, "name" : "James Torrelio", "scores" : [ 91, 84, 97 ] }
])
聚合
db.grades.aggregate( [
{
$project:
{
"name" : 1,
"summary" :
{
$switch:
{
branches: [
{
case: { $gte : [ { $avg : "$scores" }, 90 ] },
then: "Doing great!"
},
{
case: { $and : [ { $gte : [ { $avg : "$scores" }, 80 ] },
{ $lt : [ { $avg : "$scores" }, 90 ] } ] },
then: "Doing pretty well."
},
{
case: { $lt : [ { $avg : "$scores" }, 80 ] },
then: "Needs improvement."
}
],
default: "No scores found."
}
}
}
}
] )
结果
{ "_id" : 1, "name" : "Susan Wilkes", "summary" : "Doing pretty well." }
{ "_id" : 2, "name" : "Bob Hanna", "summary" : "Needs improvement." }
{ "_id" : 3, "name" : "James Torrelio", "summary" : "Doing great!" }
格式
{ $size: <expression> }
expression
可以解析为数组的任何有效的表达式
3.4 版本中被废弃,现在是 substrBytes 的别名
格式
{ $substr: [ <string>, <start>, <length> ] }
$<字段名>
查询并返回子字符串在字段中第一次出现位置的索引,如果没有找到则返回 -1
UTF-8 字节索引
格式
{ $indexOfBytes: [ <string expression>, <substring expression>, <start>, <end> ] }
示例
db.inventory.insert([
{ "_id" : 1, "item" : "foo" },
{ "_id" : 2, "item" : "fóofoo" },
{ "_id" : 3, "item" : "the foo bar" },
{ "_id" : 4, "item" : "hello world fóo" },
{ "_id" : 5, "item" : null },
{ "_id" : 6, "amount" : 3 }
])
聚合
db.inventory.aggregate(
[
{
$project:
{
byteLocation: { $indexOfBytes: [ "$item", "foo" ] },
}
}
]
)
结果
{ "_id" : 1, "byteLocation" : "0" }
{ "_id" : 2, "byteLocation" : "4" }
{ "_id" : 3, "byteLocation" : "4" }
{ "_id" : 4, "byteLocation" : "-1" }
{ "_id" : 5, "byteLocation" : null }
{ "_id" : 6, "byteLocation" : null }
注意 fóofoo 索引是4
é
is encoded using two bytes.
每个文档都有一个返回结果
格式
{ $strLenBytes: <string expression> }
示例
文档
{ "_id" : 1, "name" : "apple" }
{ "_id" : 2, "name" : "banana" }
{ "_id" : 3, "name" : "éclair" }
{ "_id" : 4, "name" : "hamburger" }
{ "_id" : 5, "name" : "jalape?o" }
{ "_id" : 6, "name" : "pizza" }
{ "_id" : 7, "name" : "tacos" }
{ "_id" : 8, "name" : "寿司" }
聚合
db.food.aggregate(
[
{
$project: {
"name": 1,
"length": { $strLenBytes: "$name" }
}
}
]
)
结果
{ "_id" : 1, "name" : "apple", "length" : 5 }
{ "_id" : 2, "name" : "banana", "length" : 6 }
{ "_id" : 3, "name" : "éclair", "length" : 7 }
{ "_id" : 4, "name" : "hamburger", "length" : 9 }
{ "_id" : 5, "name" : "jalape?o", "length" : 9 }
{ "_id" : 6, "name" : "pizza", "length" : 5 }
{ "_id" : 7, "name" : "tacos", "length" : 5 }
{ "_id" : 8, "name" : "寿司", "length" : 6 }
对两个字符串执行不区分大小写的比较
仅适用于 ASCII 字符编码
格式
{ $strcasecmp: [ <expression1>, <expression2> ] }
格式
{ $toLower: <expression> }
示例
db.inventory.aggregate(
[
{
$project:
{
item: { $toLower: "$item" },
description: { $toLower: "$description" }
}
}
]
)
将字符串转换为大写字母并返回
仅适用于 ASCII 字符编码
格式
{ $toUpper: <expression> }
格式
{ $concat: [ <expression1>, <expression2>, ... ] }
格式
{ $split: [ <string expression>, <delimiter> ] }
Example | Results |
---|---|
{ $split: [ "June-15-2013", "-" ] } |
[ "June", "15", "2013" ] |
{ $split: [ "banana split", "a" ] } |
[ "b", "n", "n", " split" ] |
{ $split: [ "Hello World", " " ] } |
[ "Hello", "World" ] |
{ $split: [ "astronomical", "astro" ] } |
[ "", "nomical" ] |
{ $split: [ "pea green boat", "owl" ] } |
[ "pea green boat" ] |
{ $split: [ "headphone jack", 7 ] } |
Errors with message: ... |
{ $split: [ "headphone jack", /jack/ ] } |
Errors with message: ... |
"$split requires an expression that evaluates to a string as a second argument, found: regex"
格式
{ $gt: [ <expression1>, <expression2> ] }
格式
{ $gte: [ <expression1>, <expression2> ] }
格式
{ $lt: [ <expression1>, <expression2> ] }
格式
{ $lte: [ <expression1>, <expression2> ] }
格式
{ $add: [ <expression1>, <expression2>, ... ] }
格式
{ $subtract: [ <expression1>, <expression2> ] }
示例
文档
db.sales.insertMany([
{ "_id" : 1, "item" : "abc", "price" : 10, "fee" : 2, "discount" : 5, "date" : ISODate("2014-03-01T08:00:00Z") },
{ "_id" : 2, "item" : "jkl", "price" : 20, "fee" : 1, "discount" : 2, "date" : ISODate("2014-03-01T09:00:00Z") }
])
聚合
db.sales.aggregate( [ { $project: { item: 1, dateDifference: { $subtract: [ "$date", 5 * 60 * 1000 ] } } } ] )
结果
{ "_id" : 1, "item" : "abc", "dateDifference" : ISODate("2014-03-01T07:55:00Z") }
{ "_id" : 2, "item" : "jkl", "dateDifference" : ISODate("2014-03-01T08:55:00Z") }
格式
{ $multiply: [ <expression1>, <expression2>, ... ] }
格式
{ $divide: [ <expression1>, <expression2> ] }
expression
结果可以保留很多位小数
{ "_id" : 1, "item" : "abc", "dateDiff" : NumberDecimal("3.333333333333333333333333333333333") }
格式
{ $mod: [ <expression1>, <expression2> ] }
格式
{ $year: <dateExpression> }
dateExpression【该部分操作符的格式具有相同的 dateExpression 规范】
可以解析为 日期、时间戳、ObjectID 的有效表达式
或具有如下格式的文档
{ date: <dateExpression>, timezone: <tzExpression> }
date
timezone【可选】
示例
Example | Result |
---|---|
{ $year: new Date("2016-01-01") } |
2016 |
{ $year: { date: new Date("Jan 7, 2003") } } |
2003 |
{ $year: { date: new Date("August 14, 2011"), timezone: "America/Chicago" } } |
2011 |
{ $year: ISODate("1998-11-07T00:00:00Z") } |
1998 |
{ $year: { date: ISODate("1998-11-07T00:00:00Z"), timezone: "-0400" } } |
1998 |
{ $year: "March 28, 1976" } |
error |
{ $year: Date("2016-01-01") } |
error |
{ $year: "2009-04-09" } |
error |
示例
{
"_id" : 1,
"date" : ISODate("2014-01-01T08:15:39.736Z")
}
聚合
db.sales.aggregate(
[
{
$project:
{
year: { $year: "$date" },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" },
hour: { $hour: "$date" },
minutes: { $minute: "$date" },
seconds: { $second: "$date" },
milliseconds: { $millisecond: "$date" },
dayOfYear: { $dayOfYear: "$date" },
dayOfWeek: { $dayOfWeek: "$date" },
week: { $week: "$date" }
}
}
]
)
结果
{
"_id" : 1,
"year" : 2014,
"month" : 1,
"day" : 1,
"hour" : 8,
"minutes" : 15,
"seconds" : 39,
"milliseconds" : 736,
"dayOfYear" : 1,
"dayOfWeek" : 4,
"week" : 0
}
格式
{ $month: <dateExpression> }
示例
Example | Result |
---|---|
{ $month: new Date("2016-01-01") } |
1 |
{ $month: { date: new Date("Nov 7, 2003") } } |
11 |
{ $month: ISODate("2000-01-01T00:00:00Z") } |
1 |
{ $month: { date: new Date("August 14, 2011"), timezone: "America/Chicago" } } |
8 |
{ $month: { date: ISODate("2000-01-01T00:00:00Z"), timezone: "-0500" } } |
12 |
{ $month: "March 28, 1976" } |
error |
{ $month: { date: Date("2016-01-01"), timezone: "-0500" } } |
error |
{ $month: "2009-04-09" } |
error |
格式
{ $week: <dateExpression> }
示例
Example | Result |
---|---|
{ $week: new Date("Jan 1, 2016") } |
0 |
{ $week: { date: new Date("2016-01-04") } } |
1 |
{ $week: { date: new Date("August 14, 2011"), timezone: "America/Chicago" } } |
33 |
{ $week: ISODate("1998-11-01T00:00:00Z") } |
44 |
{ $week: { date: ISODate("1998-11-01T00:00:00Z"), timezone: "-0500" } } |
43 |
{ $week: "March 28, 1976" } |
error |
{ $week: Date("2016-01-01") } |
error |
{ $week: "2009-04-09" } |
error |
NOTE
$hour
cannot take a string as an argument.
格式
{ $hour: <dateExpression> }
格式
{ $minute: <dateExpression> }
格式
{ $second: <dateExpression> }
格式
{ $millisecond: <dateExpression> }
格式
{ $dayOfYear: <dateExpression> }
格式
{ $dayOfMonth: <dateExpression> }
格式
{ $dayOfWeek: <dateExpression> }
是一种数据处理范式(典范模式),将大量数据处理压缩为有用的聚合结果
处理过程
第一阶段——映射(map)
将输入文档中 指定字段 的值保存到一个数组中(value),映射到 分组字段的值(key),以键值对(key-value)的形式输出至 reduce 阶段
第二阶段——归约(reduce)
根据需求对 相同键 的值 做运算,将运算结果作为 文档返回 或 写入集合
map-reduce 使用定制的 JavaScript函数——map 函数,将值(指定字段)映射到一个键(分组字段),如果一个键有多个值,reduce 函数会将其处理压缩为一个对象
支持在分片集合上执行 map-reduce 操作,从 mongodb 4.2 开始
视图不支持 map-reduce 操作
vs 聚合管道
对大多数聚合操作,聚合管道提供更好的性能和更一致的接口,但是 map-reduce 操作提供了一些目前在聚合管道中无法提供的灵活性
map-reduce 的结果文档中只包含新增字段,聚合管道可以通过 $project 控制结果文档中包含原有字段
reduce 函数返回结果文档
{ "_id": "<key>", "value": <执行结果> }
可以在 finalize 函数中添加新字段
db.collection.mapReduce(
<map>, // 类似 $group
<reduce>,
{
out: <collection>, // 类似 $out
query: <document>, // 类似 $match
sort: <document>, // 类似 $sort
limit: <number>, // 类似 $limit
finalize: <function>, // 类似 $project
scope: <document>,
jsMode: <boolean>,
verbose: <boolean>,
bypassDocumentValidation: <boolean>
}
)
map
类型:function
描述:
JavaScript函数,将输入文档中 指定字段的值 保存到一个数组中(value),映射到 分组字段的值(key),输出至 reduce 函数中
作用于每个输入文档,以分组字段的值为键(唯一),以指定字段的值为value,将值作为数组元素映射到键。执行完所有输入文档后,将组成的键值对输出至reduce函数中
相同的键只产生一个键值对
格式
function() {
...
emit(key, value);
}
在 map 函数中,通过this
引用当前文档
map 函数不能访问数据库
map 函数不能对函数外产生影响
可以访问在 scope 参数中定义的全局变量
一个 emit 只能容纳 mongodb 最大 BSON 文档大小的一半(8MB)
map 函数可以多次调用 emit( key, value)
函数,创建键值对
如果 key 已经存在,则将 value 加入key对应的 value数组中
如果 key 不存在,则创建新的 key-value 键值对
根据输入文档字段的值,调用 0 次或1次 emit 函数
function() {
if (this.status == ‘A‘)
emit(this.cust_id, 1);
}
根据输入文档数组字段的元素个数,多次调用 emit 函数
function() {
this.items.forEach(function(item){ emit(item.sku, 1); });
}
reduce
类型:function
描述:JavaScript函数,将与键关联的值数组处理压缩为一个对象
格式
function(key, values) {
...
return result;
}
reduce 函数不能访问数据库
reduce 函数不能对函数外产生影响
values 类型为数组,元素为映射到键的值对象
可以访问在 scope 参数中定义的全局变量
输入 reduce 函数的文档,其大小不能大于mongodb最大BSON文档大小的一半(8MB)
但是reduce函数处理过程中可以超过
可以多次调用 reduce 函数,对于指定的键,前一个reduce函数的输出将作为下一个reduce函数的输入
对同一个键多次调用reduce函数,或需要执行 finalize 函数,此时
reduce 返回值的类型必须与map函数输出值的类型一致,即键值对形式
reduce 函数必须是可结合的,以下声明必须为true
reduce(key, [ C, reduce(key, [ A, B ]) ] ) == reduce( key, [ C, A, B ] )
reduce 函数必须是冪等的,以下声明必须为true
reduce( key, [ reduce(key, valuesArray) ] ) == reduce( key, valuesArray )
reduce 函数应该是可交换的,values 数组中的元素顺序不应该影响输出结果,以下声明为true
reduce( key, [ A, B ] ) == reduce( key, [ B, A ] )
options
out
描述:指定 map-reduce 操作结果的输出形式,可以输出到指定集合中,也可以内联输出
格式
输出到指定集合
out: <collectionName>
指定输出模式
out: { <action>: <collectionName>
[, db: <dbName>]
[, sharded: <boolean> ]
[, nonAtomic: <boolean> ]
}
action
db
指定 map-reduce 输出集合所在的数据库
默认为与输入集合相同的数据库
sharded
nonAtomic
内联输出
out: { inline: 1 }
query
sort
类型:Document
描述
对输入文档进行排序,有利于优化 map-reduce 操作
例如将排序键指定为与 emit 相同的键
指定排序的键必须是集合中已经建立索引的键
在 map 函数之前执行
limit
finalize
类型:function
描述
格式
function(key, reducedValue) {
...
return modifiedObject;
}
scope
jsMode
verbose
collation
bypassDocumentValidation
map-reduce 操作和 $where 操作符表达式不能访问某些全局函数或属性,例如db
以下函数和属性可以访问
Available Properties | Available Functions | ||
---|---|---|---|
args | assert() | isNumber() | print() |
MaxKey | BinData() | isObject() | printjson() |
MinKey | DBPointer() | ISODate() | printjsononeline() |
DBRef() | isString() | sleep() | |
doassert() | Map() | Timestamp() | |
emit() | MD5() | tojson() | |
gc() | NumberInt() | tojsononeline() | |
HexData() | NumberLong() | tojsonObject() | |
hex_md5() | ObjectId() | UUID() | |
version() |
普通文档
计算每个消费者的总价
以 cust_id字段 分组,计算每组中 price字段的总和
文档
db.orders.insertMany([
{ _id: 1, cust_id: "Ant O. Knee", ord_date: new Date("2020-03-01"), price: 25, items: [ { sku: "oranges", qty: 5, price: 2.5 }, { sku: "apples", qty: 5, price: 2.5 } ], status: "A" },
{ _id: 2, cust_id: "Ant O. Knee", ord_date: new Date("2020-03-08"), price: 70, items: [ { sku: "oranges", qty: 8, price: 2.5 }, { sku: "chocolates", qty: 5, price: 10 } ], status: "A" },
{ _id: 3, cust_id: "Busby Bee", ord_date: new Date("2020-03-08"), price: 50, items: [ { sku: "oranges", qty: 10, price: 2.5 }, { sku: "pears", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 4, cust_id: "Busby Bee", ord_date: new Date("2020-03-18"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 5, cust_id: "Busby Bee", ord_date: new Date("2020-03-19"), price: 50, items: [ { sku: "chocolates", qty: 5, price: 10 } ], status: "A"},
{ _id: 6, cust_id: "Cam Elot", ord_date: new Date("2020-03-19"), price: 35, items: [ { sku: "carrots", qty: 10, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 7, cust_id: "Cam Elot", ord_date: new Date("2020-03-20"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 8, cust_id: "Don Quis", ord_date: new Date("2020-03-20"), price: 75, items: [ { sku: "chocolates", qty: 5, price: 10 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 9, cust_id: "Don Quis", ord_date: new Date("2020-03-20"), price: 55, items: [ { sku: "carrots", qty: 5, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 }, { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 10, cust_id: "Don Quis", ord_date: new Date("2020-03-23"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" }
])
map-reduce 操作
定义 map 函数
var mapFunction1 = function() {
emit(this.cust_id, this.price);
};
定义 reduce 函数
接收 map 函数返回的键值对列表,对每个键值对执行 reduce 函数
将键作为_id
字段的值,执行结果作为value
字段的值,以文档 { "_id": "<key>", "value": <执行结果> }
的形式返回
var reduceFunction1 = function(keyCustId, valuesPrices) {
// 具有一些 JS 目前没有的接口
return Array.sum(valuesPrices);
};
对集合中的所有文档执行 map-reduce 操作
db.orders.mapReduce(
mapFunction1,
reduceFunction1,
{ out: "map_reduce_example" }
)
聚合操作
db.orders.aggregate([
{ $group: { _id: "$cust_id", value: { $sum: "$price" } } },
{ $out: "agg_alternative_1" }
])
嵌套文档
计算2020-03-01之后,每个sku(单品)的订单数,总销量,以及每个订单平均销量
以 sku 字段分组,计算每组中 qty 字段的总和,以及每组中
_id
字段不同的文档的数量,最后计算平均销量
map-reduce 操作
定义map函数
var mapFunction2 = function() {
// 遍历数组,对嵌套子文档执行 emit 函数
// 并不是每个元素都创建一个键值对,key已经存在时,只会将 value 放入对应的 value数组
for (var idx = 0; idx < this.items.length; idx++) {
var key = this.items[idx].sku;
var value = { count: 1, qty: this.items[idx].qty };
emit(key, value);
}
};
定义 reduce 函数
var reduceFunction2 = function(keySKU, countObjVals) {
reducedVal = { count: 0, qty: 0 };
for (var idx = 0; idx < countObjVals.length; idx++) {
reducedVal.count += countObjVals[idx].count;
reducedVal.qty += countObjVals[idx].qty;
}
return reducedVal;
};
定义finalize 函数
var finalizeFunction2 = function (key, reducedVal) {
reducedVal.avg = reducedVal.qty/reducedVal.count;
return reducedVal;
};
对集合中的所有文档执行 map-reduce 操作
db.orders.mapReduce(
mapFunction2,
reduceFunction2,
{
out: { merge: "map_reduce_example2" },
query: { ord_date: { $gte: new Date("2020-03-01") } },
finalize: finalizeFunction2
}
);
聚合管道
db.orders.aggregate( [
{ $match: { ord_date: { $gte: new Date("2020-03-01") } } },
{ $unwind: "$items" },
{ $group: { _id: "$items.sku", qty: { $sum: "$items.qty" }, orders_ids: { $addToSet: "$_id" } } },
{ $project: { value: { count: { $size: "$orders_ids" }, qty: "$qty", avg: { $divide: [ "$qty", { $size: "$orders_ids" } ] } } } },
{ $merge: { into: "agg_alternative_3", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
] )
标签:lin spl 限制行 enc 查询条件 数据传递 var eth 处理过程
原文地址:https://www.cnblogs.com/usmile/p/13576805.html