时间: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:索引状态。
1db.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,包含falsenullundefined0返回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 | |