MongoDB入门三步曲2--基本操作(续)--聚合、索引、游标及mapReduce
mongodb 基本操作(续)--聚合、索引、游标及mapReduce
目录
聚合操作
像大多关系数据库一样,Mongodb也提供了聚合操作,这里仅列取常见到的几个聚合操作: Count计数
就像db.collection.find()操作能返回满足条件的记录一样,db.collection.count()返回满足条件的记录数,如下:
db.blog.count({"title":"mongo"})
此命令返回blog集合中title="mongo"的记录数。
Distinct去重 从其定义db.collection.distinct(keyword, condition)可看出,distinct是基于查询条件condition后对结果中的字段keyword去重的,如果不指定condition,则全表查询后对keyworld去重,示例如下:
db.blog.distinct("author")
此命令会把blog集合中不重复的author值返回出来。
有时在查询时想加个条件,如某天2014-02-26发表过文章的作者查询出来,例如下面的命令即可:
db.blog.distinct("author", {"create":"2014-02-26"})
当然还可以统计去重后的数量,因为返回的结果是个js的列表数据,所以只需要统计该列表的长度:
db.blog.disinct("author").length
或者
var authors = db.blog.distinct("author")
authors.length
group分组 使用过关系数据库如mysql的朋友应该知道,在mysql里group是对查询结果按某个字段分组重组后返回查询的数据列表,即返回的数据仍然是一条一条的,不改变原数据库表中的模式,只是对结果中各记录的前后顺序调整了下。
在mongodb中,分组也需要指定使用的字段,称为key,与mysql不同的地方在于需要指定具有相同key的数据存放位置,通常是一个数组,即对结果的initial;最后还要指定把哪些字段放到结果集里。举例如下:
db.blog.group({
"key":{"author":true},
"initial":{"blog":[]},
"$reduce":function(doc,aggregation){
aggregation.blog.push({"title":doc.title,"create":doc.create});
},
"condition":{"create":{$gte:"2014-01-01"}},
"finalize":function(aggregation){
aggregation.count=aggregation.blog.length;
}
})
上面这条命令就根据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,和结果被输出的集合
db.blog.mapReduce(
function(){emit(this.author, {author:this.author,count:1});},
function(key, values){
var result = {"author":"", "count":0};
for(var i = 0, i < values.length; i++) {
result.count += values[i].count;
result.author = values[i].author;
}
return result;
},
{"out":"authorStatistics"}
)
ok,执行完此语句,我们将获得一个新的集合authorStatistics,该集合有两个顶级字段:id和value,其中id的值为key对应的值,value有子json层,格式如result中定义的那般。
{ "_id" : "enjiex", "value" : { "author" : "enjiex", "count" : 1 } }
{ "_id" : "enjiex1", "value" : { "author" : "enjiex1", "count" : 2 } }
下面重点讲解下map及reduce函数:
- map函数:在map函数中调用了emit函数,此函数有两个参数:key和value,其中key是对数据分组的分组字段,value则对应于需要参与计算的值串。例如上面程序中的值串是{name:this.author,count:1},对一条数据经过此方法调用后,就生成了一个中间值,类似这样:
{"key":"enjiex", "value":{"author":"enjiex", "count":1}}
当对所有查询数据经过map方法调用后,会生成类似的中间结果:
[{"key":"enjiex",
"value":[
{"author":"enjiex","count":1},
{"author":"enjiex","count":1}
]
},
{"key":"mc",
"value":{"author":"enjiex","count":1}}
]
可以看出该中间结果是一个集合,是对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()赋给一个变量:
var blogs = db.blog.find()
那么blogs其实就拿到了mongodb返回的游标了。拿到游标后就可以做一些不一样的事情了,最简单的就是mongo shell中执行db.blog.find()效果一样的遍历输出--其实在shell中直接执行db.blog.find()也是返回了游标,只不过shell帮我们遍历输出了数据。
var blogs = db.blog.find()
while(blogs.hasNext()) {
var blog = blogs.next();
print(blog.title)
}
如上代码中,blogs是一个游标,对其调用blogs.hasNext()时,其默认会一次性从数据库中读取100条文档,然后调用blogs.next()时会返回当前位置的记录。
游标操作
除上可以对游标执行hasNext()及next()操作外,还可以对游标执行limit,skip及sort操作:
- limit:限制游标返回的数量,即数据返回上限。
- skip:指定返回结果中的前多少条数据被忽略,如果文档总数小于skip的数值,则返回空集合;
- sort:对得到的集合按指定字段进行排序。
因为在游标上执行以上操作返回的仍然是游标,所以可以在游标上按方法链调用:var blogs = db.blog.find();
blogs.limit(100).skip(10),sort({"author":1,"create":1})上面代码就先限制返回数据量为100,再跳过前10条数据(实际有效的数据是90条了),然后再按author和create排序。
其实上面的代码不是太有效,因为浪费了10条记录的返回,可能下面的写法会更好一些:db.blog.find().skip(10).limit(100).sort({"author":1,"create":1});
这个实际返回的有效数据是100条了。
索引
索引基础
索引是提高数据查询效率的有效手段,通过在常用的字段或字段组合上建立索引,就能很多好提高在该字段或字段组合上的查询效率。简单创建索引的方法:
db.blog.ensuerIndex({"author":1})
通过上面方法,就为blog集合在author字段上建立了顺序索引(author的值分配到一棵B树上,同时维护到原数据记录的引用)。后续再通过author条件查询时,就会先在这棵索引树上查找,并返回最终数据记录。
可是,通过建立索引,查询效率真的提高了吗?在建立索引前后,可通过explain方法看到查询操作遍历的记录数:
db.blog.find({"author":"enjiex"}).explain()
创建完索引后,你会发现在相同的库下面会多出一个system.indexes集合,该集合存放了当前库中所有的索引创建信息。通过db.system.indexes.find()可查看当前库下有哪些索引记录。
唯一索引
假如在blog命令中,每个author只能发表一篇文章,也就是说每个author的值在blog记录中只能出现一次,通过建立唯一索引就可以了。
db.blog.ensuerIndex({"author":1}, {"unique":true})
注意,如果blog集合中已经出现了相同author的多条记录,上面的命令是不会执行成功的,只能先手动删除重复记录后再执行该命令才可以。
组合索引
回到唯一索引前的场景,每个author可能会发表很多篇文章,但我们需要经常查询某位author在某一天的文章。只要在author和create上建立个组合索引就能提高此种查询的效率。
db.blog.ensureIndex({"name":1, "birthday":1})
对于mongodb来说,{"name":1, "birthday":1}和{"birthday":1,"name":1}是不同的索引,因此在建立索引的时候,一定要结合实际查询场景,准确处理。
如果对于查询,不太确定是否使用了自己建立的索引,可以结合explain()查看查询细节。
删除索引
创建了这么多索引,但随着业务变化,某些索引已经不再需要了,如果继续维护会增加很多不必要的开销,对这些无效索引要及时予以清除。
db.blog.dropIndexes("index_name")
调用dropIndexes,指定要删除的索引名称即可。
前面一直没有说索引名称,一句话就能查询出来:
db.blog.getIndexes()
MongoDB入门三步曲2--基本操作(续)--聚合、索引、游标及mapReduce的更多相关文章
- MongoDB入门三步曲1--安装、基本操作
mongodb 基本操作 目录 mongodb安装 mongod启动 mongo shell启动 mongod 停止 mongodb基本操作:CRUD 数据插入 数据查询 数据更新 数据删除 集合删除 ...
- MongoDB入门三步曲3--部署技术:主备、副本集和数据分片
mongodb部署--主备.副本及数据分片 主备复制 副本集 数据分片 主备复制 主备复制是最基本的一种多点部署方案,在读写分离.热备份.数据恢复等方面具有重要作用. 在真实的生产环境,主备库肯定需要 ...
- Membership三步曲之入门篇 - Membership基础示例
Membership 三步曲之入门篇 - Membership基础示例 Membership三步曲之入门篇 - Membership基础示例 Membership三步曲之进阶篇 - 深入剖析Pro ...
- [转]Membership三步曲之入门篇 - Membership基础示例
本文转自:http://www.cnblogs.com/jesse2013/p/membership.html Membership三步曲之入门篇 - Membership基础示例 Members ...
- ASP.NET 安全系列 Membership三步曲之入门篇 - Jesse Liu
Membership 三步曲 ASP.NET 安全系列 Membership三步曲之入门篇 ASP.NET 安全系列 Membership三步曲之进阶篇 ASP.NET 安全系列 Membership ...
- Membership三步曲之进阶篇 - 深入剖析Provider Model
Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...
- 第七章 new的三步曲
这章是本系列文章的重点,这章揭示了js对象的真正本质 看下面的事例 var a = new b(); 等价于 ①var a={}; ②a.__proto__=b.prototype; ③b.call( ...
- SQL Server2005 表分区三步曲(zz)
前言 SQL Server 2005开始支持表分区,这种技术允许所有的表分区都保存在同一台服务器上.每一个表分区都和在某个文件 组(filegroup)中的单个文件关联.同样的一个文件/文件组可以容纳 ...
- 素数问题三步曲_HDOJ2098
偶然间OJ上敲到一题素数问题便查询了相关算法.对于该类问题我个人学习分为三步曲:最笨的方法(TLE毫无疑问)->Eratosthrnes筛选法->欧拉线性筛选法 针对HDOJ2098这道题 ...
随机推荐
- c++ 操作注冊表
1. 注冊表简单介绍 注冊表是为Windows NT和Windows95中全部32位硬件/驱动和32位应用程序设计的数据文件,用于存储系统和应用程序的设置信息.16位驱动在Winnt (W ...
- http的get与post方式下的getParameter获取中文
客户端提交某个中文参数,比如a=中国 (1)如果以GET方式提交,在地址栏中,可以看到参数进行了URL ENCODE,形如:a=%E4%B8%AD%E5%9B%BD.服务端接收到请求,使用reques ...
- eclipse中web项目部署以后jsp的java文件找不到问题(Tomcat配置serverlocations)
我的开发环境:eclipse kepler (4.3)+tomcat7.0.42. 在我想看eclipse中web项目jsp文件被tomcat转换成java以后的java源文件的位置,发现正常情况下的 ...
- vmware mac虚拟机 停在启动界面
前言: 关于vmware安装mac 10.8 可以从参看:http://www.cnblogs.com/zyf2013/p/3888242.html 安装完成以后重新启动,卡在了白苹果启动画面处. 或 ...
- Ruby简介,附带示例程序
Ruby语言是日本人松本行弘于1993年器开始着手研发,经历2年时间,发布了Ruby语言的第一个版本:0.95版. Ruby是一种非常简介的解释性语言,一种纯粹的面向对象编程语言,甚至比Jav ...
- JAVA_eclipse 保留Java文件时自动格式化代码和优化Import
Eclipse 保存Java文件时自动格式化代码和优化Import Eclipse中format代码的快捷方式是ctrl+shift+F,如果大家想保存 java文件的时候 自动就格式化代码+消除不必 ...
- Linux下解决permission denied问题
由于权限问题,在linux下启动tomcat出现权限permission denied 提示解决方法如下: 1.cd 进入tomcat/bin目录 2.运行下面命令 sudo chmod 777 st ...
- 关于egit的日常操作总结
$git fetch -p --prune -p -- remove any remote tracking branches that no longer exist remotely prune的 ...
- 用EnumDisplaySettings获取显示设置信息
LPDEVMODE pMode = new DEVMODE;//开空间 DWORD dwBitsPerPel=0;//每象素所使用的显存位数(Bits) DWORD dwPelsWidth=0;//水 ...
- 解决从linux本地文件系统上传文件到HDFS时的权限问题
当使用 hadoop fs -put localfile /user/xxx 时提示: put: Permission denied: user=root, access=WRITE, inode=& ...