Mongo DB聚合

                   MongoDB 聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。 aggregate() 方法MongoDB中聚合的方法使用aggregate()。

语法 aggregate() 方法的基本语法格式如下所示:

db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION) 实例

集合中的数据如下:

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

{

  _id: ObjectId(7df78ad8902c)

  title: 'MongoDB Overview',

  description: 'MongoDB is no sql database',

  by_user: 'jb51.net',

  url: 'http://www.jb51.net',

  tags: ['mongodb', 'database', 'NoSQL'],

  likes: 100

},

{

  _id: ObjectId(7df78ad8902d)

  title: 'NoSQL Overview',

  description: 'No sql database is very fast',

  by_user: 'jb51.net',

  url: 'http://www.jb51.net',

  tags: ['mongodb', 'database', 'NoSQL'],

  likes: 10

},

{

  _id: ObjectId(7df78ad8902e)

  title: 'Neo4j Overview',

  description: 'Neo4j is no sql database',

  by_user: 'Neo4j',

  url: 'http://www.neo4j.com',

  tags: ['neo4j', 'database', 'NoSQL'],

  likes: 750

},

现在我们通过以上集合计算每个作者所写的文章数,使用aggregate()计算结果如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

> db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])

{

  "result" : [

   {

     "_id" : "w3cschool.cc",

     "num_tutorial" : 2

   },

   {

     "_id" : "Neo4j",

     "num_tutorial" : 1

   }

  ],

  "ok" : 1

}

>

以上实例类似sql语句:select by_user, count(*) from mycol group by by_user 在上面的例子中我们通过字段by_user字段对数据进行分组,并计算by_user字段相同值的总和。 下表展示了一些聚合的表达式:

表达式

描述

实例

$sum

计算总和。

db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])

$avg

计算平均值

db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])

$min

获取集合中所有文档对应值得最小值。

db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])

$max

获取集合中所有文档对应值得最大值。

db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])

$push

在结果文档中插入值到一个数组中。

db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])

$addToSet

在结果文档中插入值到一个数组中,但不创建副本。

db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])

$first

根据资源文档的排序获取第一个文档数据。

db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])

$last

根据资源文档的排序获取最后一个文档数据

db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])

管道的概念 管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。 MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。 表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。 这里我们介绍一下聚合框架中常用的几个操作: $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。 $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。 $limit:用来限制MongoDB聚合管道返回的文档数。 $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。 $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。 $group:将集合中的文档分组,可用于统计结果。 $sort:将输入文档排序后输出。 $geoNear:输出接近某一地理位置的有序文档。

管道操作符实例

1、$project实例

1

2

3

4

5

6

db.article.aggregate(

  { $project : {

    title : 1 ,

    author : 1 ,

  }}

 );

这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:

1

2

3

4

5

6

db.article.aggregate(

  { $project : {

    _id : 0 ,

    title : 1 ,

    author : 1

  }});

2.$match实例

1

2

3

4

db.articles.aggregate( [

            { $match : { score : { $gt : 70, $lte : 90 } } },

            { $group: { _id: null, count: { $sum: 1 } } }

            ] );

$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

3.$skip实例

1

2

db.article.aggregate(

  { $skip : 5 });

经过$skip管道操作符处理后,前五个文档被"过滤"掉。

基础知识——操作符介绍:

$project:包含、排除、重命名和显示字段 $match:查询,需要同find()一样的参数 $limit:限制结果数量 $skip:忽略结果的数量 $sort:按照给定的字段排序结果 $group:按照给定表达式组合结果 $unwind:分割嵌入数组到自己顶层文件 文档:MongoDB 官方aggregate说明

相关使用:

db.collection.aggregate([array]);

array可是是任何一个或多个操作符。 group和match的用法,使用过sqlserver,group的用法很好理解,根据指定列进行分组统计,可以统计分组的数量,也能统计分组中的和或者平均值等。 group之前的match,是对源数据进行查询,group之后的match是对group之后的数据进行筛选;

同理,sort,skip,limit也是同样的原理;

1

2

3

4

5

{_id:1,name:"a",status:1,num:1}

{_id:2,name:"a",status:0,num:2}

{_id:3,name:"b",status:1,num:3}

{_id:4,name:"c",status:1,num:4}

{_id:5,name:"d",status:1,num:5}

以下是示例: 应用一:统计name的数量和总数

1

2

3

