MongoDB 聚合

MongoDB 的产生背景是在大数据环境,所谓的大数据实际上也就是进行的信息收集汇总。那么就必须有信息的统计操作,而这样的统计操做就成为聚合。(直白:分组统计就是一种聚合操作)

获取集合个数

使用 “count()” 函数获取集合个数

范例:统计 students 表中的数据量

db.students.count();

范例:模糊查询

db.students.count({"name": /张/i});

在进行信息查询时,不设置条件永远要比设置条件查询快很多,即不管是全部查询还是模糊查询,实际上最终都使用的模糊查询一种(没有设置关键字)。


消除重复数据

使用 “distinct” 消除重复数据

范例:查询所有 name 的信息
本次的操作没有直接的函数支持,只能利用 runCommand() 函数

db.runCommand({"distinct": "students", "key": "name"});

group 操作

使用 “group” 操作分组数据

范例:查询所有年龄大于等于 19 岁的学生信息,并且按照年龄分组

db.runCommand({"group":{
    "ns": "students",
    "key": {"age": true},
    "initial": {"count": 0},
    "condition": {"age": {"$gte": 19}},
    "$reduce": function(doc, prev){
        prev.count ++;  //表示数量加一
    }
}})

以上的操作代码实现的就属于一种 MapReduce


MapReduce

MapReduce 是整个大数据的精髓所在(实际中别用)。

MapReduce 就是分为两步处理数据:

  • Map:将数据分别取出
  • Reduce:负责数据的最后的处理

范例:建立一组雇员数据

db.emps.insert({"name": "张三", "age": 30, "sex": "男", "job": "CLERK", "salary": 1000});
db.emps.insert({"name": "李四", "age": 28, "sex": "女", "job": "CLERK", "salary": 5000});
db.emps.insert({"name": "王五", "age": 26, "sex": "男", "job": "MANAGER", "salary": 6000});
db.emps.insert({"name": "赵六", "age": 32, "sex": "女", "job": "MANAGER", "salary": 7000});
db.emps.insert({"name": "孙七", "age": 31, "sex": "男", "job": "CLERK", "salary": 2000});
db.emps.insert({"name": "王八", "age": 35, "sex": "女", "job": "PRESIDENT", "salary": 9000});

范例:按照职位分组,取得每个职位的人名
第一步:编写分组的定义:

var jobMapFun = function(){
    emit(this.job, this.name); //按照 job 分组,取出 name
};

第二步:编写 reduce 操作:

var jobReduceFun = function(){
    return {age: key, names: values};
}

第三步:进行操作的整合:

db.runCommand({
    "mapreduce": "emps",
    "map": jobMapFun,
    "reduce": jobReduceFun,
    "out": "t_job_emp"
});

或者将上面第三步,换成如下的第三步和第四步

第三步:针对于 MapReduce 处理完成的数据实际上也可以执行一个最后的处理

var jobFinalizeFun = function(key, values){
    if(key == "PRESIDENT"){
        return {"job": key, "names": values, "info": "公司的老大"};
    }
    return {"job": key, "names": values};
}

第四步:进行操作的整合:

db.runCommand({
    "mapreduce": "emps",
    "map": jobMapFun,
    "reduce": jobReduceFun,
    "out": "t_job_emp",
    "finalize": jobFinalizeFun
});

现在执行之后,所有的处理结果都保存在 “t_job_emp” 集合里面,通过如下命令查看

db.t_job_emp.find().pretty();

范例:统计出各性别的人数、平均工资、最低工资、雇员姓名

var sexMapFun = function(){
    //定义好了分组的条件,以及每个集合要取出的内容
    emit(this.sex, {"count": 1, "csal": this.salary, "cmax": this.salary, "cmin": this.salary, "cname": this.name});
}

var sexReduceFun = function(key, values){
    var total = 0; //统计
    var sum = 0;  //计算总工资
    var max = values[0].cmax;  //假设第一个数据是最高工资
    var min = values[0].cmin; //假设第一个数据是最低工资
    var names = new Array();   //定义数组内容
    for(var x in values){  //表示循环取出里面的数据
        total += values[x].ccount; //人数增加
        sum += values[x].csal; //循环取出所有的工资,并且累加
        if(max < values[x].cmax){  //不是最高工资
            max = values[x].cmax;
        }
        if(min > values[x].cmin){  //不是最低工资
            min = values[x].cmin;
        }
        names[x] = values[x].cname;  //保存姓名
    }
    var avg = (sum / total).toFixed(2);
    //返回数据的处理结果
    return {"count": total, "avg": avg, "sum": sum, "max": max, "min": min, "names": names};
};

