Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1649510
  • 博文数量: 1481
  • 博客积分: 26784
  • 博客等级: 上将
  • 技术积分: 17045
  • 用 户 组: 普通用户
  • 注册时间: 2011-07-12 09:22
文章分类

全部博文(1481)

文章存档

2014年(10)

2013年(353)

2012年(700)

2011年(418)

分类: 系统运维

2012-07-03 09:49:38

HELLO,雷爹丝 and 砖头们,不知道大家这两天过得怎么样,天气热了,注意避暑降温哦,写代码之余,别忘了多喝水,注意身体!

好,今天我们来接着上文的内容讲解MongoDB的聚合、游标和索引!


不知道大家对上文中的”三个男人和一个女人的故事“是否感兴趣呢,本文咱们就来研究”唐僧师徒“的故事!

首先来介绍一下主人公:

主人公一:唐僧 --- 丛浩 饰

主人公二:悟空 --- 李强强 饰

主人公三:八戒 --- 张礼军

主人公四:沙师弟 --- 闫海静

好,接下来我们来看在他们之间发生的爱恨情仇....

  1. {
  2. "name":"丛浩",
  3. "alias":"唐僧",
  4. "age":21,
  5. "meritorious":"途嘴说死牛魔王的看守!手段极其残忍!"
  6. }
  7. {
  8. "name":"李强强",
  9. "alias":"悟空",
  10. "age":20,
  11. "meritorious":"亲手送唐僧上西天!"
  12. }
  13. {
  14. "name":"张礼军",
  15. "alias":"八戒",
  16. "age":19,
  17. "meritorious":"霸占高老庄的高翠兰女士为妻!"
  18. }
  19. {
  20. "name":"闫海静",
  21. "alias":"沙师弟",
  22. "age":18,
  23. "meritorious":"协调组织内部矛盾!"
  24. }
  25. {
  26. "name":"张涛(又名涛哥)",
  27. "alias":"白龙马",
  28. "age":2,
  29. "meritorious":"哥不是一匹骏马,但哥也不是普通的毛驴!"
  30. }

好,我们先来查看唐僧师徒总共几人?

这就用到了MongoDB聚合当中count方法。

count是最简单的聚合工具,返回集合中的文档数量:

>db.user.count()
5


不论我们的集合有多大,都能很快返回总的文档数量!

count方法还能传递查询,Mongo会计算查询结果的数量:

db.user.count({"name":"丛浩"})

但是增加查询条件会使得count变慢。

现在我们来计算唐僧师徒的所有年龄分布:


这时我们用到了distinct聚合:


distinct用来找出给定键的所有不同值,使用时必须指定集合和键。


db.runCommand({"distinct":"user","key":"age"})


上式运行结果中,"values"中是一个数组,里面包含"age"键的所有不同值!

好,上面就是我们经常用到的聚合函数,是不是很简单,接下来咱们MongoDB的游标:

MongoDB使用游标来返回find的执行结果,客户端对游标的实现通常能够对最终结果进行有效的控制,

可以限制结果的数量,略过部分结果,根据任意方向任意键的组合对结果进行各种排序,或是执行其他一些功能强大的操作。

下面我们将查询的结果分配给一个变量cursor

var cursor = db.user.find();

此时cursor并没有获取到user中的文档,而是声明一个查询结构,等我们需要的时候通过for或者next()一次性加载过来,

然后让游标逐行读取,枚举完成后,游标销毁,再获取cursor的时候,就没有数据了。

要迭代结果,不一次性输出,我们就可以使用游标当中过得next方法。也可以使用hasNext来查看有没有其他结果。

>while(cursor.hasNext()){
... obj = cursor.next();
... }

cursor.hasNext()检查是否有后续结果存在,然后用cursor.next()将其获取。

游标类还实现了迭代器接口,所以可以在foreach循环中使用。

>var cursor = db.user.find();
>cursor.forEach(function(x){
... print(x.name);
... });
丛浩
李强强
张礼军
闫海静
张涛(又名涛哥)

当调用find的时候,shell并不立即查询数据库,而是等待真正开始要求获得结果的时候才发送查询,这样在

执行之前可以给查询附加额外的选项。几乎所有游标对象的方法都返回游标本身,这样就可以按任意顺序组

成方法链。

例如,下面几种表达式等价的:

>var cursor = db.user.find().sort({"name":1}).limit(1).skip(1);
>var cursor = db.user.find().limit(1).sort({"name":1}).skip(1);
>var cursor = db.user.find().skip(1).limit(1).sort({"name":1});

