MongoDB查询

find简介

MongoDB中使用find来进行查询。查询就是返回一个集合中文档的子集,子集的范围从0个文档到整个集合。find中的第一个参数决定了要返回的哪些文档,这个参数是一个文档,用于指定查询条件。javascript

空的查询文档(例如{})会匹配集合的所有内容。要不是指定查询文档,默认就是{}. 例如:java

> db.c.find()

将批量返回集合c中全部的文档。正则表达式

使用条件数据库

> db.users.find({"age": 27})

指定须要返回的键

有时并不须要将文档中全部的键/值对都返回。遇到这种状况,能够经过find、findOne的第二参数来指定想要的键。这样作机会节省传输的数据量,又能节省客户端解码文档的时间和内存消耗。数组

例如,若是只对用户集合的“username”和"email"键感兴趣,可使用以下方式返回这些键:服务器

> db.users.find({}, {"username": 1, "email": 1})

这种状况默认"_id"这个键老是被返回的,即便没有指定要返回这个键。app

若是咱们不但愿结果中含有"fatal_weakness"键那么能够以下操做:函数

> db.users.find({}, {"username": 1, "_id": 0})

这样就能够把"_id"键剔除掉。post

 

有一些查询上的限制

当查询时,传递给数据库的查询文档的值必须是常量。也就是不能引用文档中其余键的值。例如保存库存有"in_stock"(剩余库存)和"num_sold"(已出售)两个键,想经过下列查询比较是行不通的。优化

> db.stock.find({"in_stock": "this.num_sold"})

 

查询条件

查询条件

"$lt","$lte","$gt"和"$gte"就是所有的比较操做符,分别对应<、<=、>、和>=。能够将其组合起来以便查找一个范围的值。例如查找18-30岁(含)的用户,就能够像下面这样

>db.users.find({"age": {"$gte": 18, "lte": 30}})

这样就能够找到"age"字段大于等于18,小于等于30的全部文档。

这样的范围查询对日期极为有用。例如要查找在2007年1月1日前注册的人,能够像下面这样。

> start = new Date("01/01/2007")
> db.users.find({"registered": {"$lt": start}})

若是是想查询文档的某个键/值不等于某个特定值的状况下,就要使用另外一种条件操做符“$ne”,它表示不相等,如果想查询全部名字部位joe的用户,就能够以下查询

> db.users.find({"username": {"$ne": "joe"}})

  

OR查询

MongoDB中有两种方式进行OR查询: "$in"能够用来查询一个键的多个值;"$or"更通用一些,能够在多个键中查询任意的给定值。

> db.raffle.find({"ticker_no": {"$in": [725, 542, 390]}})

  "$in"很是灵活能够指定不一样类型的条件和值。例如,在逐步将用户的ID好前已成用户名的过程当中,查询须要同时匹配ID和用户名:

这会匹配"user_id"等于12345的文档,也会匹配"user_id"等于"joe"的文档。

要是"$in"对应的数组只有一个值,那么和直接匹配这个值的效果同样例如 {ticket_no: {"$in": [725]}}  和 {"ticker_no": 725} 效果同样。

与"$in"相对的是"$nin","$nin"将返回与数组中全部的条件都不匹配的文档好比:

> db.raffle.find({"ticket_no": {"$nin": [725, 543, 490]}})

  

"$in"能够对单个键作OR 查询,但想要找到"ticket_no"为725或者"winner"为true的文档应该怎么办呢?对于这种状况可使用 $or

> db.raffle.find({"$or": [{"ticket_no": 725}, {"winner": true}]})

  还能够这样使用:

> db.raffle.find({"$or": [{"ticket_no": {"$in": [725, 345, 789]}},{"winner": true}]})

  

$not

"$not"是元条件语句,便可以用在任何其余条件之上。就拿取模运算符"$mod"来讲。"$mod"会将查询的值除以第一个给定值,若余数等于第二个给定值则匹配成功:

> db.users.find({"id_num": {"$mod: [5, 1]}})

  上面的查询会返回"id_num"值为1,6,11,16等用户。可是要想返回"id_num"为二、三、四、五、七、八、九、10等用户就要使用"$not"了:

>db.users.find({"id_num": {"$not": {"$mod": [5, 1]}}})

  

条件语义

若是比较一下更新修改器和前面的查询文档,就会发现以$开头的键位于在不一样的位置。在查询中,"$lt"在内层文档,而更新中"$inc"则是外层文档的键。基本能够确定:条件语句是内层文档的键,而修改器则是外层文档的键。

能够对一个键应用多个条件。例如,要查找年龄为20~30的全部用户,能够在"age"键上使用"$gt"和"$lt":

> db.users.find({"age": {"$lt": 30, "$gt": 20}})

  一个键能够有任意多个条件,可是一个键不能对应多个修改器。例如,修改器文档不能勇士含有{"$inc": {"age": 1}, {"$set": {"age": 40}}},由于修改了"age"两次。可是对于查询条件句就没有这种限定。

有一些元操做符也位于外层文档中,好比"$and"、"$or"和"$nor"。他们的使用形式相似:

>db.users.find({"$and": [{"x": {"$lt": 1}}, {"x": 4}]})

  这个会查询匹配哪些"x"字段的值小于1而且等于4的文档,虽然看起来是矛盾的,可是彻底是可能的,好比,若是"x"字段的值是这样的一个{"x": [0, 4]},那么这个文档就与查询条件匹配,可是查询优化器不会对"$and"进行优化,这与其余操做符不一样。若是把上面的查询改为下面这样,效率更加高

>db.users.find({"x": {"$lt": 1, "$in": [4]}})

  

特定类型的查询

在MongoDB中有一些在查询时会有特殊的表现

null

null类型的行为有点奇怪他能够匹配自身,因此有一个包含以下文档的集合:

> db.c.find()
{ "_id" : ObjectId("5cd17b2891b417d2e2ad7467"), "y" : null }
{ "_id" : ObjectId("5cd17b2891b417d2e2ad7468"), "y" : 1 }
{ "_id" : ObjectId("5cd17b2891b417d2e2ad7469"), "y" : 2 }

  就能够按照预期的方式查询"y"键为null的文档:

> db.c.find({"y": null})
{ "_id" : ObjectId("5cd17b2891b417d2e2ad7467"), "y" : null }

  可是null不只会匹配某个键的值为null的文档,并且还会匹配不包含这个键的文档,因此这种匹配还会返回缺乏这个键的全部文档:

> db.c.find({"z": null})
{ "_id" : ObjectId("5cd17b2891b417d2e2ad7467"), "y" : null }
{ "_id" : ObjectId("5cd17b2891b417d2e2ad7468"), "y" : 1 }
{ "_id" : ObjectId("5cd17b2891b417d2e2ad7469"), "y" : 2 }

  若是仅想匹配键值为null的文档,既要检查该键的值是否为null,还要经过"$exists"判断这个键值是否是存在:

> db.c.find({"z": {"$in": [null], "$exists": true}})

  由于没有 "$eq"因此使用"$in"效果是同样的

 

正则表达式

正则表达式能够灵活有效的匹配字符串。例如,想要查找全部名为Joe或者joe的用户,就可使用正则表达式执行不区分大小写的匹配:

> db.users.find({"name": /joe/i})

  系统能够接受正则表达式的标志(i),可是不是必定要有。如今已经匹配了各类大小写组合形式的joe,若是还但愿匹配"joey"这样的值,那么能够稍微修改一下

> db.users.find({"name": /joe?/i})

  正则表达式也能够匹配自身

 

查询数组

查询数组与查询标量的值是同样的。例若有一个水果列表,以下所示:

> db.food.insert({"friut": ["apple", "banana", "peach"]})

  下面查询:

> db.food.find({"friut": "banana"})
{ "_id" : ObjectId("5cd17ef091b417d2e2ad746e"), "friut" : [ "apple", "banana", "peach" ] }

  会成功匹配这个文档,好像咱们在查询一个不合法的文档: {"fruit": "apple", "fruit": "banana", "fruit": "peach"} 

$all

> db.food.find()
{ "_id" : ObjectId("5cd1800f91b417d2e2ad746f"), "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : ObjectId("5cd1800f91b417d2e2ad7470"), "fruit" : [ "apple", "kumquat", "orange" ] }
{ "_id" : ObjectId("5cd1800f91b417d2e2ad7471"), "fruit" : [ "cherry", "banana", "apple" ] }

 咱们要找到既有"apple"又有"banana"的文档,可使用"$all"来查询:

> db.food.find({"fruit": {"$all": ["apple", "banana"]}})
{ "_id" : ObjectId("5cd1800f91b417d2e2ad746f"), "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : ObjectId("5cd1800f91b417d2e2ad7471"), "fruit" : [ "cherry", "banana", "apple" ] }

  若是想查询数组特定位置的元素,需使用key.index语法指定下标:

> db.food.find({"fruit.2": "peach"})
{ "_id" : ObjectId("5cd1800f91b417d2e2ad746f"), "fruit" : [ "apple", "banana", "peach" ] }

  

$size

“$size”对于查询数组来讲也很是有用。顾名思义,能够用它来查询特定长度的数组。例如:

> db.food.find({"fruit": {"$size": 3}})
{ "_id" : ObjectId("5cd1800f91b417d2e2ad746f"), "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : ObjectId("5cd1800f91b417d2e2ad7470"), "fruit" : [ "apple", "kumquat", "orange" ] }
{ "_id" : ObjectId("5cd1800f91b417d2e2ad7471"), "fruit" : [ "cherry", "banana", "apple" ] }

  获得一个长度范围内的文档是一种常见的查询。"$size"并不能与其余查询条件(“$gt”)联合使用,可是这用查询能够在文档中添加一个"size"字段来时间每次"$push", “size”字段就加一,以后能够对这个字段进行查询。

> db.food.update(criteria, {"$push": {"fruit": "strawbery"}, {"$inc": {"size": 1}}})

  可是这种技巧不能和"$addToSet" 操做符同时使用

 

$slice

find的第二个参数是可选的,能够指定须要返回的键。这个特别的"$slice"操做符能够返回某个键匹配的数组元素的一个子集。

加入,假设如今有一个博客文章的文档,咱们但愿返回前十条评论能够这样作:

> db.blog.posts.findOne(criteria, {"comments": {"slice": 10}})

  也能够返回后面10条评论,只要在查询条件中使用-10就能够了:

> db.blog.posts.findOne(criteria, {"comments": {"slice": -10}})

  "$slice"也能够指定偏移值以及但愿返回的元素的数量,来返回元素集合中间位置的某些结果:

> db.blog.posts.findOne(criteria, {"comments": {"$slice": [23, 10]}})

  这个操做会跳过前23个元素,返回第24~33个元素。若是数组不够33个元素,则返回第23个元素后面的全部元素。

 

返回一个匹配的数组元素

若是知道元素的下标,那么"$slice"很是有用。可是有时咱们但愿返回与查询条件相匹配的任意一个数组元素。可使用$操做符获得一个匹配的元素。对于上面的博客文章示例,能够用以下的方式获得Bob的评论:

> db.blogs.find({"comments.name": "bob"}, {"comments.$": 1})
{
      "_id": ObjectId("dfadfafajkldflajdfk32230942903"),
     "comments" : {
           "name": "bob",
           "email": "bob@example.com",
           "content": "good post"
    }  
}

注意这样只会返回第一个匹配的文档。若是Bob在这篇博客文章下写过多条评论,只有“comments”数组中的第一条评论会被返回

 

数组和范围查询的相互做用

  文档中的标量(非数组元素)必须与查询条件中的每一条语句想匹配。例如,若是使用{"x": {"$gt": 10, "$lt": 20}}进行查询,只会匹配"x"键的值大于10 而且小于20的文档。可是,加入某个文档的"x"字段是一个数组,若是"x"键的某个一元素与查询条件的任意一条语句相匹配(查询条件中的每条语句能够匹配不一样的数组元素),那么这个文档也会被返回。

下面用一个例子来详细说明这种状况。假若有以下所示的文档:

> db.test.find()
{ "_id" : ObjectId("5cd2c18cb54d3c27ab3c6b37"), "x" : 5 }
{ "_id" : ObjectId("5cd2c18cb54d3c27ab3c6b38"), "x" : 15 }
{ "_id" : ObjectId("5cd2c18cb54d3c27ab3c6b39"), "x" : 25 }
{ "_id" : ObjectId("5cd2c18cb54d3c27ab3c6b3a"), "x" : [ 5, 25 ] }

若是但愿找到"x"键位于10和20之间的全部文档,直接想到的查询方式是使用

> db.test.find({"x": {"$lt": 20, "$gt": 5}})
{ "_id" : ObjectId("5cd2c18cb54d3c27ab3c6b38"), "x" : 15 }
{ "_id" : ObjectId("5cd2c18cb54d3c27ab3c6b3a"), "x" : [ 5, 25 ] }

5和25都不位于10和20之间,可是这个文档也返回了,由于25与条件查询中的第一个语句(大于10)相匹配5与查询条件中的第二个语句(小于20)相匹配。

这使对数组使用范围查询没有用:范围会匹配任意多元素数组。有几种凡是能够获得预期的行为。

首先,可使用"$elemMatch"要求同时使用查询条件中的两个语句与一个数组元素比较,可是这个有一个问题,"elemMatch"不会匹配费数组元素:

> db.test.find({"x": {"elemMatch": {"$gt": 10, "$lt": 20}}})
>

  不会和{"x": 15}相匹配了,由于只查询数组了。

若是当前查询的字段上建立过索引可使用min,max将查询条件遍历的索引范围限制为"$gt","$lt"的值:

> db.test.find({"x": {"$gt": 10, "$lt": 20}}).min({"x": 10}).max({"x": 20})
{"x": 15}

  

查询内嵌文档

查询内嵌文档

简单的查询

>db.people.find({"name.first": "Joe", "name.last": "Schmoe"})

  可是当文档结构变得更加复杂以后,内嵌文档的匹配须要一些技巧。例如假设有博客文章若干,要找到Joe发表的5分以上的评论。博客文章的结构以下例所示:

> db.blog.find()
{
    "content": "...",
    "comments": [
        {
             "author": "joe",
             "score": 3,
             "comment": "nice post"
        },
       {
             "author": "mary",
             "score": 5,
             "comment": "terrible post"
        }
    ]
}

直接用 > db.blog.find({"comments": {"author": "joe", "score": {"$gte": 5}}}) 也不行由于符合author条件的评论和score条件的评论可能不是同一条评论。也就是说,会返回刚才显示的那个文档,由于“author”: joe 在第一条评论中匹配了,"score": 6在第二条评论中匹配了

要准确的指定一组条件,而没必要指定每一个键,就须要使用"elemMatch".。

> db.blog.find("comments": {"$elemMatch": {"author": "joe", "score": {"$gte": 5}}})

  

$where

可使用任何的JavaScript代码

> db.foo.find({"$where": function() {
    ....
}})    

  若是函数返回true文档就做为结果集的一部分返回;若是为false就不返回。

 

limit skip sort

限制返回结果的数量,忽略必定数量的结果以及排序,全部这些选项必定要在查询被发送到服务器以前指定。

先限制结果数量,可在find后使用limit函数,例如只返回3个结果,能够这样:

> db.c.find().limit(3)

  要是匹配的结果不到三个,则返回匹配的数量。limit指定的是数量上限而非下限。

skip和limit类似

>db.c.find().skip(3)

  上面的操做会略过前三个匹配的文档,而后返回余下的文档。若是集合里面能匹配的文档少于3个,则不会返回任何文档。

sort 接受一个对象做为参数,这个对象是一组键/值对,键对应文档的键名,只表明排序的方向。排序方向能够是1(升序)或者是-1(降序)。若是指定了多个键,则按照这些键被指定的顺序逐个排序,例如按照"username"升序及"age"降序排序,能够这样写:

>db.c.find().sort({"username": 1, "age": -1})

  这三个方法能够组合使用。

对键的排序是有优先级的,顺序以下:

  1. 最小值
  2. null
  3. 数字(整型,长整型,双精度)
  4. 字符串
  5. 对象/文档
  6. 数组
  7. 二进制数据
  8. 对象ID
  9. 布尔型
  10. 日期型
  11. 时间戳
  12. 正则表达式
  13. 最大值
相关文章
相关标签/搜索