db.runCommand({
    "mapreduce": "emps",
    "map": sexMapFun,
    "reduce": sexReduceFun,
    "out": "t_sex_emp"
});

现在执行之后,所有的处理结果都保存在 “t_sex_emp” 集合里面,通过如下命令查看

db.t_sex_emp.find().pretty();

虽然大数据的时代提供有最强悍的 MapReduce 支持,但是从显示的开发来讲,真的不可能使用起来。


聚合框架

MapReduce 功能强大,大师它的复杂度和功能一样强大,那么很多时候我们需要 MapReduce 的功能,可是又不想把代码写的太复杂,所以从 Mongo 2.x 版本之后开始引入了聚合函数并且提供了聚合函数 aggregate()

group 主要进行数据的分组操作,若要引用每行的数据使用:”$字段名称”

范例:实现聚合查询的功能

db.emps.aggregate([{"$group": {"_id": "$job", job_count: {"$sum": 1}}}]);

范例:求出每个职位的总工资

db.emps.aggregate(["$group": {"_id": "$job", job_sal: {"$sum": "$salary"}}]);

范例:计算出每个职位的工资数据(数组显示)

db.emps.aggregate([{"$group":{
    "_id": "$job",
    "sal_data": {"$push": "$salary"},
}}]);

使用 “$push” 可以将数据变为数组进行保存,但是可能有个问题,重复的内容也会进行保存,可以通过 “$addToSet” 来解决

范例:取消重复的数据

db.emps.aggregate([{"$group":{
    "_id": "$job",
    "sal_data": {"$addToSet": "$name"}
}}]);

范例:保存第一个内容

db.emps.aggregate([{"$group":{
    "_id": "$job",
    "sal_data": {"$first": "$name"}
}}]);

范例:保存最后一个内容

db.emps.aggregate([{"$group":{
    "_id": "$job",
    "sal_data": {"$last": "$name"}
}]);

注意:所有的分组数据是无序的,并且都是在内存中完成的,所以不可能支持大数据量


$project

“$project” 用来控制数据列的显示规则,执行的规则如下:

  • 普通列 ( {成员: 1 | true} ):表示要显示的内容
  • “_id”列 ( {“_id”: 0 | false} ):表示 “_id”列表是否显示
  • 条件过滤列 ({“成员”: “表达式”}):满足表达式之后的数据可以进行显示

范例:只显示 name、job列,不显示 “_id” 列

db.emps.aggregate([{
    "$project": {
        "_id": 0,
        "name": 1
    }
}]);

此时,只有设置进去的列才可以被显示出来,而其他的列不能够被显示出来。实际上这就属于数据库的投影机制

数据库的投影机制支持各种运算符

  • 四则运算符:加法 (“$add“)、减法 (“$subtract“)、乘法 (“$multiply“)、除法 (“$divide“)、求模 (“$mod“)

  • 关系运算符:大小比较 (“$cmp“)、等于 (“$eq“)、大于 (“$gt“)、大于等于 (“$gte“)、小于 (“$lt“)、小于等于 (“$lte“)、不等于(“$ne“)、判断 NULL(“$ifNull“),这些返回的结果都是布尔型数据

  • 逻辑运算符:与 (“$and“)、或 (“$or“)、非 (“$not“)

  • 字符串操作:连接 (“ concat")(" substr”)、转小写 (“$toLower“)、转大写(“$toUpper“)、不区分大小写比较(“$strcasecmp“))

范例:观察四则运算

db.emps.aggregate([{"`$project`": {
    "_id": 0,
    "name": 1,
    "职位": "$job",
    "salary": 1}
}]);

db.emps.aggregate([{"$project": {
    "_id": 0,
    "name": 1,
    "job": 1,
    "salary": 1}
}]);

db.emps.aggregate([{"$project":{
    "_id": 0,
    "name": 1,
    "job": 1,
    "salary": {"年薪": {
        "$multiply": ["$salary", 12]
    }}
}}]);

范例:找出所有工资大于等于 2000 的雇员姓名、年龄、工资

db.emps.aggregate([{"$project": {
    "_id": 0,
    "name": 1,
    "job": 1,
    "工资": "$salary",
    "$salary": {
        "$gte": ["$salary", 2000]
    }
}}]);