db.collection.aggregate([

  {$group:{_id:"$name",count:{$sum:1},total:{$sum:"$num"}}

]);

应用二:统计status=1的name的数量;

1

2

3

4

db.collection.aggregate([

  {$match:{status:1}},

  {$group:{_id:"$name",count:{$sum:1}}}

]);

应用三:统计name的数量,并且数量为小于2的;

1

2

3

4

db.collection.aggregate([

  {$group:{_id:"$name",count:{$sum:1}},

  {$match:{count:{$lt:2}}}

]);

应用四:统计stauts=1的name的数量,并且数量为1的;

1

2

3

4

5

db.collection.aggregate([

  {$match:{status:1}},

  {$group:{_id:"$name",count:{$sum:1}}},

  {$match:{count:1}}

]);

多列group,根据name和status进行多列

1

2

3

db.collection.aggregate([

  {$group:{_id:{name:"$name",st:"$status"},count:{$sum:1}}}

]);

$project该操作符很简单,

1

2

3

db.collection.aggregate([

  {$project:{name:1,status:1}}

]);

结果是,只有_id,name,status三个字段的表数据,相当于sql表达式 select _id,name,status from collection  $unwind 这个操作符可以将一个数组的文档拆分为多条文档,在特殊条件下有用,本人暂没有进行过多的研究。 以上基本就可以实现大部分统计了,group前条件,group后条件,是重点。

 使用aggregate在MongoDB中查询重复数据记录的方法

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。

aggregate() 方法

MongoDB中聚合的方法使用aggregate()。

语法

aggregate() 方法的基本语法格式如下所示:

1

>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

我们知道,MongoDB属于文档型数据库,其存储的文档类型都是JSON对象。正是由于这一特性,我们在Node.js中会经常使用MongoDB进行数据的存取。但由于Node.js是异步执行的,这就导致我们无法保证每一次的数据库save操作都是原子型的。也就是说,如果客户端连续两次发起同一事件将数据存入数据库,很可能会导致数据被重复保存。高并发的情况下,哪怕是你在代码中已经做了非常严格的校验,例如插入数据前判断要保存的数据是否已经存在,但仍然有可能会出现数据被重复保存的风险。因为在异步执行中,你没有办法保证哪个线程先执行,哪个线程后执行,客户端发起的所有请求并非按我们想象的都是顺序执行的。一个较好的解决办法是在Mongo数据库的所有表中创建唯一索引。事实上,MongoDB默认会为所有表创建一个_id字段的唯一索引(可以取消)。如果你想在Node.js中通过mongoose.schema来自动创建索引,可以参考下面的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var customerSchema = new mongoose.Schema({

cname: String,

cellPhone, String,

sender: String,

tag: String,

behaviour: Number,

createTime: {

type: Date,

default: Date.now

},

current:{

type: Boolean,

default: true

}

}, {

versionKey: false

});

customerSchema.index({cname:1,cellPhone:1,sender:1,tag:1,behaviour:1}, {unique: true});module.exports = mongoose.model('customer', customerSchema);

  上面的model中我们定义了表customer的结构,并通过index()方法在字段cname,cellPhone,sender,tag,behaviour上创建了唯一索引,这样当包含这些字段的重复数据被插入时,数据库会抛出异常。借用mongoose,如果数据库表之前已经被创建并且程序正在运行中,当我们修改model并添加索引,然后重新启动app,只要有对该model的访问,mongoose会自动进行检测并创建索引。当然,如果数据出现重复,则索引创建会失败。此时我们可以通过在创建索引时添加dropDups选项,让数据库自动将重复的数据删除,如:

1

customerSchema.index({cname:1,cellPhone:1,sender:1,tag:1,behaviour:1}, {unique: true, dropDups: true});

  不过据MongoDB的官方说明,自3.0以后的版本不再使用该选项,而且也并没有提供替代的解决办法。貌似官方不再提供创建索引时自动删除重复记录的功能。那如何才能快速有效地找出重复的记录并且删除呢?首先我们要找出这些记录,然后通过remove()方法进行删除。下面的查询语句可以找出给定字段有重复数据的记录:

1

2

3

4

5

6

7

8

9

10

db.collection.aggregate([

{ $group: {

_id: { firstField: "$firstField", secondField: "$secondField" },

uniqueIds: { $addToSet: "$_id" },

count: { $sum: 1 }

}},

{ $match: {

count: { $gt: 1 }

}}

])

 

  替换_id属性的值以指定你想要进行判断的字段。相应地,在Node.js中代码如下:

?

1

2

3

4

5

6

7

8

var deferred = Q.defer();

var group = { firstField: "$firstField", secondField: "$secondField"};

model.aggregate().group({

_id: group,

uniqueIds: {$addToSet: '$_id'},

count: {$sum: 1}

}).match({ count: {$gt: 1}}).exec(deferred.makeNodeResolver());

return deferred.promise;

上述代码使用了Q来替换函数执行中的回调。在Node.js的异步编程中,使用Q来处理回调是个不错的选择。

  下面是返回的结果:

?

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

/* 1 */

{

"result" : [

{

"_id" : {

"cellPhone" : "15827577345",

"actId" : ObjectId("5694565fa50fea7705f01789")

},

"uniqueIds" : [

ObjectId("569b5d03b3d206f709f97685"),

ObjectId("569b5d01b3d206f709f97684")

],

"count" : 2.0000000000000000

},

{

"_id" : {

"cellPhone" : "18171282716",

"actId" : ObjectId("566b0d8dc02f61ae18e68e48")

},

"uniqueIds" : [

ObjectId("566d16e6cf86d12d1abcee8b"),

ObjectId("566d16e6cf86d12d1abcee8a")

],

"count" : 2.0000000000000000

}

],

"ok" : 1.0000000000000000

}

  从结果中可以看到,一共有两组数据相同的记录,所以返回的result数组的长度为2.uniqueIds属性为一个数组,其中存放了重复记录的_id字段的值,通过该值我们可以使用remove()方法来查找并删除对应的数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值