mongodb 基本操作(续)--聚合、索引、游标及mapReduce

目录

聚合操作

像大多关系数据库一样,Mongodb也提供了聚合操作,这里仅列取常见到的几个聚合操作: Count计数
就像db.collection.find()操作能返回满足条件的记录一样,db.collection.count()返回满足条件的记录数,如下:

  1. db.blog.count({"title":"mongo"})

此命令返回blog集合中title="mongo"的记录数。

Distinct去重 从其定义db.collection.distinct(keyword, condition)可看出,distinct是基于查询条件condition后对结果中的字段keyword去重的,如果不指定condition,则全表查询后对keyworld去重,示例如下:

  1. db.blog.distinct("author")

此命令会把blog集合中不重复的author值返回出来。
有时在查询时想加个条件,如某天2014-02-26发表过文章的作者查询出来,例如下面的命令即可:

  1. db.blog.distinct("author", {"create":"2014-02-26"})

当然还可以统计去重后的数量,因为返回的结果是个js的列表数据,所以只需要统计该列表的长度:

  1. db.blog.disinct("author").length
  2. 或者
  3. var authors = db.blog.distinct("author")
  4. authors.length

group分组 使用过关系数据库如mysql的朋友应该知道,在mysql里group是对查询结果按某个字段分组重组后返回查询的数据列表,即返回的数据仍然是一条一条的,不改变原数据库表中的模式,只是对结果中各记录的前后顺序调整了下。
在mongodb中,分组也需要指定使用的字段,称为key,与mysql不同的地方在于需要指定具有相同key的数据存放位置,通常是一个数组,即对结果的initial;最后还要指定把哪些字段放到结果集里。举例如下:

  1. db.blog.group({
  2.   "key":{"author":true},
  3.   "initial":{"blog":[]},
  4.   "$reduce":function(doc,aggregation){
  5.     aggregation.blog.push({"title":doc.title,"create":doc.create});
  6.   },
  7.   "condition":{"create":{$gte:"2014-01-01"}},
  8.   "finalize":function(aggregation){
  9.     aggregation.count=aggregation.blog.length;
  10.   }
  11. })

上面这条命令就根据author分组,并每条记录中的title和create字段放到分组聚合信息中。
在定义$reduce函数时,需要指定两个参数:

  • doc 当前操作的记录
  • aggregation 按key分组后的聚合信息
    对于$reduce,除了上面可以把查出来的信息添加进去还能执行很多其他操作,例如计算某个字段的统计值等。场景不同,方法也各异。
    group操作中的condition及finalize参数不是必须的,但在有时候也是非常有用的:
  • condition 对group操作提供条件过滤,如上只对create >= '2014-01-01'的记录进行分组统计
  • finalize 会在每组文档分组完成后触,如上命令中,在每个分组完成后统计各分组的数量,并输出到count字段。

MapReduce

MapReduce是分布式计算中的一种编程模型,其中指定一个map函数,用于映射每条数据到不同分组,reduce则对各组进行计算。因此在mongodb中,MapReduce也可以看作是聚合函数的一种复杂场景。
最简单的mapReduce调用需要三个参数:map,reduce,和结果被输出的集合

  1. db.blog.mapReduce(
  2.   function(){emit(this.author, {author:this.author,count:1});},
  3.   function(key, values){
  4.     var result = {"author":"", "count":0};
  5.     for(var i = 0, i < values.length; i++) {
  6.       result.count += values[i].count;
  7.       result.author = values[i].author;
  8.     }
  9.     return result;
  10.   },
  11.   {"out":"authorStatistics"}
  12. )

ok,执行完此语句,我们将获得一个新的集合authorStatistics,该集合有两个顶级字段:id和value,其中id的值为key对应的值,value有子json层,格式如result中定义的那般。

  1. { "_id" : "enjiex", "value" : { "author" : "enjiex", "count" : 1 } }
  2. { "_id" : "enjiex1", "value" : { "author" : "enjiex1", "count" : 2 } }

下面重点讲解下map及reduce函数:

  • map函数:在map函数中调用了emit函数,此函数有两个参数:key和value,其中key是对数据分组的分组字段,value则对应于需要参与计算的值串。例如上面程序中的值串是{name:this.author,count:1},对一条数据经过此方法调用后,就生成了一个中间值,类似这样:

    1. {"key":"enjiex", "value":{"author":"enjiex", "count":1}}

当对所有查询数据经过map方法调用后,会生成类似的中间结果:

  1. [{"key":"enjiex",
  2. "value":[
  3.     {"author":"enjiex","count":1},
  4.     {"author":"enjiex","count":1}
  5.   ]
  6. },
  7. {"key":"mc",
  8. "value":{"author":"enjiex","count":1}}
  9. ]

