关于MongoDB的group用法

摘要:
当我读蒙哥的书时,我看到了聚合的一章。它讨论了群体的功能。事实上,正如书中提到的,MongoDB中的组与SQL中的groupby非常相似。但我分析自己。可能因为Mongo中的组以不同的形式使用,并且使用js语法,所以我似乎不知道如何使用这个组。这里有一个具体的例子来详细解释Mongo的组用法。Mongo代码如下:1˃db.posts。组({23…“doc.tags”是指原始集合文档中“tags”键的多值数组。

之前在看Mongo的书时,看到了聚合这章。其中谈到了group这个功能,其实正如书中所说,MongoDB中的group和SQL中的group by是很相似的,但我自我分析,可能由于Mongo中的group的使用形式不同,而且使用的是js语法,所以导致咋一看上去不明白这个group怎么用。下面通过具体的一个例子来详细说明Mongo的group用法。

我们平常所用的博客,每天会有很多人发博客,每篇文章中都有多个标签,现在要找出每天最热点的标签。首先,我们可以按天分组,将每天每一标签的计数给统计出来。

我们可以简单地假设集合中文档的结构如下:

1 {“title” : “java sun”, “author” : “jk”, “day” : “2012-12-14”, “tags” : [“java”, “nosql”, “spring”]}
2 
3 {“title” : “SSH2的整合”, “author” : “cj”, “day” : “2012-5-10”, “tags” : [“struts2”, “hibernate”, “spring”]}
4 
5 {“title” : “C#的高级用法”, “author” : “zt”, “day” : “2012-4-3”, “tags” : [“C#”, “SQL”]}
6 
7 {“title” : “PHP Mongo”, “author” : “lx”, “day” : “2012-12-14”, “tags” : [“PHP”, “nosql”, “mongo”]}
8 
9

Mongo代码如下:

 1 >db.posts.group({
 2 
 3 … “key” : {“day” : true},
 4 
 5 … “initial” : {“tags” : {}},
 6 
 7 … “$reduce” : function (doc, prev) {
 8 
 9for (i in doc.tags) {
10 
11if (doc.tags[i] in prev.tags) {
12 
13 …                       prev.tags[doc.tags[i]]++;
14 
15 …              }
16 
17else
18 
19 …              {
20 
21 …                       prev.tags[doc.tags[i]] = 1;
22 
23 …              }
24 
25 …     }
26 
27 … }
28 
29 })

现在我们来逐行分析上述的代码意思。

1 “key” : {“day” : true}

“key”表示集合数据分组的依据。这里我们指定了“day”键,那么会根据集合文档中的发布博客时间来进行分组。那有人会问,那个“true”有什么意义?如果指定了{“day” : true},那么在分组的结果中就会显示每组“day”的键值。

1 “initial” : {“tags” : {}}

这个大家可能会迷惑,initial是初始化的意思,分组为什么还要初始化呢?SQL好像也没有类似的概念啊。这个就是两者不同的地方了。这里是为了初始化累加器的键值,我们可以把这个所谓的“累加器”当作一个文档,这个文档中存放是的在分组过程中收集、计算出的信息,不一定是集合文档中的原信息,这里需要注意。“tags”表示每个分组中第一个文档对应调用“$reduce”指定函数时的参数初始化,后续该分组的文档对应调用“$reduce”指定函数时,会不断“累加”,保留住每次对“tags”所做的更新结果。有人可能不理解上面一句在讲什么,可以结合下面的解释来理解。

1 “$reduce” : function(doc, prev) { … }

看到这里可能有些人就彻底崩溃了,我也是,不就分个组吗,怎么把函数都整出来,还是js的。其实我们可以把这里的函数理解为分组过程,在分组的过程中,我们又人为地添加了一些操作,比如信息收集、汇总、统计等等。“doc”代表分组过程中的每一个集合中的文档,而“prev”则代表“累加器文档”的累加状态,当一个分组的组员划分完毕时,这个“prev”文档中的键值对的最终状态就是我们想要的结果。这里需要注意函数体中的“prev.tags”和“doc.tags”是不同的,不信我们可以看结果。

按照上面的代码分类后得到的结果如下所示:

1 {“day” : “2012-12-14”, “tags” : {“java” : 1, “nosql” : 2, “spring” : 1, “mongo” : 1}}
2 
3 {“day” : “2012-5-10”, “tags” : {“struts2” : 1, “hibernate” : 1, “spring” : 1}}
4 
5 {“day” : “2012-4-3”, “tags” : {“C#” : 1, “SQL” : 1}}

看出不同了吗?上面的结果中,“tags”键的值是一个内嵌文档,而这对应的就是“pre.tags”。“doc.tags”表示的原集合文档中“tags”键的多值数组。

我们要找的是每天最热门的标签,显然我们上面的结果还有些多余的信息,那么就让我们使用“finalize”键来进行精简吧,还是先看代码。

 1 >db.posts.group({
 2 
 3 … “key” : {“day” : true},
 4 
 5 … “initial” : {“tags” : {}},
 6 
 7 … “$reduce” : function (doc, prev) {
 8 
 9for (i in doc.tags) {
10 
11if (doc.tags[i] in prev.tags) {
12 
13 …                       prev.tags[doc.tags[i]]++;
14 
15 …              }
16 
17else
18 
19 …              {
20 
21 …                       prev.tags[doc.tags[i]] = 1;
22 
23 …              }
24 
25 …     }
26 
27 … },
28 
29 … “finalize” : function (prev) {
30 
31var mostPopular = 0;
32 
33for (i in prev.tags) {
34 
35if (prev.tags[i] > mostPopular) {
36 
37 …                       prev.tag = i;
38 
39 …                       mostPopular = prev.tags[i];
40 
41 …              }
42 
43 …     }
44 
45delete prev.tags;
46 
47 … }
48 
49 })

“finalize”附带的函数,在每个分组结果传递到客户端之前会被调用一次,从而可以对分组结果进行“修剪”。从上面我们可以看出,原先结果中的“tags”键值给删去了,加上了一个“tag”键值,所以最终的结果如下:

1 {“day” : “2012-12-14”, “tag” : { “nosql”}
2 
3 {“day” : “2012-5-10”, “tags” : {“struts2” : 1}}
4 
5 {“day” : “2012-4-3”, “tags” : {“C#” : 1}}

在举个例子,本来我在刚看过上面的那段代码后,想在自己写的示例系统——用户管理系统中,用PHP结合group功能实现用户的爱好分类统计,但由于刚开始我也一直处于迷糊状态,所以一直没想出解决办法,今天通过仔细阅读PHP开发手册的例子,终于实现了这一功能。下面是用户集合中的文档结构:

1 { "_id" : ObjectId("50f0c0ca323365ec0c000006"), "username" : "大胖", "age" : 23, "birthday" : ISODate("1989-10-29T16:00:00Z"), "interest" : [ "篮球", "足球", "乒乓球", "高尔夫球" ] }
2 
3 { "_id" : ObjectId("50f35b6f323365f00c000001"), "username" : "六福", "age" : 24, "birthday" : ISODate("1988-05-05T15:00:00Z"), "interest" : [ "乒乓球", "篮球" ] }
4 
5 { "_id" : ObjectId("50f37257323365ec0c000000"), "username" : "七喜", "age" : 30, "birthday" : ISODate("1984-09-30T16:00:00Z"), "interest" : [ "足球", "橄榄球" ] }
6 
7

那么如何用group方法来统计出喜爱篮球的有多少人,喜爱足球的有多少人等信息呢?现在我就用Mongo——PHP驱动提供的API来实现group的功能。有兴趣的人可以根据下面的代码简练出Mongo语法的代码。

 1 /*
 2 
 3      * 统计爱好分类信息
 4 
 5      */
 6 
 7     public function groupByInterest()
 8 
 9     {
10 
11        $key = array();   //$key没有指定分组依据,那么所有文档认为属于同一组
12 
13        $initial = array('interests' => array());
14 
15        $reduce = 'function(obj, prev) {
16 
17            for (i in obj.interest)
18 
19            {
20 
21               if (obj.interest[i] in prev.interests)
22 
23               {
24 
25                   prev.interests[obj.interest[i]]++;
26 
27               }
28 
29               else
30 
31               {
32 
33                   prev.interests[obj.interest[i]] = 1;
34 
35               }
36 
37            }
38 
39        }';
40 
41        $g = $this->users->group($key, $initial, $reduce);
42 
43        return $g['retval'];
44 
45     }

注意上面代码中的“$key = array();”,我没有指定分组依据的键名,有人会问,这样不会错吗?答案是不会的。当我们没有指定分组依据的键名时,那么集合中所有文档被认为属于同一组。我之所以这么说是我从分组结果中分析出来的,但PHP开发手册上的例子说的是如果在这种情况下,集合中的文档是各自独立成为一组的,但我坚持自己的说法,因为我看得到的结果是这样的,如果有哪位朋友可以给个确切说明的话,欢迎指正。

免责声明:文章转载自《关于MongoDB的group用法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇scribefire 多博客管理利器 安装详解mybatis mapper学习1-mapper.xml映射文件生成:Mybatis Generator的下载-安装-配置-运行下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

利用MongoDB进行地理坐标查询

BS的应用在生活中已经非常常见,我们打车,叫外卖,查个地图之类的都会查询附近的相关坐标位置,mongodb提供了原生的二维地图查询,极大地方便了大家的开发。 假定我们有一个定义了位置信息的集合location,给定a,b,c,d节点 db.location.find() { "_id" : "A", "position" : [ 0, 10 ] } { "...

MongoDB自动删除过期数据--TTL索引

前序: 由于公司业务需求,对于3个月前的过期数据需要进行删除动作,以释放空间和方便维护本来想的是使用crontab写个脚本定时执行,但是看到Mongo本身就有自动删除过期数据的功能,所以还是用一下吧这个方法就是使用TTL索引,后续我再写一个脚本定时删除的任务介绍:TTL索引是MongoDB中一种特殊的索引, 可以支持文档在一定时间之后自动过期删除,目前TT...

Node.JS + MongoDB技术浅谈

看到一个Node.JS + MongoDB的小例子,分享给大家,魔乐科技软件学院(www.mldnjava.cn)的讲座 Node.JS + MongoDB技术讲座          云计算 +大数据 = 未来。          在中国的云计算上基本上是一个概念,个人感觉与当初的SOA没有太大的区别,空泛的理论。          中小型开发的未来...

Mongodb查询

https://www.cnblogs.com/t2xingzhe/p/3555268.html 数据库查询 db.ser.find()#select * from ser;只输出id和time字段,第一个参数为查询条件,空代表查询所有db.ser.find({"name" : scott})#select * from ser where name='s...

Python的几个爬虫代码整理(网易云、微信、淘宝、今日头条)

整理了一下网易云歌曲评论抓取、分析好友信息抓取、淘宝宝贝抓取、今日头条美图抓取的一些代码 抓取网易云评论 进入歌曲界面: http://music.163.com/#/song?id=453185824 找到如下的数据源: 贴一段Lyrichu的代码: (运行环境为P2.7) # -*- coding: utf-8 -*- # @Time   : ...

mongodb进阶一之高级查询

上篇文章我们讲了mongodb的crud基本操作 http://blog.csdn.net/stronglyh/article/details/46812579 这篇我们来说说mongodb的进阶--------------高级查询 一:各种查询 1:条件操作符 <, <=, >, >= 这个操作符就不用多解释了,最经常使用也是...