第二节的时候已经初步了解MongoDB的增删改查操作,这节主要学习MongoDB的进阶语法操作:聚合、游标、索引等。很多人都觉得应该立马学习驱动、客户端的实例应用等,其实,我是很不推荐这种走马观花的跳级形式,驱动只不过是一层外壳,我们终究还是要了解它的本质。就好比如一个美女穿着漂亮的衣服,你难道只欣赏衣服?而不想更加深入的了解美女么?#¥%(*&)!~#%&(邪恶了- 。-)......好了,在学习新的知识点前,我们详细温习补充下前面查询的一些操作。
① "\$lt"、"\$lte"、"\$gt"、"\$gte"就是全部的比较操作符,分别对应<、<=、>、>=。可以将其组合起来以便查找范围的值。
{ "age" : { $gt : 1 , $lt : 10 } }(查找年龄大于1岁小于10岁的数据)
这样的范围查询对日期尤为有用。对于文档的键值不等于某个特定值的情况,就要使用另外一个条件操作符"$ne"了,它表示不等于。
②OR查询有两种方式:"\$in"和"\$or"
{ "age" : { $in : [ 1 , "haha" , ... ] } }与之相反的是"\$nin";
{ $or : [ { "name" : "zsl2" },{ "age" : 9 } ] },条件可嵌套。
③"\$not"是元条件句,及可以用在任何其他条件之上,用来查找那些与特定模式不符的文档。
"\$mod"取模运算符,它会将查询的值除以第一个给定值,若余数等于第二个给定值则返回该结果。如:
{ "age" : { $mod : [ 3,1 ] } },结果将会显示年龄是1,4,7,10,13...的文档。
{ "age" : { $not :{ $mod : [ 3,1 ] } } },结果将会显示年龄是2,3,5,6,8...的文档。
注意条件语句是内层文档的键,而修改器是外层文档的键。一个键可以有多个条件,但不能有多个更新修改器!如:
{ $inc : { "age" : 1 } , { $set : { "age" : 10 } } }是不允许存在的
④"$exists"条件判定键值是否存在 { "name" : /nick/i }系统可以接受正则表达式(i),但不一定要有。MongoDB可以为前缀型正则表达式(如:/^nick/)查询创建索引,这种类型的查询会非常高效。
⑤"\$all"通过多个元素来匹配一组元素
{ "love_city" : { \$all : [ "XiaMen" , "XianYou" ] } }------元素顺序无关紧要
切记,当使用完整的数组精确匹配时,对于有缺少或者冗余元素的情况就不大管用了。精确匹配应严格按照数组顺序执行查找。要是想查询数组指定位置的元素,我们可以使用key.index语法来指定下标:
{ "love_city.2" : "XianYou" }
匹配喜爱城市数组里第3个元素为"XianYou"的元素。
⑥"\$size"用其查询指定长度的数组,并且,它不能与其他查询子句组合(比如\$lt等)。这里有个小技巧就是在文档中添加一 个"size"键来实现。如:
{ \$push : { "love_city" : "BeiJing" } , \$inc : { "size" : 1 } }
这样的储存文档后,我们就可以这样查询:{ "size" : { \$gt : 3 } }查询数组长度大于3的文档。可惜的是,这种技巧并不能与"\$addToSet"操作符同时使用。
⑦"\$slice"返回数组的一个子集合。前面介绍过find的第二个参数是可选的,可以指定返回哪些键。\$slice也可以接受偏移值和要返回的数量,来返回中间的结果。
findOne( criteria , { "age" : { $slice : [ 3 , 4 ] } } )会返回第6~12个。
注意,如果没有特殊声明,\$slice是会返回所有的键。
⑧内嵌文档查询:可以使用点表示法查询内嵌的键
{ "school.teacher" : "LaoShi" , "school.student" : "XueSheng" }
点表示法也是待插入文档不能包含"."的原因,但是将键作为URL保存的时候经常就会碰到这种情况,通常下的解决方案是将URL 里的"."替换成一个URL里的非法字符串。
"\$elemMatch"将限定条件进行分组,仅当需要对一个内嵌文档的多个键操作时才会用到。如:查询学校里年龄大于30的nick教师:
{ "teacher" : { "name" : "nick" , "age" : { \$gt : 30 } } }
{ "teacher.name" : "nick" , "teacher.age" : { \$gt : 30 } }
以上两种方法都不能达到目的,要正确的指定一组条件,而不用指定每一个键,这时候就要使用"\$elemMatch"。这种模糊的命名条件句能用来指定匹配数组中单个内嵌文档的限定条件。正确写法:
{ "teacher" : { $elemMatch : { "name" : "nick" , "age" : { $gt : 30 } } } }
⑨"\$where"查询,用它可以执行任意的JavaScript作为查询的一部分。它可以用函数也可以用字符串来指定,这两条是等价的:
{ "$where" : "this.x + this.y == 10 " }
{ "$where" : "function(){ return this.x + this.y == 10; }" }
注意,不是非常必要时,一定要避免使用"$where",速度慢而且不能利用索引。
好了,上面已经很详细的讲解了MongoDB的基础语法。接下来正式进入进阶:
数据库使用游标来返回find的执行结果,它可以限制结果的数量,略过部分结果,根据任意方向任意键的组合对结果进行各种排序,或者是执行其他一些功能强大的操作。MongoDB里面的游标有点类似我们说的C#里面延迟执行。
在shell中创建一个游标我们必须用局部变量(用var声明的变量):
var testCursor = db.test.find();
如果要迭代结果,可以使用next方法,也可以使用hasNext方法来查看有没有其他的结果。
while( testCursot.hasNext() ){
obj = testCursot.next();
...
}
当然,迭代类还实现了迭代器的接口,所以可以在foreach循环中使用:
testCursot.forEach(function(_test){
print(_test.name);
})
注意,shell调用find操作的时候并不会立即查询数据库,而是等待真正开始要求获得数据时才发送查询。这个过程会一直持续到游标耗尽或者结果全部返回。
limiit、skip和sort在第二节的时候已经讲过了,MongoVUE里操作很简单,这里就不深入去探讨了。这三个组合对分页的用处很大,这里还要注意一些细节:
sort比较顺序:最小值->null->数字->字符串->对象/文档->数组->二进制数据->对象ID->布尔型->日期->时间戳->正则表达式->最大值;
避免使用skip略过大量数据,会很影响性能。
选取随机文档和略过大量数据是一样很低效,可以给每个文档用Math.random分配个随机数进行查询。
对于绝大数驱动向查询添加各种选项,比较有用的有:
$maxscan:(integer)指定查询最多扫描的文档数量
$max:(document)查询的开始条件
$min:(document)查询的结束条件
$hint:(document)指定服务器使用哪个索引进行查询
$explain:(boolean)获取查询执行的细节,并非查询
$snapshot:(boolean)确保查询的结果是在插叙执行的那一刻的一致快照。
索引就是用来加速查询的,就好比我们看书一样,直接打开标签,而不是一页页去翻。同时,绝大多数优化索引的方法同样适用于MongoDB。
我相信索引这个概念大家都不陌生,这里也就粗略概括下:
shell下创建索引:
db.test.ensureIndex( { "name" : 1 } )
对于同一个集合,同样的索引反复创建是徒劳的。
对于某个键创建的索引对其他查询是没有帮助的,即使是查询包含了索引的键。如:
db.test.find( { "age" : 23 } ).sort( { "age" : 23 , "name" : 1 } )
这种情况一定要创建查询中用到的所有键的索引。
创建索引的缺点就是每次插入或者更新还是删除时都会产生额外的开销。每个集合默认的最大索引个数为64个。
可以用getLastError来检查索引是否成功创建了或者未创建的原因。
唯一索引,类似"_id",不解释。切记唯一索引要避免null。
复合唯一索引,顾名思义,允许索引键值相同,只要组合不同就行。
索引的元信息储存在每个数据库的system.indexes集合中,不能对其插入或者删除文档。只能通过ensureIndex插入和dorpIndexes删除。
地理空间索引,它的参数不再是1和-1了,而是"2d"。索引键必须是某种形式的一对值,可以是两个元素的数组,也可以是包含两个键内嵌文档。默认情况下地理空间索引假设值的范围是-180~180。可以用max,min来指定。这货对定位很有用。它的两种查询方式:
db.map.find( { "GPS" : { $near : [ 66,-11 ] } } )
db.runCommand( { goNear : map , near : [ 66,-11 ] } )
对这货感兴趣的可以查看[官方文档](http://www.mongodb.org/display/DOCS/Geospatial+%20Indexing)
题外话,我的写作水平还是菜的不行,本节本想用MongoVUE工具进行讲解,无奈工作本罢工-。-|||只好先参考MongoDB权威指南进行知识点讲解。所以,以上讲解的都是在shell环境下的操作。下节----MongoDB语法进阶②③ 将继续讲解聚合和MapReduce以及MongoVUE可视化工具下的操作。