可以看出该中间结果是一个集合,是对emit中的value按key分组后的数据。

  • reduce函数:有两个参数:key和values,其中key的含义与emit中的含义相同,values则对应于map方法生成的中间结果。在reduce函数中对中间结果再做处理,如上程序中,只对中间结果中的count字段做了求和计算。
  • 结果输出:在mapReduce方法中最后一个参数:{"out":"authorStatistics"}指出了结果集的输出位置为:authorStatistics,执行完调用后会在当前库下生成一个名为authorStatistics的集合。

游标

游标概念
在mongo shell中执行db.blog.find(),时立即把结果输出到shell窗口中,但是如果我们把db.blog.find()赋给一个变量:

  1. var blogs = db.blog.find()

那么blogs其实就拿到了mongodb返回的游标了。拿到游标后就可以做一些不一样的事情了,最简单的就是mongo shell中执行db.blog.find()效果一样的遍历输出--其实在shell中直接执行db.blog.find()也是返回了游标,只不过shell帮我们遍历输出了数据。

  1. var blogs = db.blog.find()
  2. while(blogs.hasNext()) {
  3.   var blog = blogs.next();
  4.   print(blog.title)
  5. }

如上代码中,blogs是一个游标,对其调用blogs.hasNext()时,其默认会一次性从数据库中读取100条文档,然后调用blogs.next()时会返回当前位置的记录。
游标操作
除上可以对游标执行hasNext()及next()操作外,还可以对游标执行limit,skip及sort操作:

  • limit:限制游标返回的数量,即数据返回上限。
  • skip:指定返回结果中的前多少条数据被忽略,如果文档总数小于skip的数值,则返回空集合;
  • sort:对得到的集合按指定字段进行排序。
    因为在游标上执行以上操作返回的仍然是游标,所以可以在游标上按方法链调用:

    1. var blogs = db.blog.find();
    2. blogs.limit(100).skip(10),sort({"author":1,"create":1})

    上面代码就先限制返回数据量为100,再跳过前10条数据(实际有效的数据是90条了),然后再按author和create排序。
    其实上面的代码不是太有效,因为浪费了10条记录的返回,可能下面的写法会更好一些:

    1. db.blog.find().skip(10).limit(100).sort({"author":1,"create":1});

    这个实际返回的有效数据是100条了。

索引

索引基础
索引是提高数据查询效率的有效手段,通过在常用的字段或字段组合上建立索引,就能很多好提高在该字段或字段组合上的查询效率。简单创建索引的方法:

  1. db.blog.ensuerIndex({"author":1})

通过上面方法,就为blog集合在author字段上建立了顺序索引(author的值分配到一棵B树上,同时维护到原数据记录的引用)。后续再通过author条件查询时,就会先在这棵索引树上查找,并返回最终数据记录。
可是,通过建立索引,查询效率真的提高了吗?在建立索引前后,可通过explain方法看到查询操作遍历的记录数:

  1. db.blog.find({"author":"enjiex"}).explain()

创建完索引后,你会发现在相同的库下面会多出一个system.indexes集合,该集合存放了当前库中所有的索引创建信息。通过db.system.indexes.find()可查看当前库下有哪些索引记录。

唯一索引
假如在blog命令中,每个author只能发表一篇文章,也就是说每个author的值在blog记录中只能出现一次,通过建立唯一索引就可以了。

  1. db.blog.ensuerIndex({"author":1}, {"unique":true})

注意,如果blog集合中已经出现了相同author的多条记录,上面的命令是不会执行成功的,只能先手动删除重复记录后再执行该命令才可以。

组合索引
回到唯一索引前的场景,每个author可能会发表很多篇文章,但我们需要经常查询某位author在某一天的文章。只要在author和create上建立个组合索引就能提高此种查询的效率。

  1. db.blog.ensureIndex({"name":1, "birthday":1})

对于mongodb来说,{"name":1, "birthday":1}和{"birthday":1,"name":1}是不同的索引,因此在建立索引的时候,一定要结合实际查询场景,准确处理。
如果对于查询,不太确定是否使用了自己建立的索引,可以结合explain()查看查询细节。
删除索引
创建了这么多索引,但随着业务变化,某些索引已经不再需要了,如果继续维护会增加很多不必要的开销,对这些无效索引要及时予以清除。

  1. db.blog.dropIndexes("index_name")

调用dropIndexes,指定要删除的索引名称即可。
前面一直没有说索引名称,一句话就能查询出来:

  1. db.blog.getIndexes()