范例:查询职位是 manager 的信息(MongoDB 的数据是区分大小写的)

db.emps.aggregate([{"`$project`": {
    "_id": 0,
    "name": 1,
    "职位": "$job",
    "job":{
        "$eq":["$job", "MANAGER"]
    }
}}]);

db.emps.aggregate([{"`$project`": {
    "_id": 0,
    "name": 1,
    "职位": "$job",
    "job":{
        "$eq":["$job", {"$toUpper": "manager"]
    }
}}]);

db.emps.aggregate([{"`$project`": {
    "_id": 0,
    "name": 1,
    "职位": "$job",
    "job":{
        "$eq":{"strcasecmp": ["$job", "manager"]
    }
}}]);

范例:使用字符串截取

db.emps.aggregate([{"$project":{
    "_id": 0,
    "name": 1,
    "职位": "$job",
    "job":{"前面三位": {"$substr": ["$job", 0, 3]}}
}}]);

$sort

使用 “$sort” 可以实现排序,设置 1 表示升序,设置 -1 表示降序。

范例:实现排序

db.emps.aggregate(["$sort":{"age": -1, "salary": 1}]);

范例:将所有的操作一起使用

db.emps.aggregate([
    {"$match":{
        "salary": {"$gte": 1000, "$lte": 10000}
    }},
    {"$project": {"_id": 0, name: 1, salary: 1, "job": 1}},
    {"$group": {"_id": "$job", "count": {"$sum": 1}, "avg": {"$avg": "$salary"}}},
    {"$sort": {"count": -1}}
]);

此时实现了降序排序,使用的是生成定义的别名。


分页处理

分页处理:

  • “$limit”:负责数据的取出个数
  • “$skip”:数据的跨过个数

范例:使用 “$limit” 设置取出的个数

db.emps.aggregate([
    {"$project": {
        "_id": 0, 
        "name": 1,
        "salary": 1,
        "job": 1
        }},
    {"$limit": 2}
]);

范例:跨过三行数据

db.emps.aggregate([
    {"$project":{
        "_id": 0,
        "name": 1,
        "salary": 1,
        "job": 1
        }},
    {"$skip": 3},
    {"$limit": 2}
]);

范例:综合应用

db.emps.aggregate([
    {"$match":{
        "salary": {
            "$gte": 1000,
            "$lte": 10000
            }}},
    {"$project": {
        "_id": 0, 
        name: 1,
        salary: 1,
        "job": 1}},
    {"$group":{
        "_id": "$job",
        "count": {"$sum": 1},
        "avg": {"avg": "$salary"}}},
    {"$sort": {"count": -1}},
    {"$skip": 1},
    {"$limit": 1}
]);

$unwind

在查询数据时,经常会返回数组信息,但是数组并不方便信息的浏览,所以可以通过 “$unwind” 可以将数组数据变为独立的字符串内容。

范例:添加一些信息

db.depts.insert({"title": "技术部", "bus": ["研发", "生产", "培训"]});

db.depts.insert({"title": "财务部", "bus": ["工资", "税收"]});

范例:将信息进行转化

db.depts.aggregate([
    {"$project": {"_id": 0, "title": true, "bus": true}},
    {"$unwind": "$bus"}
]);

此时相当于将数组中的数据变为了单行的数据。


$geoNear

使用 “$geoNear” 可以得到附近的坐标点。

范例:准备测试数据

db.shop.drop();
db.shop.insert({loc: [10,10]});
db.shop.insert({loc: [11,10]});
db.shop.insert({loc: [10,11]});
db.shop.insert({loc: [12,15]});
db.shop.insert({loc: [16,17]});
db.shop.insert({loc: [90,90]});
db.shop.insert({loc: [120,130]});

范例:设置查询

db.shop.aggregate([
    {"$geoNear":{
        "near": [11, 12],
        "distanceField": "loc",
        "maxDistance": 1,
        "num": 2,
        "spherical": true
    }}
]);

地理信息的检索必须存在有索引的支持。


“$out”

“$out”:利用此操作可以将查询结果输出到指定的集合里面。

范例:将投影的结果输出到集合里

db.emps.aggregate([
    {"$project": {"_id": 0, "name": 1, "salary": 1, "job": 1}},
    {"$out": "emp_infos"}
]);

这类的操作相当于实现了最早的数据表的复制操作。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值