在CRUD四个操作中查找是从mongodb数据库的集合中获取一个记录或者一个文档。对于非DBA来说,使用的最多的就是查询,所以说如果学好了查询,对于我们将来更好的操作数据库有很大的帮助。
在mongodb中从集合中获得一条数据或者文档可以通过以下两个方法:
find()
findOne()
find()是我们从数据库中查找数据使用最主要的方法。find()语法如下:
db.[集合名].find( <query>, <projection> )
类比SQL语句,query就相当于我们SQL中的查询条件,projection就相当于SQL中SELECT后面我们需要的字段。默认情况下find()最多返回20条记录。
在前面我们介绍了如果我们省略query、projection参数默认会返回集合中的文档。
下面我们查询一下user集合中name为user2的文档:
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 11 }
{ "_id" : 2, "name" : "user2", "age": 12 }
{ "_id" : 3, "name" : "user3","age" : 13 }
{ "_id" : 4, "name" : "user4","age" : 14 }
{ "_id" : 5, "name" : "user5","age" : 15 }
{ "_id" : 6, "name" : "user6","age" : 16 }
{ "_id" : 7, "name" : "user7","age" : 17 }
{ "_id" : 8, "name" : "user8","age" : 18 }
{ "_id" : 9, "name" : "user9","age" : 19 }
{ "_id" : 10, "name" : "user10","age" : 20 }
> db.user.find({name:"user2"}) /*我们只是用了query参数,默认返回所有字段,类似SQL中的SELECT * from table*/
{ "_id" : 2, "name" : "user2","age" : 12 }
上面的代码中将文档的所有字段都返回了,可能这个文档小无所谓。如果对于一个很大的文档,我们只需要一个字段,如果再这样返回,那么我们就有点浪费了,这时候我们就可以使用projection参数。
我们自user表中查找name为user2的文档,我们只想知道它的age,不需要它的name,我们可以向下面这样:
> db.user.find({name:"user2"},{age:1})
{ "_id" : 2, "age" : 12 }
在mongodb中,对于非零都会认为为真,只有0会被当做假,所以像上面的情况中使用true和false也可。包括自后面的代码中,我也会经常使用非0数代表真。
可能我们有人希望按照下面那样写projection参数:
{name:0,age:1}
这样再mongodb中错误的,mongodb中projection参数,要不就只写你需要的值(也就是值都为真),要不你就都写不想要的值(值都为假),混合使用是不被接受的。
findOne()与find()基本相同,只是findONe()不管满足查询条件的文档有多少,只会返回第一条数据。findOne()的语法和使用方法与find()一样,这里就不再介绍了。
4.2.1 查询选择器
如果mongodb只是提供了上面介绍的查询方式,那么mongodb号称强大的查询功能就太名不副实了。下面我们就来学习mongodb强大的查询选择器。
在mongodb的文档中将其分为比较、逻辑、元素、javascript、地理空间、数据选择器,我们也按照这个介绍。
比较选择器
Mongodb提供的比较选择器有$all、$gt、$gte、$lt、$lte、$in、$nin、$ne。
$all
语法:{ field: {$all: [ <value> , <value1> ... ] }
查找字段的值为数组,并且包含所有给定的值的文档。
> db.phone.find()
{ "_id" : ObjectId("5198e20220c9b0dc40419385"),"num" : [ 1, 2, 3 ] }
{ "_id" : ObjectId("5198e21820c9b0dc40419386"),"num" : [ 4, 2, 3 ] }
{ "_id" : ObjectId("5198e22120c9b0dc40419387"),"num" : [ 1, 2, 5 ] }
> db.phone.find({num:{$all:[1,4]}}) /*同时包含1,4的没有数据*/
> db.phone.find({num:{$all:[1,2]}})
{ "_id" : ObjectId("5198e20220c9b0dc40419385"),"num" : [ 1, 2, 3 ] }
{ "_id" : ObjectId("5198e22120c9b0dc40419387"),"num" : [ 1, 2, 5 ] }
$gt
语法:{field: {$gt:value} }
查找字段的值大于给定值的文档。
> db.user.find({age:{$gt:6}})
{ "_id" : ObjectId("5198c286c686eb50e2c843b9"),"name" : "user7", "age" : 7 }
{ "_id" : ObjectId("5198c286c686eb50e2c843ba"),"name" : "user8", "age" : 8 }
{ "_id" : ObjectId("5198c286c686eb50e2c843bb"),"name" : "user9", "age" : 9 }
{ "_id" : ObjectId("5198c286c686eb50e2c843bc"),"name" : "user10", "age" : 10 }
{ "_id" : ObjectId("5198c3cac686eb50e2c843bd"),"name" : "user0", "age" : 20 }
$gte
语法:{field:{$gte: value} }
查找字段的值大于等于给定值的文档。
> db.user.find({age:{$gt:6}})
{ "_id" : ObjectId("5198c286c686eb50e2c843b8"),"name" : "user6", "age" : 6 }
{ "_id" : ObjectId("5198c286c686eb50e2c843b9"),"name" : "user7", "age" : 7 }
{ "_id" : ObjectId("5198c286c686eb50e2c843ba"),"name" : "user8", "age" : 8 }
{ "_id" : ObjectId("5198c286c686eb50e2c843bb"),"name" : "user9", "age" : 9 }
{ "_id" : ObjectId("5198c286c686eb50e2c843bc"),"name" : "user10", "age" : 10 }
{ "_id" : ObjectId("5198c3cac686eb50e2c843bd"),"name" : "user0", "age" : 20 }
$lt
语法:{field: {$lt:value} }
查找字段的值小于给定值的文档。
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({age:{$lt:5}})
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
$lte
语法:{field: {$lte:value} }
查找字段的值小于等于给定值的文档。
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({age:{$lte:5}})
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
$in
语法:{ field: {$in: [ <value> , <value1> ... ] }
查找字段的值为数组,并且包含一个或多个给定的值的文档。
> db.c2.find()
{ "_id" : ObjectId("519d4db8f90a444101408c2c"),"ary" : [ 1, 2 ] }
{ "_id" : ObjectId("519d4dbcf90a444101408c2d"),"ary" : [ 1, 3 ] }
{ "_id" : ObjectId("519d4dc4f90a444101408c2e"),"ary" : [ 4, 5, 6 ] }
> db.c2.find({ary:{$in:[1,2]}})
{ "_id" : ObjectId("519d4db8f90a444101408c2c"),"ary" : [ 1, 2 ] }
{ "_id" : ObjectId("519d4dbcf90a444101408c2d"),"ary" : [ 1, 3 ] }
$nin
语法:{ field: {$nin: [ <value> , <value1> ... ] }
查找字段的值为数组,并且不包含一个或多个给定的值的文档。
> db.c2.find()
{ "_id" : ObjectId("519d4db8f90a444101408c2c"),"ary" : [ 1, 2 ] }
{ "_id" : ObjectId("519d4dbcf90a444101408c2d"),"ary" : [ 1, 3 ] }
{ "_id" : ObjectId("519d4dc4f90a444101408c2e"),"ary" : [ 4, 5, 6 ] }
> db.c2.find({ary:{$nin:[1,2]}})
{ "_id" : ObjectId("519d4dc4f90a444101408c2e"),"ary" : [ 4, 5, 6 ] }
$ne
语法:{field:{$ne: value} }
查找字段不等于给定的值或者不存在此字段的文档。
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({age:{$ne:1}})
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({sex:{$ne:1}}) /*不存在sex字段,将所有文档都查出来*/
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
逻辑选取器
顾名思义这组选择器是跟逻辑有关系的,有$and、$not、$or、$nor。
$and
语法:{ $and: [{ <expression1> }, { <expression2> } , ... , { <expressionN>} ] }
查找满足所有给定条件的文档,这里使用数组存放所有的条件。$and在执行的时候如果第一个条件不满足,就会短路不在继续执行了。
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({$and:[{name:"user1"},{age:2}]})
> db.user.find({$and:[{name:"user1"},{age:1}]})
{ "_id" : 1, "name" : "user1","age" : 1 }
$and一般都可以转换成为普通的查询,例如上面的完全可以转换为:
db.user.find({name:”user1”,age:1})
既然都可以转换为不适用$and的方法查询,那为何还需要使用and呢?因为使用简单方法查询如果条件太多的话或者结合其他选择器的时候比较混乱,使用$and会比较清晰。
$not
语法:{ field: {$not: { <operator-expression> } } }
查找不匹配所给定查询表达式的文档。
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({age:{$not:{$gt:5}}}) /*查找age不大于5的文档*/
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
$or
语法:{ $or: [ {<expression1> }, { <expression2> }, ... , { <expressionN> } ]}
查找满足表达式数组其中一个的文档。
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({$or:[{name:"user1"},{age:10}]}) /*查找name为user1,或者age为10的文档*/
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 10, "name" : "user10","age" : 10 }
$nor
语法:{ $nor: [{ <expression1> }, { <expression2> }, ... { <expressionN> } ] }
查找不满足给定表达式数组中任意表达式的文档。
> db.user.find()
{ "_id" : 1, "name" : "user1","age" : 1 }
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
{ "_id" : 10, "name" : "user10","age" : 10 }
> db.user.find({$nor:[{name:"user1"},{age:10}]}) /*查找name不为user1或者age不是10的数组*/
{ "_id" : 2, "name" : "user2","age" : 2 }
{ "_id" : 3, "name" : "user3","age" : 3 }
{ "_id" : 4, "name" : "user4","age" : 4 }
{ "_id" : 5, "name" : "user5","age" : 5 }
{ "_id" : 6, "name" : "user6","age" : 6 }
{ "_id" : 7, "name" : "user7","age" : 7 }
{ "_id" : 8, "name" : "user8","age" : 8 }
{ "_id" : 9, "name" : "user9","age" : 9 }
元素选择器(Element)
在mongodb的文档中叫做Element,翻译成元素选择器总是感觉有点别扭,但是也没有找到更好的。
此类选择器包括$exists、$mod、$type。
$exists
语法:{ field: {$exists: <boolean> } }
查找是否存在某字段的文档。True表示存在,false表示不存在。
> db.c2.find()
{ "_id" : 1, "name" : "yue" }
{ "_id" : 2, "age" : 2 }
> db.c2.find({name:{$exists:1}}) /*查找存在name字段的文档*/
{ "_id" : 1, "name" : "yue" }
> db.c2.find({name:{$exists:0}}) /*查找不存在age字段的文档*/
{ "_id" : 2, "age" : 2 }
$mod
语法:{ field: {$mod: [ divisor, remainder ]} }
选取某字段满足模取运算的文档。该字段的值必须为数字,否则查不到结果。
> db.c2.find()
{ "_id" : 1, "name" : "yue" }
{ "_id" : 2, "age" : 2 }
{ "_id" : 3, "age" : 15 }
> db.c2.find({age:{$mod:[2,1]}}) /*age取模2余1的文档*/
{ "_id" : 3, "age" : 15 }
> db.c2.find({name:{$mod:[2,1]}})
$type
语法:{ field: {$type: <BSON type> } }
按照字段的类型查找文档。每种类型对应的数字,下面已经列出了。
类型 |
编号 |
双精度 |
1 |
字符串 |
2 |
对象 |
3 |
数组 |
4 |
二进制数据 |
5 |
对象 ID |
7 |
布尔值 |
8 |
日期 |
9 |
空 |
10 |
正则表达式 |
11 |
JavaScript |
13 |
符号 |
14 |
JavaScript(带范围) |
15 |
32 位整数 |
16 |
时间戳 |
17 |
64 位整数 |
18 |
最小键 |
255 |
最大键 |
127 |
在mongodbshell中某些类型是不存在的,比如数字32位精度表示的是双精度。
> db.c2.find()
{ "_id" : 1, "name" : "yue" }
{ "_id" : 2, "age" : 2 }
{ "_id" : 3, "age" : 15 }
> db.c2.find({age:{$type:1}}) /*age类型为双精度的文档*/
{ "_id" : 2, "age" : 2 }
{ "_id" : 3, "age" : 15 }
Javascript选择器
此类选择器包括$regex、$where。
$regex
查找满足正则表达式的文档。
>db.user.find()
{"_id" : 1, "name" : "user1", "age" : 1}
{"_id" : 2, "name" : "user2", "age" : 2}
{"_id" : 3, "name" : "user3", "age" : 3}
{"_id" : 4, "name" : "user4", "age" : 4}
{"_id" : 5, "name" : "user5", "age" : 5}
{"_id" : 6, "sex" : "nan" }
>db.user.find({name:/user*/i})
{"_id" : 1, "name" : "user1", "age" : 1}
{"_id" : 2, "name" : "user2", "age" : 2}
{"_id" : 3, "name" : "user3", "age" : 3}
{"_id" : 4, "name" : "user4", "age" : 4}
{"_id" : 5, "name" : "user5", "age" : 5}
Mongodb中的regex兼容perl,类似上面代码中的正则表达式是比较方便的。Mongodb同样也可以使用$regex方法。例子如下:
>db.user.find()
{"_id" : 1, "name" : "user1", "age" : 1}
{"_id" : 2, "name" : "user2", "age" : 2}
{"_id" : 3, "name" : "user3", "age" : 3}
{"_id" : 4, "name" : "user4", "age" : 4}
{"_id" : 5, "name" : "user5", "age" : 5}
{"_id" : 6, "sex" : "nan" }
>db.user.find({name:{$regex:"user*",$options:"i"}}) /*$options设置正则选项*/
{"_id" : 1, "name" : "user1", "age" : 1}
{"_id" : 2, "name" : "user2", "age" : 2}
{"_id" : 3, "name" : "user3", "age" : 3}
{"_id" : 4, "name" : "user4", "age" : 4}
{"_id" : 5, "name" : "user5", "age" : 5}
$where
$where可以根据表达式或者function查找满足的文档。使用function的时候如果返回的是true,则查找出此文档。
>db.user.find()
{"_id" : 1, "name" : "user1", "age" : 1}
{"_id" : 2, "name" : "user2", "age" : 2}
{"_id" : 3, "name" : "user3", "age" : 3}
{"_id" : 4, "name" : "user4", "age" : 4}
{"_id" : 5, "name" : "user5", "age" : 5}
{"_id" : 6, "sex" : "nan" }
>db.user.find({$where:"this.sex == ‘nan‘"})
{"_id" : 6, "sex" : "nan" }
>db.user.find({$where:"obj.sex == ‘nan‘"})
{"_id" : 6, "sex" : "nan" }
>db.user.find({$where:function(){return this.sex == ‘nan‘;}})
{"_id" : 6, "sex" : "nan" }
>db.user.find({$where:function(){return obj.sex == ‘nan‘;}})
{"_id" : 6, "sex" : "nan" }
以上代码中的this和obj都代表当前的文档。
地理位置选择器
参看4.5地理空间搜索
数组选择器
数组选择器主要是对数组操作的,包括$elemMatch、$size。
$elemMatch
对于字段的值是数组,而且数组中的元素是内嵌的文档,在我们根据数组中的内嵌文档做查询的时候,需要 $elemMatch。
>db.c3.find()
{"_id" : 1, "array" : [ { "value1" : 1,"value2" : 0 }, { "value1" : 2, "value2" : 2 } ]}
> db.c3.find({ array: { $elemMatch: { value1: 1, value2: { $gt: 1 } } } } )
> db.c3.find({ array: { $elemMatch: { value1: 1, value2: { $lt: 1 } } } } )
{"_id" : 1, "array" : [ { "value1" : 1,"value2" : 0 }, { "value1" : 2, "value2" : 2 } ]}
从上面的代码中可以看出$elemMath会循环数组中的每一内嵌文档,然后与给定的条件进行比较,查找出满足条件的文档。
$size
语法:db.collection.find({ field: { $size: <number>} } )
查找出数组的长度满足给定值的数组。
>db.c4.find()
{"_id" : 1, "ary" : [ 1 ] }
{"_id" : 2, "ary" : [ 2, 3 ] }
{"_id" : 3, "ary" : [ 4, 5, 6 ] }
>db.c4.find({ary:{$size:5}}) /*没有数组长度等于5的文档*/
>db.c4.find({ary:{$size:2}})
{"_id" : 2, "ary" : [ 2, 3 ] }