MongoDB入门三步曲2--基本操作(续)--聚合、索引、游标及mapReduce的更多相关文章

  1. MongoDB入门三步曲1--安装、基本操作

    mongodb 基本操作 目录 mongodb安装 mongod启动 mongo shell启动 mongod 停止 mongodb基本操作:CRUD 数据插入 数据查询 数据更新 数据删除 集合删除 ...

  2. MongoDB入门三步曲3--部署技术:主备、副本集和数据分片

    mongodb部署--主备.副本及数据分片 主备复制 副本集 数据分片 主备复制 主备复制是最基本的一种多点部署方案,在读写分离.热备份.数据恢复等方面具有重要作用. 在真实的生产环境,主备库肯定需要 ...

  3. Membership三步曲之入门篇 - Membership基础示例

    Membership 三步曲之入门篇 - Membership基础示例 Membership三步曲之入门篇 -  Membership基础示例 Membership三步曲之进阶篇 -  深入剖析Pro ...

  4. [转]Membership三步曲之入门篇 - Membership基础示例

    本文转自:http://www.cnblogs.com/jesse2013/p/membership.html Membership三步曲之入门篇 - Membership基础示例   Members ...

  5. ASP.NET 安全系列 Membership三步曲之入门篇 - Jesse Liu

    Membership 三步曲 ASP.NET 安全系列 Membership三步曲之入门篇 ASP.NET 安全系列 Membership三步曲之进阶篇 ASP.NET 安全系列 Membership ...

  6. Membership三步曲之进阶篇 - 深入剖析Provider Model

    Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...

  7. 第七章 new的三步曲

    这章是本系列文章的重点,这章揭示了js对象的真正本质 看下面的事例 var a = new b(); 等价于 ①var a={}; ②a.__proto__=b.prototype; ③b.call( ...

  8. SQL Server2005 表分区三步曲(zz)

    前言 SQL Server 2005开始支持表分区,这种技术允许所有的表分区都保存在同一台服务器上.每一个表分区都和在某个文件 组(filegroup)中的单个文件关联.同样的一个文件/文件组可以容纳 ...

  9. 素数问题三步曲_HDOJ2098

    偶然间OJ上敲到一题素数问题便查询了相关算法.对于该类问题我个人学习分为三步曲:最笨的方法(TLE毫无疑问)->Eratosthrnes筛选法->欧拉线性筛选法 针对HDOJ2098这道题 ...

随机推荐

  1. MySQL 性能监控 4 大指标

    [编者按]本文作者为 John Matson,主要介绍 mysql 性能监控应该关注的 4 大指标. 文章系国内 ITOM 管理平台 OneAPM 编译呈现.    MySQL 是什么? MySQL  ...

  2. WCF的ABC

    首先: WCF的全称是Windows Communication Foundation,Windows通信基础的意思,是Microsoft为构建面向服务的应用程序而提供的统一编程模型,它整合.NET平 ...

  3. cocos2dx 公告效果

    第一种方法: http://blog.csdn.net/jackystudio/article/details/12991977 [玩转cocos2d-x之十六]滚动字幕和公告 第二种方法: http ...

  4. visual studio 2013 有效序列号

    还没试过,可以参考下! visual studio 2013 有效序列号: BWG7X-J98B3-W34RT-33B3R-JVYW9

  5. FBReaderJ 编译Jni

    最近要做一个电子书项目用到FBReaderJ 第一步,也是最难的一步,要编译他的Jni 文件,对于android开发还是小白的我,只能说难!好难!非常难!,于是乎百度了一下,找到几篇有价值性的文章 第 ...

  6. Fixflow引擎解析(一)(介绍) - Fixflow开源流程引擎介绍

    Fixflow引擎解析(四)(模型) - 通过EMF扩展BPMN2.0元素 Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件 Fixflow引擎解析(二)(模型) - BPMN ...

  7. C#基础篇03

    1:不管是实参还是形参,都在内存中开辟空间. 2:写一个方法,它的功能一定要单一,方法中最忌讳的就是出现提示用户输入的字眼. 3:out参数 如果在一个方法中,返回多个类型相同的值时,可以考虑返回一个 ...

  8. JAVA_JDBC

    测试类: 1 import java.util.ArrayList; import java.util.List; /** * 创建数据库: * 1.加载驱动 * Class.forName(&quo ...

  9. c数组和指针的理解

    #include<stdio.h> int main(void) { ,,,,}; ); printf(,*(p-)); // ] = &a; √ // ] = a; × // ] ...

  10. 实现虚拟机上面的linux系统和windows主机的通信

    一:配置静态ip 1:使用startx命名切换到图形化用户界面: 2:在“开始”——“系统设置”——“网络”——eth0 将通过dchp自动获取ip改为静态绑定ip ip地址:填入你要给的ip,需要与 ...