首页 > 代码库 > 第二部分 应用篇 第五章 MongoDB高级查询

第二部分 应用篇 第五章 MongoDB高级查询

声明:该文章主要来自《MongoDB实战》一书内容,主要想通过该书学习MongoDB的相应知识,加深理解,故写在自己的博文当中,作为记录,在最后的章节中,会有一个自己集合MongoDB数据库应用的JavaEE的web应用。

本章将结合实际应用,重点阐述一些实际工作中最常用的方法。

面向文档的NoSQL数据库主要解决的问题不是高性能的并发读写,而是保证海量数据存储的同时,具有良好的查询性能。

MongoDB最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

最后由于MongoDB可以支持复杂的数据结构,而且带有强大的数据查询功能,因此非常受到欢迎,很多项目都考虑用MongoDB来替代MySQL等传统数据库来实现不是特别复杂的web应用,由于数据量实在太大,所以迁移到MongoDB上面,数据查询的速度得到非常显著显著提升。

下面将介绍一些高级查询语法。

1.1.、条件操作符

<、<=、>、>=这几个操作符不用多解释了,最常用的也是最简单的。

> db.xuz.find({"j":{$gt:10}}); //大于:field>value
{ "_id" : ObjectId("54a8a620e708df7187eb2de2"), "x" : 5, "j" : 11 }
{ "_id" : ObjectId("54a8a620e708df7187eb2de3"), "x" : 5, "j" : 12 }
{ "_id" : ObjectId("54a8a620e708df7187eb2de4"), "x" : 5, "j" : 13 }
> db.xuz.find({"j":{$lt:10}});//小于:field<value
{ "_id" : ObjectId("54a8a5a427681683ca2c2466"), "x" : 5, "j" : 1 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2467"), "x" : 5, "j" : 2 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2468"), "x" : 5, "j" : 3 }
> db.xuz.find({"j":{$gte:10}});//大于等于:field>=value
{ "_id" : ObjectId("54a8a5a427681683ca2c246f"), "x" : 5, "j" : 10 }
{ "_id" : ObjectId("54a8a620e708df7187eb2de1"), "x" : 5, "j" : 10 }
{ "_id" : ObjectId("54a8a620e708df7187eb2de2"), "x" : 5, "j" : 11 }
{ "_id" : ObjectId("54a8a620e708df7187eb2de3"), "x" : 5, "j" : 12 }
> db.xuz.find({"j":{$lte:10}});//小于等于:field<=value
{ "_id" : ObjectId("54a8a5a427681683ca2c2466"), "x" : 5, "j" : 1 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2467"), "x" : 5, "j" : 2 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2468"), "x" : 5, "j" : 3 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2469"), "x" : 5, "j" : 4 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246a"), "x" : 5, "j" : 5 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246b"), "x" : 5, "j" : 6 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246c"), "x" : 5, "j" : 7 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246d"), "x" : 5, "j" : 8 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246e"), "x" : 5, "j" : 9 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246f"), "x" : 5, "j" : 10 }
如果要同时满足多个条件,可以这样做。

> db.xuz.find({"j":{$gt:5,$lt:10}});//value1<field<value
{ "_id" : ObjectId("54a8a5a427681683ca2c246b"), "x" : 5, "j" : 6 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246c"), "x" : 5, "j" : 7 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246d"), "x" : 5, "j" : 8 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246e"), "x" : 5, "j" : 9 }


1.2、$all匹配所有

这个操作符跟SQL语法的in类似,但不同的是,in只需满足()内的某一个值即可,而$all必须满足[]内的所有值,比如:

> db.xuz.find({age:{$all:[6,8]}})
{ "_id" : ObjectId("54a8bc71587fb1134affd9cf"), "name" : "zhangsan", "age" : [ 6, 7, 8 ] }

但是查询不出

{ "_id" : ObjectId("54a8bc71587fb1134affd9cf"), "name" : "zhangsan", "age" : [ 6, 7, 9] }


1.3、$exists判断字段是否存在

查询所有存在的age字段的记录:

> db.xuz.find({age:{$exists:true}})
{ "_id" : ObjectId("54a8bc71587fb1134affd9cf"), "name" : "zhangsan", "age" : [ 6, 7, 8 ] }
{ "_id" : ObjectId("54a8bc76587fb1134affd9d0"), "name" : "zhangsan", "age" : [ 6, 7, 9 ] }

查询所有不存在name字段的记录:

> db.xuz.find({name:{$exists:false}});
{ "_id" : ObjectId("54a8a5042db3e1b27d0e5204"), "x" : 4 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2466"), "x" : 5, "j" : 1 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2467"), "x" : 5, "j" : 2 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2468"), "x" : 5, "j" : 3 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2469"), "x" : 5, "j" : 4 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246a"), "x" : 5, "j" : 5 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246b"), "x" : 5, "j" : 6 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246c"), "x" : 5, "j" : 7 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246d"), "x" : 5, "j" : 8 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246e"), "x" : 5, "j" : 9 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246f"), "x" : 5, "j" : 10 }
再比如:c1表的数据如下:

> db.c1.find();
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
查询存在age字段的数据

> db.c1.find({age:{$exists:true}})
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
只能显示出有age字段的数据,age_1的数据并没有显示出来。


1.4、NULL值处理

NULL值的处理稍微有一点奇怪,具体看下面的样例数据:

> db.c2.find();
{ "_id" : ObjectId("54a8bde4587fb1134affd9d3"), "nage" : "wangwu", "age" : null }
{ "_id" : ObjectId("54a8bdf7587fb1134affd9d4"), "name" : "zhangsan", "age" : 23 }
{ "_id" : ObjectId("54a8be09587fb1134affd9d5"), "name" : "zhaoliu", "addr" : 23 }
其中wangwu的age字段为空,zhaoliu没有age字段,我们想找到age为空的行,具体如下:
> db.c2.find({age:null})
{ "_id" : ObjectId("54a8bde4587fb1134affd9d3"), "nage" : "wangwu", "age" : null }
{ "_id" : ObjectId("54a8be09587fb1134affd9d5"), "name" : "zhaoliu", "addr" : 23 }
但是我们以为只能找到wangwu,却zhaoliu也被找出来了,所以null不仅能找到它自身,连不存在的age字段的记录也找出来了,那么怎么样才能只找到wangwu呢?我们用exists来限制一下即可:

> db.c2.find({age:{$in:[null],$exists:true}})
{ "_id" : ObjectId("54a8bde4587fb1134affd9d3"), "nage" : "wangwu", "age" : null }
这样如我们期望一样,只有wangwu被找出来了。


1.5、$mod取模运算

查询age取模10等于0的数据

> db.c1.find({age:{$mod:[10,0]}})
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
在比如,c1表数据如下:

> db.c1.find();
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
查询age取模10等于1的数据

> db.c1.find({age:{$mod:[10,1]}})
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
可以看出只显示出了age取模10等于1的数据,其他不符合规则的数据并没有显示出来。


1.6、$ne不等于

查询age的值不等于20的数据。

c1表的数据如下:

> db.c1.find();
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
查询age的值不等于20的数据。

> db.c1.find({age:{$ne:20}})
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
可以看出只显示出了age不等于20的数据,其他不符合规则的数据并没有显示出来。


1.7、$in包含

与SQL标准语法的用途一样,即要查询的是一系列的枚举值的范围。

比如c1表的数据如下:

> db.c1.find();
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
查询age的值在21,22范围内的数据。

> db.c1.find({age:{$in:[21,22]}})
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
可以看出只显示出了age等于21或者22的数据,其他不符合规则的数据并没有显示出来。


1.8、$nin不包含

与SQL标准语法的用途是一样的,既要查询的数据在一系列的枚举范围外。

比如c1表的数据如下:

> db.c1.find();
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }

查询age的值在21,22范围外的数据

> db.c1.find({age:{$nin:[21,22]}})
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
可以看出只显示出了age不等于21或者22的数据,其他不符合规则的数据并没有显示出来。


1.9、$size数组元素的个数

对于c3表的数据如下:

> db.c3.find();
{ "_id" : ObjectId("54a8c156587fb1134affd9d8"), "age" : [ 6, 7, 8 ] }
{ "_id" : ObjectId("54a8c160587fb1134affd9d9"), "age" : [ 6, 7 ] }
{ "_id" : ObjectId("54a8c163587fb1134affd9da"), "age" : [ 6, 7, 9 ] }
{ "_id" : ObjectId("54a8c166587fb1134affd9db"), "age" : [ 6, 7, 9, 10 ] }
查询age字段size为3的记录:

> db.c3.find({age:{$size:3}})
{ "_id" : ObjectId("54a8c156587fb1134affd9d8"), "age" : [ 6, 7, 8 ] }
{ "_id" : ObjectId("54a8c163587fb1134affd9da"), "age" : [ 6, 7, 9 ] }
可以看出只显示出了age的size为3的数据,其他不符合规则的数据并没有显示出来。


1.10、正则表达式

对于c4表数据如下:

> db.c4.find();
{ "_id" : ObjectId("54a8c1e6587fb1134affd9dc"), "name" : "Boy" }
{ "_id" : ObjectId("54a8c1eb587fb1134affd9dd"), "name" : "Tom" }
{ "_id" : ObjectId("54a8c1ef587fb1134affd9de"), "name" : "Joe" }

查询name不以B开头的数据:

