`
Coding4J
  • 浏览: 7535 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

MongoDB聚合查询

阅读更多

出于对性能的要求,公司希望把Mysql的数据迁移到MongoDB上,于是我开始学习Mongo的一些CRUD操作,由于第一次接触NoSQL,还是有点不习惯。

先吐个槽,公司的Mongo版本是2.6.4,而用的java驱动包版本是超级老物2.4版。当时一个“如何对分组后的文档进行筛选”这个需求头痛了很久,虽然shell命令下可以使用Aggregation很方便地解决,但是java驱动包从2.9.0版本才开始支持该特性,我至今没有找到不用Aggregation解决上述需求的办法。只能推荐公司升级驱动包版本,希望没有后续的兼容问题。

Mongo2.2版本后开始支持Aggregation Pipeline,而java驱动包从2.9.0版本才开始支持2.2的特性,2.9版本是12年发布的,mongodb在09年就出现了,可见Mongo对java的开发者似乎不怎么友好←_←

话扯到这里,接下来就对我这一周所学的Mongo做一个总结,错误之处还望指教 :-D。

 

MongoDB目前提供了三个可以执行聚合操作的命令:aggregate、mapReduce、group。三者在性能和操作的优劣比较见官网提供的表格 Aggregation Commands Comparison,这里不再赘述细节。

先说一下这三个函数的原型及底层封装的命令,这张是我自己总结的表格。

函数名

函数原型

封装的命令

db.collection.group()

db.collection.group(

    {

        key,

        reduce,

        initial

        [, keyf]

        [, cond]

        [, finalize]

    }

)

db.runCommand(

    {

      group:

       {

        ns: <namespace>,

        key: <key>,

        $reduce: <reduce function>,

        $keyf: <key function>,

        cond: <query>,

        finalize: <finalize function>

       }

    }

)

db.collection.mapReduce()

db.collection.mapReduce(

    <map>,

    <reduce>,

    {

        out: <collection>,

        query: <document>,

        sort: <document>,

        limit: <number>,

        finalize: <function>,

        scope: <document>,

        jsMode: <boolean>,

        verbose: <boolean>

    }

)

db.runCommand(

    {

    mapReduce: <collection>,

    map: <function>,

    reduce: <function>,

    finalize: <function>,

    out: <output>,

    query: <document>,

    sort: <document>,

    limit: <number>,

    scope: <document>,

    jsMode: <boolean>,

    verbose: <boolean>

    }

)

db.collection.aggregate()

db.collection.aggregate(

    pipeline,

    options

)

db.runCommand(

    {

      aggregate: "<collection>",

      pipeline: [ <stage>, <...> ],

      explain: <boolean>,

      allowDiskUse: <boolean>,

      cursor: <document>

    }

)

 

好记性不如烂笔头,下面通过操作来了解这几个函数和命令:

 

先准备SQL的测试数据(用来验证结果、比较SQL语句和NoSQL的异同):

先创建数据库表:

create table dogroup (
       _id int,
       name varchar(45),
       course varchar(45),
       score int,
       gender int,
       primary key(_id)
);

 插入数据:

insert into dogroup (_id, name, course, score, gender) values (1, "N", "C", 5, 0);
insert into dogroup (_id, name, course, score, gender) values (2, "N", "O", 4, 0);
insert into dogroup (_id, name, course, score, gender) values (3, "A", "C", 5, 1);
insert into dogroup (_id, name, course, score, gender) values (4, "A", "O", 6, 1);
insert into dogroup (_id, name, course, score, gender) values (5, "A", "U", 8, 1);
insert into dogroup (_id, name, course, score, gender) values (6, "A", "R", 8, 1);
insert into dogroup (_id, name, course, score, gender) values (7, "A", "S", 7, 1);
insert into dogroup (_id, name, course, score, gender) values (8, "M", "C", 4, 0);
insert into dogroup (_id, name, course, score, gender) values (9, "M", "U", 7, 0);
insert into dogroup (_id, name, course, score, gender) values (10, "E", "C", 7, 1);

接着准备MongoDB测试数据:

创建Collection(等同于SQL中的表,该行可以不写,Mongo会在插入数据时自动创建Collection)

db.createCollection("dogroup") 

插入数据:

db.dogroup.insert({"_id": 1,"name": "N",course: "C","score": 5,gender: 0})
db.dogroup.insert({"_id": 2,"name": "N",course: "O","score": 4,gender: 0})
db.dogroup.insert({"_id": 3,"name": "A",course: "C","score": 5,gender: 1})
db.dogroup.insert({"_id": 4,"name": "A",course: "O","score": 6,gender: 1})
db.dogroup.insert({"_id": 5,"name": "A",course: "U","score": 8,gender: 1})
db.dogroup.insert({"_id": 6,"name": "A",course: "R","score": 8,gender: 1})
db.dogroup.insert({"_id": 7,"name": "A",course: "S","score": 7,gender: 1})
db.dogroup.insert({"_id": 8,"name": "M",course: "C","score": 4,gender: 0})
db.dogroup.insert({"_id": 9,"name": "M",course: "U","score": 7,gender: 0})
db.dogroup.insert({"_id": 10,"name": "E",course: "C","score": 7,gender: 1})

 

以下操作可能逻辑上没有实际意义,主要是帮助熟悉指令

 

1、查询出共有几门课程(course),返回的格式为“课程名、数量”

SQL写法:

select course as '课程名', count(*) as '数量' from dogroup group by course;

MongoDB写法:

① group方式

db.dogroup.group({
key : { course: 1 },
initial : { count: 0 },
reduce : function Reduce(curr, result) {
    result.count += 1;
},
finalize : function Finalize(out) {
    return {"课程名": out.course, "数量": out.count};
}
});

 返回的格式如下:

{
        "课程名" : "C",
        "数量" : 4
},
{
        "课程名" : "O",
        "数量" : 2
},
{
        "课程名" : "U",
        "数量" : 2
},
{
        "课程名" : "R",
        "数量" : 1
},
{
        "课程名" : "S",
        "数量" : 1
}

 ② mapReduce方式

db.dogroup.mapReduce(
    function () {
        emit(
            this.course,
            {course: this.course, count: 1}
        );
    },
    function (key, values) {
        var count = 0;
        values.forEach(function(val) {
            count += val.count;
        });
        return {course: key, count: count};
    },
    {
        out: { inline : 1 },
        finalize: function (key, reduced) {
            return {"课程名": reduced.course, "数量": reduced.count};
        }
    }
)

这里把count初始化为1的原因是,MongoDB执行完map函数(第一个函数)后,如果key所对应的values数组的元素个数只有一个,reduce函数(第二个函数)将不会被调用。

返回的格式如下:

{
      "_id" : "C",
      "value" : {
              "课程名" : "C",
              "数量" : 4
      }
},
{
      "_id" : "O",
      "value" : {
              "课程名" : "O",
              "数量" : 2
      }
},
{
      "_id" : "R",
      "value" : {
              "课程名" : "R",
              "数量" : 1
      }
},
{
      "_id" : "S",
      "value" : {
              "课程名" : "S",
              "数量" : 1
      }
},
{
      "_id" : "U",
      "value" : {
              "课程名" : "U",
              "数量" : 2
      }
}

 ③ aggregate方式

db.dogroup.aggregate(
    {
        $group:
        {
            _id: "$course",
            "数量": { $sum: 1 }
        }
    }
)

返回格式如下:

{ "_id" : "S", "数量" : 1 }
{ "_id" : "R", "数量" : 1 }
{ "_id" : "U", "数量" : 2 }
{ "_id" : "O", "数量" : 2 }
{ "_id" : "C", "数量" : 4 }

以上三种方式中,group得到了我们想要的结果,mapReduce返回的结果只能嵌套在values里面,aggregate必须返回_id,无法为分组的字段指定别名,但是无疑第三种是最简单的。

 

虽然上面的问题不影响程序在前台展现数据,但是对于一个略微有强迫症的开发者确实难以忍受的。本人才疏学浅,刚接触Mongo,不知道后两者有没有可行的方法来获取想要的结果,希望网友指教。

 

2、查询Docouments(等同于SQL中记录)数大于2的课程

SQL写法:

select course, count(*) as count from dogroup group by course having count > 2;

MongoDB写法:

 ① aggregate方式(注意$group和$match的先后顺序)

db.dogroup.aggregate({
    $group: {
        _id: "$course",
        count: { $sum: 1 }
    }
    },{
    $match: {
        count:{
            $gt: 2
        }
    }
});

 目前尚未找到group和mapReduce对分组结果进行筛选的方法,欢迎网友补充

 

3、找出所有分数高于5分的考生数量及分数,返回的格式为“分数、数量”

SQL写法:

select score as '分数', count(distinct(name)) as '数量' from dogroup where score > 5 group by score;

MongoDB写法:

① group方式

db.dogroup.group({
    key : { score: 1 },
    cond : { score: {$gt: 5} },
    initial : { name:[] },
    reduce : function Reduce(curr, result) {
        var flag = true;
        for(i=0;i<result.name.length&&flag;i++){
            if(curr.name==result.name[i]){
                flag = false;
            }
        }
        // 如果result.name数组里面没有curr.name则添加curr.name
        if(flag){
            result.name.push(curr.name);
        }
    },
    finalize : function Finalize(out) {
        return {"分数": out.score, "数量": out.name.length};
    }
});

 ② mapReduce方式

db.dogroup.mapReduce(
    function () {
        if(this.score > 5){
            emit(
                this.score,
                {score: this.score, name: this.name}
            );
        }
    },
    function (key, values) {
        var reduced = {score: key, names: []};
        var json = {};//利用json对象的key去重
        for(i = 0; i < values.length; i++){
            if(!json[values[i].name]){
                reduced.names.push(values[i].name);
                json[values[i].name] = 1;
            }
        }
        return reduced;
    },
    {
        out: { inline : 1 },
        finalize: function (key, reduced) {
            return {"分数": reduced.score, "数量": reduced.names?reduced.names.length:1};
        }
    }
)

 ③ aggregate方式

db.dogroup.aggregate({
        $match: {
            score: {
                $gt: 5
            }
        }
    },{
        $group: {
            _id: {
                score: "$score",
                name: "$name"
            }
        }
    },{
        $group: {
            _id: {
                "分数": "$_id.score"
            },
            "数量": { $sum: 1 }
        }
});

 

弄熟上面这几个方法,大部分的分组应用场景应该没大问题了。

 

英文还可以的朋友,推荐看一看这张图示:




 

  • 大小: 141.8 KB
1
0
分享到:
评论

相关推荐

    JAVA mongodb 聚合几种查询方式详解

    JAVA mongodb 聚合查询方式详解 聚合查询是 MongoDB 中的一种强大查询方式,通过对数据进行分组、过滤、排序等操作,可以快速地获取所需的数据。在 JAVA 中使用 MongoDB 时,通常使用 Spring Data MongoDB 框架来...

    MongoDB聚合操作详细步骤.pdf

    MongoDB是一款强大的NoSQL数据库,它以文档形式存储数据,提供了灵活的查询语言和丰富的索引支持。MongoDB中的聚合框架是一个强大且灵活的数据处理工具,它允许用户对存储在数据库中的数据执行各种操作,从简单的...

    mongodb-query-exporter:Prometheus MongoDB聚合查询导出器

    Prometheus MongoDB查询导出器 用于MongoDB聚合查询导出器。特征支持量规指标推和推(仅MongoDB&gt; = 3.6支持推) 支持多个MongoDB服务器Golang的公共API 公制缓存支持请注意,这并不是要替代来检测MongoDB内部。 此...

    11、MongoDB聚合操作及索引使用详解-ev.rar

    11、MongoDB聚合操作及索引使用详解_ev.rar11、MongoDB聚合操作及索引使用详解_ev.rar11、MongoDB聚合操作及索引使用详解_ev.rar11、MongoDB聚合操作及索引使用详解_ev.rar11、MongoDB聚合操作及索引使用详解_ev.rar...

    MongoDB 聚合管道(Aggregation Pipeline)

    接下来,我们将详细地探讨MongoDB聚合管道的相关知识点。 首先,从概念上理解,MongoDB的聚合管道可以类比为一个数据处理的“流水线”,每个阶段(Stage)可以看作是流水线上的一个节点,文档会按顺序地通过这些...

    Mongodb聚合

    ### MongoDB 聚合框架详解 #### 概述 MongoDB 的聚合框架是数据库系统中一个强大而灵活的功能,用于处理复杂的数据分析需求。通过一系列的管道操作(pipeline stages),可以实现对数据集进行筛选、分组、计算等...

    MongoDB聚合实战:数据分析与财务应用轻松入门

    本书“MongoDB 聚合实战:数据分析与财务应用轻松入门”是为初学者和专业人士设计的一份指南,旨在帮助读者快速理解并应用 MongoDB 的聚合功能。 在第一部分,书中介绍了 MongoDB 的基础知识。从聚合的魅力开始,...

    mongodb聚合学习使用文档.txt

    mongodb聚合学习使用文档

    MongoDB查询练习题及答案

    以上仅是 MongoDB 查询的一些基本操作,实际上,MongoDB 提供了更多高级查询功能,如聚合框架、正则表达式匹配、地理空间查询等。学习并熟练掌握这些查询技巧,对于有效管理和分析 MongoDB 数据库中的数据至关重要。

    spring data mongodb 聚合 管道

    本篇文章将详细介绍如何利用Spring Data MongoDB API进行聚合查询,并提供一个具体的步骤示例。 首先,我们需要理解MongoDB的聚合框架,它包括一系列的阶段,每个阶段都是一个操作,如`$match`、`$group`、`$sort`...

    MongoDB聚合分组取第一条记录的案例与实现方法

    MongoDB的聚合框架提供了内置的聚合操作,如 `$first` 和 `$last`,可以用于在一个聚合管道中直接获取每个分组的第一个或最后一个文档。然而,在这个案例中,由于需要基于 `createTime` 的最新记录,因此需要额外的...

    MongoDB聚合框架:数据转换与分析的强大工具

    5. **丰富的查询语言**:MongoDB提供了一个强大的查询语言,支持文档的复杂查询和数据聚合。 6. **索引**:支持多种类型的索引,以优化查询性能。 7. **灵活的聚合框架**:MongoDB的聚合框架允许用户执行复杂的数

    深入解析MongoDB聚合与索引:提升数据库效能的关键策略

    **一、MongoDB聚合** 聚合是MongoDB中处理数据的一种方式,允许用户对集合中的数据进行分析和计算,类似于SQL中的GROUP BY操作。聚合框架包括多个阶段,如`$match`(过滤数据)、`$group`(分组数据)、`$sort`...

    MongoDB命令查询.txt

    在MongoDB中,各种操作主要通过命令来完成,这些命令可以分为几大类:查询命令、更新命令、聚合命令等。其中查询命令是使用频率最高的一类,它允许用户按照指定条件从集合中检索文档。 ### `findOne()` 命令详解 #...

    头歌6.4MongoDB 之聚合函数查询统计

    头歌6.4MongoDB 之聚合函数查询统计

    MongoDB之查询详解

    ### MongoDB查询详解 #### 一、引言 在NoSQL数据库的世界里,MongoDB因其灵活的数据模型、高性能和可扩展性...在未来的学习和实践中,我们还可以进一步探索其他高级查询特性,如聚合框架等,以应对更复杂的业务需求。

    MongoDB查询性能验证及优化

    MongoDB使用查询解释器来解析和执行查询操作。通过`explain()`方法,我们可以获取查询的执行计划,包括扫描的文档数量、使用的索引、是否进行了排序等关键信息。例如,以下Java代码片段展示了如何在`MainTest.java`...

    《MongoDB实战》第2版-第6章-聚合查询统计分析1

    《MongoDB实战》第二版的第6章聚焦于“聚合查询与统计分析”,这是数据库管理和数据分析的关键领域。MongoDB的聚合框架是其高级查询语言的一部分,它允许对数据进行复杂处理,生成在单个文档中不存在的新信息。这一...

Global site tag (gtag.js) - Google Analytics