此时,查询还没有执行,所有这些函数都只是构造查询。现在,假设我们执行如下操作:

>cursor.hasNext()

这时,查询被发往服务器。shell立刻获取前100个结果或者前4MB数据(两者之中较小者),这样下次调用next或者hasNext时

就不必兴师动众跑到服务器上去了。客户端用光了第一组结果,shell会再一次联系数据库,并要求更多的结果。这个过程一直

会持续到游标耗尽或者结果全部返回。

从上面的查询语句中可以看出,在MongoDB中我们可以使用limit、skip和sort

他们的作用与sql语句的功能是一样的,例如:

db.user.find().sort({"name":1}).limit(1).skip(1);

这条语句中,limit(1)表示只返回结果集中的一条。limit指定的是上限,不是下限。

skip和limit类似:

上面的操作会略过前一个匹配的文档,然后返回余下的文档。如果集合里面能匹配的文档少于1个,则不会返回任何文档。

sort用一个对象作为参数:一组键/值对,键对应文档的键名,值代表排序的方向。
排序方向可以是1(升序)或者-1(降序)。如果指定了多个键,则按照多个键的顺序逐个排序。

例如:

要按照”name“升序及”age“降序排序,可以这样写:

db.user.find().sort({"name":1,"age":-1})

这三个方法可以组合使用,对于分页非常有效。

看待游标有两种角度:客户端的游标以及客户端游标表示的数据库游标。前面讨论的都是客户端的游标。

在服务器端,游标消耗内存和其他资源。游标遍历尽了结果以后,或者客户端发来消息要求终止,数据库将会释放这些资源。

释放的资源可以被数据库换做他用,这是非常有益的,所以要尽量保证尽快释放游标。

还有一些情况导致游标终止,首先,当游标完成匹配结果的迭代时,它会清除自身。另外,当游标在客户端已经不在作用域了,驱动会

向服务器发送专门的消息,让其销毁游标。最后,即便用户也没有迭代完所有结果,并且游标也还在作用域中,10分钟不适用,数据库

游标也会自动销毁。

好,游标先说到这里,我们来看看索引,大家都知道,索引就是用来加速查询的。

数据库的索引与数据的索引类似:有了索引就不需要翻遍整本书,数据库则可以直接在索引中查找,使得查找速度提高几个

数量级。

下面我们对”name“建立索引。创建索引要使用ensureIndex方法:

db.user.ensureIndex({"name":1})

对于同一个集合,同样的索引只需要创建一次,反复创建是徒劳的。

传递给ensureIndex的文档其形式与传递给sort的文档形式一样:一组值为1或-1的键,表示索引创建的方向。若索引只有一个键,则方向无关紧要,

若有多个键,就得考虑索引的方向问题了,例如我们举的上述例子,

db.user.find().sort({"name":1,"age":-1})

对于这样一个查询,我们就需要创建这样的索引:

db.ensureIndex({"name":1,"age":-1})

创建索引的缺点就是每次插入、更新和删除时都会产生额外的开销,这是因为数据库不但需要执行这些操作,还要将

这些操作在集合的索引中标记。因此,要尽可能减少创建索引。

在MongoDB的索引中,ensureIndex方法可以指定索引的名称,可以使用ensureIndex的第二个参数来设定:

db.user.ensureIndex({"age":1},{"name":"age_index"})

这样就创建了名称为age_index的索引。

查看索引我们在当前库的system.indexes集合中来查看:

db.system.indexes.find()

我们还可以创建唯一索引:

db.user.ensureIndex({"name":1},{"unique":true})

这样,我们就对name键创建了唯一索引,再执行插入name相同的文档时,数据库会提示错误。

当为已有的集合创建索引,可能有些值已经有重复了,这样创建唯一索引的时候就会创建失败!

所以首先要先对文档中的重复值删除,而这些工作ensureIndex方法也都可以为我们来做:

db.user.ensureIndex({"name":1},{"unique":true,"dropDups":true})

但是这种做法有些不安全,如果数据很重要的话,这样做就会不合适,所以还是写个脚本做个预处理比较稳妥。

接下来,我们来看怎样来删除一条索引:

例如我们要删刚才创建的索引:

db.user.dropIndex({"name":1})

这样就能把这条索引删除掉,使用方法dropIndexes方法将删除集合当中所有的索引。

好,本文就先介绍到这里,夜深了,大家也注意休息啊,下文将给大家搞定PHP连接MongoDB数据库进行操作,完成唐僧师徒的取经大业!
原文地址:
阅读(465) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~