HELLO,雷爹丝 and
砖头们,不知道大家这两天过得怎么样,天气热了,注意避暑降温哦,写代码之余,别忘了多喝水,注意身体!
好,今天我们来接着上文的内容讲解MongoDB的聚合、游标和索引!不知道大家对上文中的”三个男人和一个女人的故事“是否感兴趣呢,本文咱们就来研究”唐僧师徒“的故事!首先来介绍一下主人公:主人公一:唐僧 --- 丛浩 饰主人公二:悟空 --- 李强强
饰主人公三:八戒 --- 张礼军 饰主人公四:沙师弟 ---
闫海静 饰好,接下来我们来看在他们之间发生的爱恨情仇....
- {
- "name":"丛浩",
- "alias":"唐僧",
- "age":21,
- "meritorious":"途嘴说死牛魔王的看守!手段极其残忍!"
- }
- {
- "name":"李强强",
- "alias":"悟空",
- "age":20,
- "meritorious":"亲手送唐僧上西天!"
- }
- {
- "name":"张礼军",
- "alias":"八戒",
- "age":19,
- "meritorious":"霸占高老庄的高翠兰女士为妻!"
- }
- {
- "name":"闫海静",
- "alias":"沙师弟",
- "age":18,
- "meritorious":"协调组织内部矛盾!"
- }
- {
- "name":"张涛(又名涛哥)",
- "alias":"白龙马",
- "age":2,
- "meritorious":"哥不是一匹骏马,但哥也不是普通的毛驴!"
- }
好,我们先来查看唐僧师徒总共几人?这就用到了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数据库进行操作,完成唐僧师徒的取经大业!
原文地址: