第一部分 MongoDB介绍

  • 第一章 MongoDB简介
  • 1.1 易于使用
  • 1.2 易于扩展
  • 1.3 丰富的功能
  • 1.4 卓越的性能
  • 第二章 MongoDB基础知识
  • 2.1 文档
  • 2.2 集合
  • 2.2.1 动态模式
  • 2.2.2 命名
  • 2.2.3 集合的创建、删除
  • 2.3 数据库
  • 2.3.1 数据库命名
  • 2.3.2 数据库的创建、使用和删除
  • 2.4 数据类型
  • 2.5 MongoDB shell
  • 2.5.1 简单的增、删、改、查:
  • 2.5.2 mongo连接
  • 第三章 文档CRUD
  • 3.1 插入并保存文档
  • 3.2 删除文档
  • 3.3 更新文档
  • 3.3.1 替换文档
  • 3.3.2 使用修改器
  • 一、$set、 $setOnInsert 与 $unset 修改器
  • 二、$inc 增加和减少
  • 三、数组修改器(重点)
  • 1. 添加元素
  • 1.1 `$push`:如果数组已经存在,`$push`会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组。
  • 1.2 `$push`+`$each`:使用`$each`自操作符,可以通过一次`$push`操作添加多个值
  • 1.3 `$push`+`$slice`:可以使数组的最大长度固定
  • 1.4 `$push`+`$slice`+`$sort`:在控制固定数组长度前排序
  • 1.5 `$addToSet`:将数组作为数据集,保证数组内没有重复元素
  • 2. 删除元素
  • 2.1 `$pop`:通过元素位置删除
  • 2.2 `$pull`:通过元素特定条件删除
  • 3. 基于位置的数组修改器
  • 4. 修改器速度
  • 5. upsert、multi
  • 3.4 查询文档
  • 3.4.1 find简介
  • 3.4.2 查询条件
  • 一、比较操作符
  • 二、OR查询:`$or`、`$in`、`$nin`
  • 三、`$not`:不匹配
  • 四、条件语义
  • 五、特定类型的查询
  • 1. null
  • 2. 正则表达式
  • 3.4.3 查询数组
  • 一、`$all`
  • 二、`$size`
  • 三、`$slice`
  • 四、在文档数组中的字段上指定查询条件
  • 1. 在嵌入文档数组中的字段上指定查询条件
  • 2. 使用数组索引来查询嵌入式文档中的字段
  • 五、为文档数组指定多个条件
  • 1. 单个嵌套文档在嵌套字段上满足多个查询条件
  • 2. 元素组合满足标准(数组和范围查询的相互作用)
  • 3.4.4 游标
  • 一、limit、skip和sort
  • 二 、避免使用skip略过大量结果
  • 1. 不用skip对结果分页
  • 2. 随机选取文档


第一章 MongoDB简介

1.1 易于使用

MongoDB是一个面向文档的数据库,而不是关系型数据库,不采用关系模型是为了获得更好的扩展性。
与关系型数据库相比,没有“行”的概念,取而代之的是文档模型,通过在文档中嵌入文档和数组,仅使用一条数据来表现复杂的层次关系。不再有预定义模式:文档的key和value不再是固定的大小。

1.2 易于扩展

MongoDB的设计采用了横向扩展,面向文档的数据模型使他能很容易地在多台服务器之间进行数据分割。MongoDB能自动处理跨集群的数据和负载,自动重新分配文档,以及将用户请求路由到正确的机器上。

1.3 丰富的功能

除了增、删、改、查外还有索引、聚合、特殊的集合类型、文件存储功能

1.4 卓越的性能

MongoDB的一个主要目标是提供卓越的性能,能对文档进行动态填充,也能预分配数据文件以利用额外的空间来换取稳定的性能。MongoDB把尽可能多的内存用作缓存,试图为每次查询自动选择正确的索引。

第二章 MongoDB基础知识

2.1 文档

文档是MongoDB中数据的基本单元,是MongoDB的核心概念。文档就是键值对的一个有续集,非常类似于关系型数据库中的行

文档中的键值对,有很多需要注意的地方:

  • 文档的键是字符串:但不能含有 \0、.、$
  • MongoDB不但区分类型,而且区分大小写,文档中不能有重复的键。
  • 文档中的键值对是有序的。

2.2 集合

集合就是一组文档,相当于关系型数据库中的表

2.2.1 动态模式

集合是动态模式的,意味着一个集合里面的文档可以是各式各样的。
虽然集合是动态的,但是尽量把相似的文档存放在同一集合里面,这样做有以下优点:

  • 便于管理
  • 提高查询速度
  • 把同类型的文档放在一个集合里,数据会更加集中
  • 创建索引时,需要使用文档的附加结构,索引是按照集合来定义的

2.2.2 命名

集合使用名称进行标识,集合名可以是满足下列条件的任意UTF-8字符串:

  • 不能为空字符串
  • 不能包含\0字符,此字符表示集合名结束
  • 不能以system.开头,这是系统集合保留的前缀
  • 不能包含$,某些系统生成的集合中包含 $

子集合:组织集合的一种惯例是使用.分隔不同命名空间的子集合,使用子集合来组织数据非常高效。

2.2.3 集合的创建、删除

  • 创建集合:db.createCollection(name, options)
  • 删除集合:db.collection_name.drop();
  • 查看已存在的合集:show collections

2.3 数据库

在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。

2.3.1 数据库命名

数据库命名可以是满足以下条件的任意UTF-8字符串:

  • 不能是空字符串
  • 不能含有/、\、.、’’、<、>、:、|、?、$、(一个空格)、\0。基本上只能使用ASCII中的字母和数字(数据库最终会变成文件系统里的文件,数据库名就是相应的文件名)
  • 数据库名区分大小写
  • 数据库名最多为64字节

admin(身份验证和特定的服务器端命令运行)、local(这个数据库永远不可以复制)、config(分片信息会存储在config数据库中)是保留的数据库名

命名空间:把数据库名添加到集合名前,得到的集合的完全限定名。(如:cms.blog.posts)

2.3.2 数据库的创建、使用和删除

创建数据库use database_name查看当前指向哪个数据库db选择数据库use database_name查看所有数据库show dbs

2.4 数据类型

  • null: { "x" : null }
  • 布尔型: { "x" : true }
  • 数值: { "x" : 3.14 }
  • 字符串: { "x" : "foobar" }
  • 日期: { "x" : new Date() } 注意时区
  • 正则表达式: { “x” : /foobar/i }
  • 数组: { "x" : ["a","b","c"] }
  • 内嵌文档: { "x" : { "foo" : "bar" } } 文档可以作为建的值
  • 对象id: { "x" : ObjectId() }
  • 二进制数据: 二进制数据是一个任意字节的字符串
  • 代码: { "x" : function() { /* .... */ } }

2.5 MongoDB shell

shell是一个功能完备的JavaScript解释器,可运行任意JavaScript。而且可以使用多行命令,shell会检测输入的JavaScript语句是否完整。取消为输入完成的命令在某行连续三次按下回车键即可。

2.5.1 简单的增、删、改、查:

post ={"tittle":"My Blog Post",
... "content": "Here's my blog post.",
... "date": Date.now()}
  • 创建(增):db.blog.insert(post);
  • 读取(查):db.blog.findOne();
  • 更新(改):post.comment = []; db.blog.update({"title":"My Blog Post"}, post)
  • 删除(删):db.blog.remove({"title": "My Blog Post"})

2.5.2 mongo连接

运行mongo启动shell,启动时shell会连接到MongoDB服务器的test数据库。

在启动shell时指定机器名和端口,就可以连接到一台不同的机器

mongo some-host:3000/myDB

