MongoDB 聚合

聚合框架

    先看一个示例:有一个保存着杂志文章的集合,希望找出发表文章最多的前3名作者。
    那么按照以下步骤创建管道:
    1:将每篇文章中的作者投射出来;
    2:将作者按照名称排序,统计出每个名字出现的次数;
    3:将作者按照名字出现的次数降序排列;
    4:将返回的结果限制为前3个。

    1:{"$project": {"author": 1}} --> {"_id": id, "author": authorName}
         通过"fieldName": 1选择需要投射的字段,默认会投射"_id"字段。
    2:{"$group": {"_id": "$author", "count": {"$sum": 1}}} --> {"_id": authorName, "count": articleCount}
         指明分组字段"author",该操作执行完后,每个作者只对应一个结果文档,所以"author"就成了文档的唯一标识符("_id")。
    3:{"$sort": {"$count": 1}}
    4:{"$limit": 3}
    > db.article.aggregate(
    ... {"$project": {"author": 1}},
    ... {"$group": {"_id": "$author", "count": {"$sum": 1}}},
    ... {"$sort": {"$count": 1}},
    ... {"$limit": 3}
    )
    {
        "result": [
            {
                "_id": "Bob",
                "count": 490
            },
            {
                "_id": "Ellice",
                "count": 420
            },
            {
                "_id": "Nora",
                "count": 400
            }
        ],
        "ok": 1
    }
如果管道没有给出预期的结果,可以按照需要进行调试,逐步添加管道操作符调试。

管道操作符

    $match
    $match:对于文档集合进行筛选,之后可以在筛选得到的文档子集上做聚合。
         注意:不能在"$match"上使用地理空间操作符。尽可能将$match放在管道的前面位置,这样做优点:过滤不需要的文档,以减少管道的工作量,在投射和分组之前执行$match,查询可以使用索引。
    $project
    $project:从文档中提取字段,还可以重命名字段,等做一些有意思的操作。
    // 将每个用户文档的"_id"在返回结果中重命名成userId:
    > db.article.aggregate(
    ... {"$project": {"userId": "$_id", "_id": 0}}
    )
    {
        "result": [
            {
                "userId": ObjectId("54fd0571e4b055a0030461fb")
            },
            {
                "userId": ObjectId("54fd0571e4b055a0030461fc")
            },
            ...
        ],
        "ok": 1
    }
         注意:对字段进行重命名时,MongoDB并不会记录字段的历史名称。因此,如果在原字段名上有一个索引,被重命名之后,聚合框架之后就无法使用这个索引。因此应该尽量在修改字段名称之前使用索引。
    // 聚合框架无法在sort操作中使用在originFieldName上创建的索引:
    > db.article.aggregate(
    ... {"$project": {"newFieldName": "$originFieldName"}},
    ... {"$sort": {"newFieldName": 1}}
    )
    1.管道表达式
        在集合框架中,有几个表达式可以用来组合或进行任意深度的嵌套,以便创建复杂的表达式。
    2.数学表达式
    // 求总薪资:
    > db.employees.aggregate(
    ... {
    ...     "$project": {
    ...         "totalPay": {
    ...             "$add": ["$salary", $bonus]
    ...         }
    ...     }
    ... }
    )
 
操作符语法描述
$add[exp1[, exp2, ..., expN]]求和
$subtract[exp1, exp2]求差
$multiply[exp1[, exp2, ..., expN]]求积
$divide[exp1, exp2]求商
$mod[exp1, exp2]求余
    3.日期表达式

    // 计算每个雇员在公司的工作时间:
    > db.employees.aggregate(
    ... {
    ...     "$project": {
    ...         "tenure": {
    ...             "$subtract": [{"$year": new Date()}}, {"$year": "$hireDate"}]
    ...         }
    ...     }
    ... }
    )

      提取日期的表达式:"$year", "$month", "$week", "$dayOfMonth", "$dayOfWeek", "$dayOfYear", "$hour", "$minute", "$second"。

    4.字符串表达式
    // 生成j.doe@example.com格式的email地址,提取firstName第一个字符,并拼接lastName生成指定格式:
    > db.employees.aggregate(
    ... {
    ...     "$project": {
    ...         "email": {
    ...              "$concat": [
    ...                  {
    ...                      "$substr": ["$firstName", 0, 1],
    ...                      ".",
    ...                      "$lastName",
    ...                      "@example.com"
    ...                  }
    ...              ]
    ...         }
    ...     }
    ... }
    )
