MongoDB查询

find简介

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

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

> db.c.find()

将批量返回集合c中所有的文档。

使用条件

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

指定需要返回的键

有时并不需要将文档中所有的键/值对都返回。遇到这种情况,可以通过find、findOne的第二参数来指定想要的键。这样做机会节省传输的数据量,又能节省客户端解码文档的时间和内存消耗。

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

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

这种情况默认"_id"这个键总是被返回的,即使没有指定要返回这个键。

如果我们不希望结果中含有"fatal_weakness"键那么可以如下操作:

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

这样就可以把"_id"键剔除掉。

有一些查询上的限制

当查询时,传递给数据库的查询文档的值必须是常量。也就是不能引用文档中其他键的值。例如保存库存有"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"为2、3、4、5、7、8、9、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. 最大值
原文地址:https://www.cnblogs.com/Stay-J/p/10822146.html