(5) MongoDB查询操作
mongodb采用find来进行查询操作,根据传进去的参数不同,可以进行不同类型的操作。
1. 最简单的查询
首先,最简单的查询,当然是不带任何条件的查询,这在我们前面的例子中也看过了,如db.blog.find(),该查询将返回blog集合中所有的文档。
2. 限定条件
在关系型数据库(RDB)中,我们通过where来限定查询条件,如:
select *
from user
where age =28
我们来看看在mongodb中如何进行类似的操作:
db.user.find({"age":28})
mongodb的第一个参数用来指定要返回哪些文档,如,上面的例子表示,返回user集合中,age为28的所有文档。
在RDB中,我们通过where A and B来指定多个条件,mongodb中的and也很简单,只需在第一个参数中列出多个键即可,如:
db.user.find({"age":28,"name":"tian"})
3. 返回指定的键
在RDB中,我们可以通过指定列来我们需要的字段,在mongodb中,我们可以通过为find指定第二个参数,来指定我们需要返回的键。
返回所有的字段有时是不必要的,甚至要浪费数据的传输量和客户端的解码时间。
如:
> db.user.insert({"_id":1,"name":"tian","age":28})
> db.user.insert({"_id":2,"name":"jerry","age":36})
> db.user.insert({"_id":3,"name":"harry","age":26})
> db.user.find({},{"name":1})
{ "_id" : 1, "name" : "tian" }
{ "_id" : 2, "name" : "jerry" }
{ "_id" : 3, "name" : "harry" }
我们发现,age这个键没有返回,但是_id这个键返回了,这是mongodb默认的。
Note:在上面的find语句中,第一个参数为{},表示没有限定条件。
有时候,集合里面的键比较多,如果我们一一列举显得有点罗嗦,这个时候,我们可以通过指定不需要返回的键,如:
> db.user.find({},{"age":0})
{ "_id" : 1, "name" : "tian" }
{ "_id" : 2, "name" : "jerry" }
{ "_id" : 3, "name" : "harry" }
将不需要的键指定为0,这样当集合的键比较多时,就可以省掉不少麻烦了。
4. 其他查询条件
除了前面一些简单的匹配外,mongodb还可以进行更加复杂的查询,我们通过跟RDB对比进行说明
跟RDB对比后,mongodb的用法就一目了然了。虽然mongodb的操作符没有像平常的比较运算符好记,但是mongodb的这些操作符也都是根据英文缩写,如$gt就是great than,$lte就是less than,所以也不算难记。
5. 特殊的null
null无论在哪里,都注定是特殊的一员,mongodb中的null也是一样。
首先,null确实可以匹配自身,如:
> db.user.insert({"_id":4,"name":"leon",age:null})
> db.user.find({"age":null})
{ "_id" : 4, "name" : "leon", "age" : null }
但是,null还会匹配不存在的键
> db.user.insert({"_id":6,"name":"jim"})
> db.user.find({"age":null})
{ "_id" : 4, "name" : "leon", "age" : null }
{ "_id" : 6, "name" : "jim" }
我们看到,没有age这个键的文档也匹配上了。
当然,我们也可以防止这种情况发生,那就是指定存在null值的键的文档才返回:
> db.user.find({"age":{"$in":[null],"$exists":true}})
{ "_id" : 4, "name" : "leon", "age" : null }
这个写法看上去很别扭,但是,没办法,mongodb没有提供类似$eq(equal)的操作符。
6. 操作数组
先看个简单的例子:
> db.food.insert({"fruit":["apple","banana","peach"]})
> db.food.find({"fruit":"apple"})
{ "_id" : ObjectId("500fe140ad99f95967489682"), "fruit" : [ "apple", "banana", "peach" ] }
只要数组里面包含要匹配的值,就会匹配成功。
有时候,我们需要查找数组中包含多个值的,就需要用到$all了。
考虑这样一个集合
{ "_id" : ObjectId("500fe140ad99f95967489682"), "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : ObjectId("500fe349ad99f95967489683"), "fruit" : [ "cherry", "banana","apple" ] }
{ "_id" : ObjectId("500fe358ad99f95967489684"), "fruit" : [ "apple", "orange", "peach" ] }
我们要找到既包含apple,又包含banana的文档:
> db.food.find({"fruit":{"$all":["apple","banana"]}})
{ "_id" : ObjectId("500fe140ad99f95967489682"), "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : ObjectId("500fe349ad99f95967489683"), "fruit" : [ "cherry", "banana","apple" ] }
我们还可以通过数组进行查找,需要注意的是,用数组查找就是完全匹配了,如:
> db.food.find({"fruit":["apple","banana"]})
> db.food.find({"fruit":["apple","banana","peach"]})
{ "_id" : ObjectId("500fe140ad99f95967489682"), "fruit" : [ "apple", "banana", "peach" ] }
有时候需要根据数组个数来查找的,如
> db.food.find({"fruit":{"$size":3}})
RDB中经常用top来获取前几条数据,对于数组的前几个的获取,就需要用到$slice了,如:
> db.food.find({},{"fruit":{"$slice":2}})
{ "_id" : ObjectId("500fe140ad99f95967489682"), "fruit" : [ "apple", "banana" ]}
{ "_id" : ObjectId("500fe349ad99f95967489683"), "fruit" : [ "cherry", "banana" ] }
{ "_id" : ObjectId("500fe358ad99f95967489684"), "fruit" : [ "apple", "orange" ]}
可以看到,fruit中只返回了前两个值。
也可以返回指定的返回的元素,如
> db.food.find({},{"fruit":{"$slice":[2,4]}})
这个语句是指,跳过前面的2个元素,返回3~4这2个元素。
7.内嵌文档的查询
所谓内嵌文档,就是指文档的值本身又是一个文档。
假设有这样的一个文档:
{
"name”:
{
"first":"terry",
"last":"chen"
},
"age":12
}
要寻找名字为terry chen的可以这样:
> db.userinfo.find({"name":{"first":"terry","last":"chen"}})
{ "_id" : ObjectId("500fee69ad99f95967489687"), "name" : { "first" : "terry", "last" : "chen" }, "age" : 12 }
但是有个问题就是如果我们把"first"键和"last"键位置放错了,或者我们在加入一个"middle"键,那么就没有办法找到该文档了。
这就需要我们更改查询的策略,mongodb中用点表示查询内嵌的键:
> db.userinfo.find({"name.first":"terry","name.last":"chen"})
这样,即使将来加了"middle"键,依旧可以找到上面的文档。
当内嵌文档更加复杂之后,我们就需要更多的技巧了,如,有文档:
> db.blog.find()
{ "_id" : 1, "title" : "1st post", "comments" : [ { "author" : "joe"
, "score" : 3 }, { "author" : "marry", "score" : 6 } ] }
我们要找到评论者为joe,且score>=5的文档,如果我们采用:
> db.blog.find({"comments.author":"joe","comments.score":{"$gte":5}})
{ "_id" : 1, "title" : "1st post", "comments" : [ { "author" : "joe"
, "score" : 3 }, { "author" : "marry", "score" : 6 } ] }
来查找,明显是错误的,因为它返回了刚才的文档,因为第一条评论匹配了author:joe,第二条评论匹配了score:6.
要正确的指定一组条件,而不用指定每个键,要使用"$elemMatch":
> db.blog.find({"comments":{"$elemMatch":{"author":"joe","score":{"$gte":5}}}})
"$elemMatch"将限定条件分组,仅当需要对一个内嵌文档的多个键操作时才会用到。
8.$where查询
$where子句可以执行任意JavaScript作为查询的一部分,这就使得查询几乎能做任何事。
但是,它的性能比常规查询要慢很多,因为每个文档都要从BSON转换成JavaScript对象,然后通过$where表达式来运行,同时,还不能利用索引。
因此,不是非常必要时,不要用$where子句。
参考:mongodb权威指南
备注:本节基本是《mongodb权威指南》第4章的笔记