也可以通过–nodb来启动,此时不会连接到任何数据库
上面的命令行与下面的命令行功能相同

mongo --nodb
conn = new Mongo("some-host:3000")
db = conn.getDB("myDB");

第三章 文档CRUD

3.1 插入并保存文档

  • 将一个或多个文档插入到集合中
    db.COLLECTION_NAME.insert(document)
  • 将单个文档插入集合中
    db.COLLECTION_NAME.insertOne(document)
db.inventory.insertOne(
   { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
)
  • 将多个 文档插入到集合中
    db.COLLECTION_NAME.insertMany([document,document,document])
db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } }}
])
db.collection.insertOne(
   <document>,
   { writeConcern: <document> }
)

db.collection.insertMany(
   [ <document 1> , <document 2>, ... ],
   { writeConcern: <document>, ordered: <boolean> }
)
  • document:要写入的文档。
  • writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
  • ordered:指定是否按顺序写入,默认 true,按顺序写入。

3.2 删除文档

db.inventory.insertMany( [
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" },
] );
  • 删除一个与指定过滤器匹配的文档db.collection.deleteOne()
db.inventory.deleteOne( { status: "D" } )
  • 删除所有与指定过滤器匹配的文档db.collection.deleteMany()
db.inventory.deleteMany({ status : "A" })
  • 删除单个文档或与指定过滤器匹配的所有文档db.collection.remove()

如果要清空整个集合,使用drop直接删除集合会更快

3.3 更新文档

3.3.1 替换文档

db.inventory.insertMany( [
   { item: "mousepad", qty: 25, size: { h: 19, w: 22.85, uom: "cm" }, status: "P" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" },
   { item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
] );
  • 更新单个文件db.collection.updateOne(<filter>, <update>, <options>)
db.inventory.updateOne(
   { item: "paper" },
   {
     $set: { "size.uom": "cm", status: "P" },
     $currentDate: { lastModified: true }
   }
)

使用$currentDate运算符将lastModified字段的值更新为当前日期。如果 lastModified字段不存在, $currentDate将创建该字段。

  • 更新多个文件db.collection.updateMany(<filter>, <update>, <options>)
db.inventory.updateMany(
   { "qty": { $lt: 50 } },
   {
     $set: { "size.uom": "in", status: "P" },
     $currentDate: { lastModified: true }
   }
)
  • 更换文件db.collection.replaceOne(<filter>, <update>, <options>)
db.inventory.replaceOne(
   { item: "paper" },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)

要替换文档中除_id 字段以外的全部内容,请将一个全新的文档作为第二个参数传递给 db.collection.replaceOne()。

3.3.2 使用修改器

db.products.insert({
	"_id" : 100,
	"sku" : "abc123",
	"details" : {"model":"14Q2","make":"xyz"},
	"tags" : ["apparel","clothing"],
	"ratings" : [{"by":"ijk","rating":4}]
});
一、$set、 $setOnInsert 与 $unset 修改器

$set用来指定一个字段的值,如果不存在则创建。

db.products.update(
   { _id: 100 },
   { $set:
      {
        quantity: 500,
        details: { model: "14Q3", make: "xyz" },
        tags: [ "coats", "outerwear", "clothing" ]
      }
   }
)

集合中内嵌文件
db.products.update(
   { _id: 100 },
   { $set: { "details.make": "zzz" } }
)

集合中元素的数组
db.products.update(
   { _id: 100 },
   { $set:
      { "tags.1": "rain gear", "ratings.0.rating": 2 }
   }
)

$setOnInsert用来删除指定的键。

如果使用upsert:true进行更新操作会导致插入文档,然后$setOnInsert 将指定的值分配给文档中的字段。如果更新操作未导致插入,则不 $setOnInsert执行任何操作。

数据存在时不进行操作:
db.products.update(
  { _id: 1 },
  { $setOnInsert: { defaultQty: 100 } },
  { upsert: true }
)

如果db.collection.update()用UPSERT:真正找到了一个匹配的文件,然后MongoDB的执行更新,应用$set操作,但是忽略了$setOnInsert操作。
db.users.update(
  { _id: 1 },
  {
     $set: { "sku": 123 },
     $setOnInsert: {"bbb": 456}
  },
  { upsert: true }
)

$unset用来删除指定的键。

db.products.update(
   { _id: 100 },
   { $unset:
      { quantity: 1 }
   }
)
二、$inc 增加和减少

$inc 修改器用来增加已有键的值,或者该键不存在就创建一个

{
  _id: 1,
  sku: "abc123",
  quantity: 10,
  metrics: {orders: 2,ratings: 3.5}
}

db.products.update(
   { sku: "abc123" },
   { $inc: { quantity: -2, "metrics.orders": 1 } }
)

执行结果:
{
   "_id" : 1,
   "sku" : "abc123",
   "quantity" : 8,
   "metrics" : { "orders" : 3, "ratings" : 3.5 }
}

$inc 只能用于整型、长整型、双精度浮点型的值,必须是数字,不能使用字符串、数组或其他非数字的值。

三、数组修改器(重点)
1. 添加元素
1.1 $push:如果数组已经存在,$push会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组。
db.students.update(
   { _id: 1 },
   { $push: { scores: 89 } }
)
{ "_id" : 1, "scores" : [ 89 ] }
1.2 $push+$each:使用$each自操作符,可以通过一次$push操作添加多个值
db.students.update(
   { _id: 1 },
   { $push: { scores: { $each: [ 90, 92, 85 ] } } }
)
{ "_id" : 1, "scores" : [ 89, 90, 92, 85 ] }

如果指定的数组中只含有一个元素,那这个操作就等同于没有使用$each的普通$push操作

1.3 $push+$slice:可以使数组的最大长度固定
db.students.update(
   { _id: 1 },
   { $push: { scores: { $each: [ 80, 78, 86 ], $slice: -5 } } }
)
{ "_id" : 1, "scores" : [ 92, 85, 80, 78, 86 ] }
  • $slice值为0时:将数组更新为空数组。
  • $slice值为正数时:将数组更新为只包含前num个元素。
  • $slice值为负数时:将数组更新为只包含后加入的num个元素。
1.4 $push+$slice+$sort:在控制固定数组长度前排序

不能只将$slice或者$sort$push配合使用,且必须使用$each

{
   "_id" : 5,
   "quizzes" : [
      { "wk": 1, "score" : 10 },{ "wk": 2, "score" : 8 },
      { "wk": 3, "score" : 5 },{ "wk": 4, "score" : 6 }
   ]
}

db.students.update(
   { _id: 5 },
   {
     $push: {
       quizzes: {
          $each: [ { wk: 5, score: 8 }, { wk: 6, score: 7 }, { wk: 7, score: 6 } ],
          $sort: { score: -1 },
          $slice: 3
       }
     }
   }
)

{
  "_id" : 5,
  "quizzes" : [
     { "wk" : 1, "score" : 10 },{ "wk" : 2, "score" : 8 },{ "wk" : 5, "score" : 8 }
  ]
}
1.5 $addToSet:将数组作为数据集,保证数组内没有重复元素

$addToSet仅确保没有重复项添加到集合中,并且不影响现有重复项。 $addToSet不保证修改集中的元素有特定的顺序。

  • 如果您$addToSet在不是数组的字段上使用,则操作将失败。
{ _id: 1, colors: "blue,green,red" }

db.foo.update(
   { _id: 1 },
   { $addToSet: { colors: "c" } }
)
  • 如果该值为数组,$addToSet则将整个数组作为单个元素附加。
{ _id: 1, letters: ["a", "b"] }

db.test.update(
   { _id: 1 },
   { $addToSet: { letters: [ "c", "d" ] } }
)

{ _id: 1, letters: [ "a", "b", [ "c", "d" ] ] }
  • 正确添加
{ _id: 1, item: "polarizing_filter", tags: [ "electronics", "camera" ] }

db.inventory.update(
   { _id: 1 },
   { $addToSet: { tags: "accessories" } }
)

{ _id: 1, item: "polarizing_filter", tags: [ "electronics", "camera", "polarizing_filter" ] }
  • $addToSet+$each
db.inventory.update(
   { _id: 2 },
   { $addToSet: { tags: { $each: [ "camera", "electronics", "accessories" ] } } })
2. 删除元素
2.1 $pop:通过元素位置删除

若是把数组看成队列或者栈,可以用$pop从数组的任何一端删除元素

  • { "$pop": { "key": 1 } }:从数组末尾删除一个元素
db.students.update( { _id: 1 }, { $pop: { scores: 1 } } )
  • { "$pop": { "key": -1 } }:从数组头部删除一个元素
db.students.update( { _id: 1 }, { $pop: { scores: -1 } } )
2.2 $pull:通过元素特定条件删除
  • 删除在数组中的元素
{ _id: 1, votes: [ 3, 5, 6, 7, 7, 8 ] }
db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )
{ _id: 1, votes: [  3,  5 ] }
  • 删除
{
   _id: 1,
   results: [
      { item: "A", score: 5 },
      { item: "B", score: 8, comment: "Strongly agree" },
      { item: "B", score: 8, comment: "Strongly agree2" }
   ]
}

db.survey.update(
  { },
  { $pull: { results: { score: 8 , item: "B" } } },
  { multi: true }
)

{
   "_id" : 1,
   "results" : [ { "item" : "A", "score" : 5 } ]
}

$pull会将所有匹配的文档删除,而不是只删除一个。

3. 基于位置的数组修改器

位置$运算符可在不显式指定数组中元素位置的情况下标识要更新的数组中的元素,但只更新第一个匹配的元素。

db.students.insert([
   { "_id" : 1, "grades" : [ 85, 80, 80 ] },
   { "_id" : 2, "grades" : [ 88, 90, 92 ] },
   { "_id" : 3, "grades" : [ 85, 100, 90 ] }
])

db.students.updateOne(
   { _id: 1, grades: 80 },
   { $set: { "grades.$" : 82 } }
)

{ "_id" : 1, "grades" : [ 85, 82, 80 ] }
{ "_id" : 2, "grades" : [ 88, 90, 92 ] }
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }

db.students.updateOne(
   { _id: 1, grades: 80 },
   { $set: { "grades.2" : 82 } }
)
  • $unset运算符一起使用时,位置 $运算符不会从数组中删除匹配的元素,而是将其设置为null。
4. 修改器速度

填充因子:是MongoDB为每一个新文档预留的增长空间,可以运行db.coll.stats()查看填充因子。
当文档插入到MongoDB中时,依次插入的文档在磁盘上的位置是相邻的,因此如果一个文档变大了,原来的位置就放不下这个文档了,这个文档就会被移动到集合中的另一个位置,当MongoDB不得不移动一个文档时,就会修改集合的填充因子。
当文档增大之后,填充因子随之增大到1.5,为每个新文档预留其一半大小的空间作为增长空间。如果随后的更新导致了更多次的文档移动,填充因子会持续变大,如果文档不在移动,填充因子会慢慢降低。如果程序进行插入和删除时会进行大量的移动或者是经常的打乱数据,就会造成碎片化问题,占用大量空间。
可以使用usePowerOf2Sizes选项以提高磁盘的复用率,这样进行空间分配时,得到的块大小都是2的幂,但是会导致分配不那么高效。

5. upsert、multi
db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>,
     collation: <document>,
     arrayFilters: [ <filterdocument1>, ... ]
   }
)

upsert:可选的。如果设置为true,则在没有文档与查询条件匹配时创建新文档。默认的 value 是false,当没有找到 match 时,它不会插入新文档。
multi:可选的。如果设置为true,则更新符合query条件的多个文档。如果设置为false,则更新一个文档。默认的 value 是false。

3.4 查询文档

3.4.1 find简介

db.coll.find({"age": 27})
db.coll.find({"username": "joe", "age": 27})
  • find的第一个参数决定了要返回那些文档,这个参数是一个文档,用于指定查询条件。可以向查询文档加入多个键/值对,“条件1AND条件2AND…AND条件N”。
db.coll.find({}, {"username": 1, "email": 0})
  • find的第二个参数指定想要返回的键,默认情况下"_id"这个键总被返回,参数为0时,不返回,参数为1时返回该键。

find传递给数据库的查询文档的值必须是常量,不能引用其他键的值。

3.4.2 查询条件

一、比较操作符

$lt代表<、$lte代表≤、$gt代表>、$gte代表≥、$ne代表≠、$eq代表=、$in代表包含、$nin代表不包含

查询age小于等于17 ( ≤ 17 ) 的数据:
db.coll.find({"age": {"$lte": 17}});

查询age大于等于17小于等于35 ( 17 ≤ age ≤ 35 ) 的数据:
db.coll.find({"age": {"$gte": 17, "$lte": 35}});
二、OR查询:$or$in$nin

MongoDB中有两种方式进行OR查询:$in可以用来查询一个键的多个值,$or可以在多个键中查询任意的给定值。

  • $in可以指定不同类型的条件和值
db.users.find( { user_id: { $in: [ 1234, "joe" ] } } )
  • $nin将返回与数组中所有条件都不匹配的文档
db.users.find( { ticket_no: { $in: [ 123, 456, 222 ] } } )
  • $or可以包含其他条件(或)
db.users.find( { "$or": [ { ticket_no: 725 }, { winner: true } ] } )
  • $no + $in
db.users.find( { "$or": [ { ticket_no: { "$in": [ 123, 456, 222 ] } }, { winner: true } ] } )
  • 使用普通的AND型查询时,总是希望尽可能用最少的条件来限定结果范围,OR型查询正相反,第一个条件应该尽可能匹配更多的文档,这样才高效。
三、$not:不匹配

$not元条件句,即可以用在任何条件之上。与正则表达式联合使用时极为有用,用来查找那些与特定模式不匹配的文档。

db.inventory.find( { price: { $not: { $gt: 1.99 } } } )
四、条件语义
  • 与更新修改器相比,在查询中条件语句($lt$lte$gt$gte$ne$eq$in$nin)是内层文档的键,而修改器则是外层文档的键。
  • 一个键可以有任意多个条件,但是一个键不能对应多个更新修改器。
错误❌:{ "$inc": { "age": 1 }, "$set": { "age": 40 } }
  • 有一些元操作符也位于外层文档中,比如$and$not$or$nor
五、特定类型的查询
1. null
  • null不仅会匹配某个键的值为null的文档,而且还会匹配不包含这个键的文档。
{ "_id": 1, "y": null }
{ "_id": 2, "y": 1 }
{ "_id": 3, "y": 2 }

db.coll.find( { "y": null } )
{ "_id": 1, "y": null }
  • 仅想匹配键值为null的文档,既要检查该键得值是否为null,还要通过exists条件判定键的存在。
db.coll.find( { "2": { "$in": [null], "$exists": true } } )
2. 正则表达式

正则表达式能够灵活有效的匹配字符串,也可以匹配自身。

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

3.4.3 查询数组