> db.c4.find({name:{$not:/^T.*/}})
{ "_id" : ObjectId("54a8c1e6587fb1134affd9dc"), "name" : "Boy" }
{ "_id" : ObjectId("54a8c1ef587fb1134affd9de"), "name" : "Joe" }
可以看出只显示出了name不等于Boy的数据,Boy的数据并没有显示,因为它符合查询规则。


1.11、JavaScript查询和$where查询

比如c1表的数据如下:

> db.c1.find()
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
查询age大于20的数据,下面各种方法殊途同归。

> db.c1.find({age:{$gt:20}})
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }

> db.c1.find({$where:"this.age>20"})
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }

> db.c1.find("this.age>20")
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }

function (){return this.age>20;}
> db.c1.find(f);
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }


1.12、count查询记录条数

count查询记录条数。

> db.c1.find().count();
4
返回c1表中所有数据的记录数。


1.13、skip限制返回记录的起点

从3条记录开始,返回5条记录(limit3,5)

xuz表中总共有64条数据:

> db.xuz.find().count();
64

从第三条开始,查询出5条记录:

> db.xuz.find().skip(3).limit(5)
{ "_id" : ObjectId("54a8a5a427681683ca2c2468"), "x" : 5, "j" : 3 }
{ "_id" : ObjectId("54a8a5a427681683ca2c2469"), "x" : 5, "j" : 4 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246a"), "x" : 5, "j" : 5 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246b"), "x" : 5, "j" : 6 }
{ "_id" : ObjectId("54a8a5a427681683ca2c246c"), "x" : 5, "j" : 7 }

该实现在应用中可用于分页查询。


1.14、sort排序

c1表中数据如下:

> db.c1.find();
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
以年龄升序asc排序:

> db.c1.find().sort({age:1})
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }

以年龄降序排序:

> db.c1.find().sort({age:-1})
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{ "_id" : ObjectId("54a8bd51587fb1134affd9d2"), "age_1" : 20, "length_1" : 30 }

1.15、游标

像大多数数据库产品一样,MongoDB也是用游标来循环处理每一条结果数据,具体语法如下:

> for(var c=db.c1.find();c.hasNext();){printjson(c.next())}
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{
        "_id" : ObjectId("54a8bd51587fb1134affd9d2"),
        "age_1" : 20,
        "length_1" : 30
}
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }

MongoDB还有另外一种方式来处理游标

> db.c1.find().forEach(function(u){printjson(u);})
{ "_id" : ObjectId("54a8bd47587fb1134affd9d1"), "age" : 20, "length" : 30 }
{
        "_id" : ObjectId("54a8bd51587fb1134affd9d2"),
        "age_1" : 20,
        "length_1" : 30
}
{ "_id" : ObjectId("54a8bf2b587fb1134affd9d6"), "age" : 21, "length" : 31 }
{ "_id" : ObjectId("54a8bf31587fb1134affd9d7"), "age" : 22, "length" : 35 }


1.16、存储过程

MongoDB为很多问题提供了一系列的解决方案,针对于其他数据库特性,它仍然毫不逊色,表现非比寻常。

MongoDB同样支持存储过程,关于存储过程你需要知道的第一件事就是它是用javaScript来些的,也许这会让你很奇怪,为什么它用javaScript来写,但实际上它会让你非常满意,MongoDB存储过程是存储在db.system.js表中,我们想象一个简单的sql自定义函数如下:

function addNumbers(x,y){

return x+y;

}

下面我们将这个SQL自定义函数转换为MongoDB的存储过程:

> db.system.js.save({_id:"addNumbers",value:function(x,y){return x+y;}});
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "addNumbers" })

存储过程可以被查看,修改和删除,所以我们用find来查看一下是否这个存储过程已经被创建上了。

> db.system.js.find();
{ "_id" : "addNumbers", "value" : function (x,y){return x+y;} }

下面我们来实际调用一下这个存储过程:

> db.eval(‘addNumbers(3,4.2)‘);
7.2

db.eval()是一个比较奇怪的东西,我们可以将存储过程的逻辑直接在里面并同时调用,而无需事先声明存储过程的逻辑。

> db.eval(function(){return 3+2;});
5

从上面可以看出,MongoDB的存储过程可以方便的完成算术运算,但其它数据库产品在存储过程中可以处理数据库内部的一些事情,比如取出某张表的数据量等等操作,这些MongoDB能做到吗?答案是肯定的,MongoDB可以轻而易举的实现,如下实例:

> db.system.js.save({_id:"get_count",value:function(){return db.c1.count();}});
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "get_count" })
> db.eval(‘get_count()‘)
4
可以看到存储过程可以很轻松的操作表。

至此MongoDB的高级查询相关的一些特性介绍完毕。

第二部分 应用篇 第五章 MongoDB高级查询