操作符语法描述
$substr[expr, startOffset, numToReturn]截取字符串,从startOffset字节开始的numToReturn个字节
$concat[exp1[, exp2, ... , expN]]连接字符串
$toLowerexpr转换称小写
$toUpperexpr转换称大写
    5.逻辑表达式
    // 为学生打分,出勤率占10%,日常测验占30%,期末考试占60%,(如果是老师最宠爱的学生,那么分数是100):
    > db.students.aggregate(
    ... {
    ...     "$project": {
    ...         "grade": {
    ...              "$cond": [
    ...                  "$teacherPet",
    ...                  100, // if
    ...                  {    // else
    ...                      "$add": [
    ...                          "$multiply": [.1, "$attendanceAvg"],
    ...                          "$multiply": [.3, "$quizzAvg"],
    ...                          "$multiply": [.6, "$testAvg"]
    ...                      ]
    ...                  }
    ...              ]
    ...         }
    ...     }
    ... }
    )

比较表达式
$cmp[exp1, exp2]相等返回0,exp1<exp2返回负数,否则返回正数
$strcasecmp[string1, string2]比较string1和string2,区分大小写,只对罗马字符有效
$eq/$ne/$gt/$gte/$le/$lte[exp1, exp2]进行比较操作,返回比较的结果(true/false)

布尔表达式
$and[exp1[, exp2, ... , expN]]与运算
$or[exp1[, exp2, ... , expN]]或运算
$notexpr取反
控制语句
$cond[booleanExpr, trueExpr, falseExpr]如果booleanExpr值为true,返回trueExpr,否则返回falseExpr
$ifNull[expr, replacementExpr]如果expr是null,返回replacementExpr,否则返回expr
   $group  

   $group:将文档依据特定字段的不同值进行分组。

    1.分组操作符
    对每一个分组进行计算,得到相应的结果。例:我们最先介绍的示例
    2.算术操作符
    $sum:value, 对于分组中的每一个文档,将value与计算结果相加。
    $avg:value, 返回每个分组的平均值。
    3.极值操作符
    // 查找学生考试成绩的最高分和最低分:
    > db.scores.aggregate(
    ... {
    ...     "$group": {
    ...         "_id": "$grade",
    ...         "lowestScore": {"$min": $score},
    ...         "highestScore": {"$max": $score}
    ...     }
    ... }
    )
         如果数据是排过序的,那么$first和$last效率比$min和$max高,否则反之。

操作符语法描述
$maxexpr返回分组内的最大值
$minexpr返回分组内的最小值
$firstexpr返回分组的第一个值,忽略后面的所有值,只有排序之后(数据有序),才有意义
$lastexpr返回分组的最后一个值,忽略前面所有值,只有排序之后(数据有序),才有意义
    4.数组操作符
    $addToSet:expr, 如果当前数组中不包含expr,那么将其添加到数组中。返回结果集每个元素最多只出现一次,而且元素的顺序时不确定的。

    $push:expr, 不管expr是什么值,都将其添加到数组中,返回包含所有值的数组。

    $unwind
    $unwind:可以将数组中的每一个值拆分为单独的文档。
    // 拆分博客评论为单个文档
    > db.bolgs.aggregate(
    ... {"$unwind": "$comments"}
    )
    {
        "result": [
            {
                // 博客公共部分 start
                "_id": ObjectId("54fd0571e4b055a0030461fb"),
                "author": "r",
                "post": "hello world!",
                // 博客公共部分 end
                "comments": {
                    "author": "bob",
                    "content": "good post"
                }
            },
            {
                "_id": ObjectId("54fd0571e4b055a0030461fb"),
                "author": "r",
                "post": "hello world!",
                "comments": {
                    "author": "bill",
                    "content": "nice post"
                }
            },
            ...
        ],
        "ok": 1
    }
    $sort

    $sort:可以根据任何字段(或者多个字段)进行排序。

    $limit

    $limit:接受一个数字n,返回结果集中的前n个文档。

    $skip
    $skip:接受一个数字n,丢弃结果集中的前n个文档,将剩余文档作为结果返回。



MongoDB 聚合管道

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值