时间:2019/1/15 10:57:27
MongoDB 聚合查询#
MongoDB 查询操作用于从 Collection
中查询出对应的文档信息。
MongoDB 聚合查询用于数据统计和计算,一次聚合查询可以分为多个阶段,上一阶段的结果作为下一阶段的输入。
Pipleline 阶段#
-
$match: 根据条件过滤元素。
1 2 3
db.articles.aggregate( [ { $match : { author : "dave" } } ] );
-
$project:用于控制传递拿些字段,以及增加字段 。
条件控制#
db.books.aggregate( [ { $project: { title: 1, "author.first": 1, "author.last" : 1, "author.middle": { $cond: { if: { $eq: [ "", "$author.middle" ] }, then: "$$REMOVE", else: "$author.middle" } } } } ] )
字段截取#
db.books.aggregate( [ { $project: { title: 1, isbn: { prefix: { $substr: [ "$isbn", 0, 3 ] }, group: { $substr: [ "$isbn", 3, 2 ] }, publisher: { $substr: [ "$isbn", 5, 4 ] }, title: { $substr: [ "$isbn", 9, 3 ] }, checkDigit: { $substr: [ "$isbn", 12, 1] } }, lastName: "$author.last", copiesSold: "$copies" } } ] )
数组#
db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y" ] } } ] )
-
-
$facet:多个阶段联合起来作为输出结果,可以实现对上一阶段的文档进行多维度的聚合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
db.artwork.aggregate( [ { $facet: { "price": [ { $bucket: { groupBy: "$price", boundaries: [ 0, 200, 400 ], default: "Other", output: { "count": { $sum: 1 }, "artwork" : { $push: { "title": "$title", "price": "$price" } } } } } ], "year": [ { $bucket: { groupBy: "$year", boundaries: [ 1890, 1910, 1920, 1940 ], default: "Unknown", output: { "count": { $sum: 1 }, "artwork": { $push: { "title": "$title", "year": "$year" } } } } } ] } } ] )
-
$addFields 添加字段,从现有字段中。
1 2 3 4 5 6 7 8 9 10 11 12
db.scores.aggregate( [ { $addFields: { totalHomework: { $sum: "$homework" } , totalQuiz: { $sum: "$quiz" } } }, { $addFields: { totalScore: { $add: [ "$totalHomework", "$totalQuiz", "$extraCredit" ] } } } ] )
-
$bucket: 把文档按指定的分组条件分成几个桶,并计算每个桶里面有几个元素。
1 2 3 4 5 6 7 8 9 10 11 12 13
db.artwork.aggregate( [ { $bucket: { groupBy: "$price", boundaries: [ 0, 200, 400 ], default: "Other", output: { "count": { $sum: 1 }, "titles" : { $push: "$title" } } } } ] )
-
bucketAuto:自动分桶,并计算桶里面元素的个数。
db.artwork.aggregate( [ { $bucketAuto: { groupBy: "$price", buckets: 4 } } ] ) * $group:根据指定字段分组。
group by null 所有文档作为一个分组#
db.sales.aggregate( [ { $group : { _id : null, totalPrice: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageQuantity: { $avg: "$quantity" }, count: { $sum: 1 } } } ] )
内部支持的操作:
- $addToSet:当前组的元素添加到集合,去重。
- $push:分组的元素放到数组,不去重。
- $avg: 计算当前组里面元素的平均值,非数字忽略。
- $first:取分组里文档取第一个元素。
- $last:取分组文档里最后一个元素。
- $max: 取分组里的最大元素。
- $min: 取分组里的最小元素。
- $sum: 取分组元素的和。
- $mergeObjects:合并分组里面的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
{ $mergeObjects: [ { a: 1 }, { a: 2, b: 2 }, { a: 3, c: 3 } ] } * $stdDevPop:标准差。 db.users.aggregate([ { $group: { _id: "$quiz", stdDev: { $stdDevPop: "$score" } } } ]) * $stdDevSamp:计算样本标准差。 db.users.aggregate( [ { $sample: { size: 100 } }, { $group: { _id: null, ageStdDev: { $stdDevSamp: "$age" } } } ] )
-
-
$sortByCount:根据数量排序。
-
$unwind:拆分数组。
1 2 3 4 5 6 7 8
{ $unwind: { path: <field path>, 字段 includeArrayIndex: <string>, 新字段的名字,不指定则使用数组名字 preserveNullAndEmptyArrays: <boolean> 是否保留空值(没有数组字段,和对应值为空的都会保留)。 } }
-
$sort:排序。
{ $sort: {
: , : ... } }
-
-
$skip: 跳过多少个元素。
1
{ $skip: <positive integer> }
-
$limits: 限制返回数量。
1
{ $limit: <positive integer> }
-
$count: 计算上一阶段文档的数量。
db.scores.aggregate([{ $count: "passing_scores" }])
-
-
$out:把结果存入到另外一个
Collection
中。1 2 3 4
db.books.aggregate( [ { $group : { _id : "$author", books: { $push: "$title" } } }, { $out : "authors" } ] )
-
$sample:从输入文档中随机选择指定个数的文档。当指定个书大于实际文档个数时返回所有元素。
db.users.aggregate( [ { $sample: { size: 3 } } ] ) * $replaceRoot: 替换根元素,用于把子文档升级成主文档。
db.people.aggregate( [ { $match: { pets : { $exists: true } } }, { $replaceRoot: { newRoot: "$pets" } } ] ) * $redact: 修剪文档。
-
$$DESCEND:保留,不保留包含的低级别的文档。
- $$PRUNE:修剪。
- $$KEEP:保留,如果文档不含低等级的文档,低等级的也会被包含。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# db.forecasts.aggregate( [ { $match: { year: 2014 } }, { $redact: { $cond: { if: { $gt: [ { $size: { $setIntersection: [ "$tags", [ "STLW", "G" ] ] } }, 0 ] }, then: "$$DESCEND", else: "$$PRUNE" } } } ] );
-
-
$lookup: 连接其它集合,类似于SQL的 join。
1 2 3 4 5 6 7 8 9 10 11
db.orders.aggregate([ { $lookup: { from: "inventory", localField: "item", foreignField: "sku", as: "inventory_docs" } } ])
-
$graphLookup: 图查找。
1 2 3 4 5 6 7 8 9 10 11 12
# db.employees.aggregate( [ { $graphLookup: { from: "employees", startWith: "$reportsTo", connectFromField: "reportsTo", connectToField: "name", as: "reportingHierarchy" } } ] )
-
$geoNear: 地理位置相关聚合。
-
$indexStats:索引状态。
1
db.orders.aggregate( [ { $indexStats: { } } ] )
-
$collStats: 集合状态。
聚合操作#
在聚合阶段可以使用的操作符,用于聚合阶段的运算。
-
$setIntersection
:数组交集运算。1
{ $setIntersection: [ [ "a", "b", "a" ], [ "b", "a" ] ] } -> [ "b", "a" ]
-
$abs
:求绝对值。 avg
: 求平均值。$ceil
:进一。$floor
: 去尾。$divide
:除法。{ $divide: [ <expression1>, <expression2> ] }
$add
:计算数组中元素的和,当其中一个元素为日期时,其它数字会被转化为毫秒。$addToSet
: 将元素放入集合(去重)。$push
:将元素放入数组(不去重)。$allElementsTrue
: 判断数组和集合中的元素都是 true,包含false
null
undefined
0
返回false,其它返回true。$and
:并操作,表达式都为true时返回true。$anyElementTrue
: 表达式中有一个元素为true时返回true。$arrayElemAt
:获取数组的指定位置上的元素。- $arrayToObject: 数组转换为对象。
$cmp
:比较两个元素的大小。 -1 、1、0。$concat
:连接元素。{ $concat: [ <expression1>, <expression2>, ... ] }
。$concatArrays
:连接数组。$cond
:条件判断。{ $cond: [ <boolean-expression>, <true-case>, <false-case> ] }
$convert
:类型转换。$dateFromParts
:将年月日转换为对应的日期。{ $dateFromParts: { 'year' : 2017, 'month' : 14, 'day': 1, 'hour' : 12 } }
。$dateToParts
:日期转换为年月日时分秒。$dateFromString
:字符串转换为日期。$dateToString
: 日期转换为字符串。$dayOfMonth
:一月的第几天。$dayOfWeek
:一周的第几天。$dayOfYear
一年的第几天。$divide
:除法。$eq
:是否相等。$exp
: e 的n次方。$filter
:用于过滤数组中的元素。$first
:分组中的第一个文档。$gt
:大于。$gte
: 大于等于。$hour
:日期的小时。$ifNull
:如果表达式的结果为空,返回默认。$ifNull: [ "$description", "Unspecified" ]
$in
: 判断元素是否存在于数组中。{ $in: [ 2, [ 1, 2, 3 ] ] }
$indexOfArray
:返回元素在数组中的坐标,没有返回 -1。{ $indexOfArray: [ [ "a", "abc" ], "a" ] }
$indexOfBytes
:字符在字符串中的位置。$indexOfCP
:字符串在另一个字符串中的位置。$isArray
:是否是数组。{ $isArray: [ <expression> ] }
$isoDayOfWeek
:一周的第几天。$isoWeek
: 第几周。$isoWeekYear
: 一年的第几周。$last
:分组中的最后一个元素。$let
: 定义变量。$literal
: 字面含义,相当于转义操作。{ $literal: { $literal: 1 } } -> { "$literal" : 1 }
$ln
: e 的对数。$log
: 对数。{ $log: [ <number>, <base> ] }
$log10
:10的对数。$lt
:小于。$lte
: 小于等于。$ltrim
:句首去除字符。{ $ltrim: { input: <string>, chars: <string> } }
,chars: <string> }
中的任意一个字符。$map
:转换运算。类似于java流的map。$max
:最大值。$mergeObjects
: 合并对象。$meta
: 元信息,如文档分数。$min
:最小值。$millisecond
: 毫秒数。$minute
:第几分钟。$mod
: 求余。$month
:月。$multiply
:乘法。$ne
: 不等于。$not
:非。$objectToArray
: 对象转数组。转换成 k/v 格式。$or
:或。$pow
: x 的 n 次方。{ $pow: [ 5, 0 ] }
$push
: 加入数组。$range
: 自动生成等差数组。{ $range: [ <start>, <end>, <non-zero step> ] }
$reduce
: 运算。1 2 3 4 5 6 7 8 9 10 11
# { "sum" : 15, "product" : 48 } { $reduce: { input: [ 1, 2, 3, 4 ], initialValue: { sum: 5, product: 2 }, in: { sum: { $add : ["$$value.sum", "$$this"] }, product: { $multiply: [ "$$value.product", "$$this" ] } } } }
$reverseArray
: 反转数组。$rtrim
:句尾去除指定字符。$second
:秒。$setDifference
: 集合差集。$setEquals
:集合是否相等。$setIntersection
: 集合交集。$setIsSubset
: 集合是否是另一个的子集。$setUnion
: 集合并集。$size
: 数组或集合的大小。$slice
: 数组切割。$split
: 字符串分割。$sqrt
: 开方。$stdDevPop
:标准差。$stdDevSamp
: 样本标准差。$strcasecmp
: 字符串比较。$strLenBytes
:字符串的字节长度。$strLenCP
:字符串的长度,一个汉字一个字节。$substr
: 子字符串。$substrBytes
:子字符串,utf字符中间分割会报错。$substrCP
:子字符串。$subtract
:减法。$sum
:求和。每个分组中文档个数count: { $sum: 1 }
。-
$switch
:条件选择。{ $switch: { branches: [ { case: { $eq: [ 0, 5 ] }, then: "equals" }, { case: { $gt: [ 0, 5 ] }, then: "greater than" }, { case: { $lt: [ 0, 5 ] }, then: "less than" } ] } } *
$toBool
:转换为boolean值。 *$toDate
: 转换为日期。 *$toDecimal
:转换为10进制数字。 *$toDouble
: 转换为double类型。 *$toInt
: *$toLong
: *$toObjectId
: *$toString
: *$toLower
: *$toUpper
: *$trim
:去除句首句尾的字符。 *$trunc
:去尾。 *$type
:字段类型。 *$week
:周 *$year
:年 *$zip
:矩阵转置。
聚合查询的一个例子#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
|