db.inventory.insertMany([
    { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, 
    { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
    { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
    { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },  
    { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
一、$all

如果需要通过多个元素来匹配数组,就要使用$all,跟数组中元素顺序无关。

db.inventory.find( { tags: { $all: ["red", "blank"] } } )
二、$size

使用$size运算符可按元素数量查询数组,但是$size不能与其他查询条件组合使用,但是可以通过在文档中添加一个size字段方法来实现。

db.inventory.find( { "tags": { $size: 3 } } )
三、$slice

$slice作为find第二个参数可选,如没有指定,则查询并返回文档中所有的键。

db.inventory.insertMany( [
    { item: "journal", instock: [ 
      { warehouse: "A", qty: 5 }, 
      { warehouse: "B", qty: 10 }, 
      { warehouse: "C", qty: 15 }, 
      { warehouse: "D", qty: 20 }, 
      { warehouse: "E", qty: 25 } ] }
]);
  • $slice操作符可以返回某个键匹配的数组元素的一个子集。
返回符合条件的第一条数据
db.inventory.find({"item": "journal"},{"instock":{"$slice": 1}});

返回符合条件的最后一条数据
db.inventory.find({"item": "journal"},{"instock":{"$slice": -1}});
  • 也可以指定偏移值以及希望返回的元素数量。
db.inventory.find({"item": "journal"},{"instock":{"$slice": [2, 3]}});
四、在文档数组中的字段上指定查询条件
1. 在嵌入文档数组中的字段上指定查询条件

如果您不知道嵌套在数组中的文档的索引位置,请使用点(.)和嵌套文档中的字段名称来连接数组字段的名称。

db.inventory.find( { 'instock.qty': { $lte: 20 } } )
2. 使用数组索引来查询嵌入式文档中的字段

如果知道元素下标,那么$slice非常有用,可以使用$操作符得到一个匹配的元素。使用点表示法,可以为文档中特定索引或数组位置的字段指定查询条件。 该数组使用基于零的索引。

db.inventory.find( { 'instock.0.qty': { $lte: 20 } } )
五、为文档数组指定多个条件
1. 单个嵌套文档在嵌套字段上满足多个查询条件

使用$elemMatch运算符可在一组嵌入式文档上指定多个条件,以使至少一个嵌入式文档满足所有指定条件。

db.inventory.find( { "instock": { $elemMatch: { qty: 5, warehouse: "A" } } } )
2. 元素组合满足标准(数组和范围查询的相互作用)
db.inventory.find( { "instock.qty": { $gt: 10,  $lte: 20 } } )

3.4.4 游标

游标类继承了JavaScript的迭代器接口。
在mongo shell中,当使用var关键字将find() 方法返回的游标分配给变量时,游标不会自动进行迭代。

for(i = 0; i < 1000; i++){
	db.users.insert({"type": i})
}
  • 要迭代结果,可以使用游标的next方法,也可以使用hasNext来查看游标中是否还有返回结果
var myCursor = db.users.find( { type: 2 } );

while (myCursor.hasNext()) {
   printjson(myCursor.next());
}
  • forEach
var myCursor =  db.users.find( { type: 2 } );

myCursor.forEach(printjson);
一、limit、skip和sort
  • limit:限定结果数量
db.coll.find().limit(3)
  • skip:略过前几个匹配的文档,略过过多,会导致性能问题
db.coll.find().skip(3)
  • sort:按照指定键进行排序,1升序、-1降序
db.coll.find().sort({username: 1, age: -1})
二 、避免使用skip略过大量结果
1. 不用skip对结果分页
  • 用skip略过少量的文档还不错,但是要是数量非常多的话,skip就会变得很慢,因为要先找到需要被略过的数据,然后再抛弃这些数据。
  • 一般来讲可以找到一种方法在不使用skip的情况下实现分页,比如按照date降序来显示文档列表。
2. 随机选取文档
  • 很笨的方法
var total = db.foo.count()
var random = Math.floor(Math.random()*total)
db.foo.find().skip(random).limit(1)
  • 插入文档时给每个文档添加一个随机键。
db.people.insert({"name": "Joe", "random": Math.random()})