(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对比进行说明

image

跟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章的笔记

原文地址:https://www.cnblogs.com/tian2010/p/2607465.html