MongoDB 基础系列十五:增删查改 CRUD 之 Query

前言

此篇博文是 Mongdb 基础系列之一;

本文为作者的原创作品,转载需注明出处;

简介

本文主要是增对特定语言如何执行插入操作的讲解;官网上对各个热门语言如何对 MongoDB 执行 query 操作分别作了比较详细的描述;这里呢,因为 mongo shell 是在平时调试、检查、运维最常用的操作,所以,笔者这里重点考察如何通过 mongo shell 来进行 query 操作;要注意的是,Mongo Shell 是通过 javascript 实现的交互式应用;

在讲解 query 操作以前,假设,我们有如下这些测试用例,在各个章节没有特别指明测试数据的前提下,将会使用下面的测试数据;

1
2
3
4
5
6
7
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: "A" },
{ 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" }
]);

查询基础

Select All Documents in a Collection

1
> db.inventory.find( {} )

等价于 SQL

1
SELECT * FROM inventory

Specify Equality Condition

使用<field>:<value>来指定相等表达式,格式如下,Query Filter Documents

1
{ <field1>: <value1>, ... }

在 invertory 中查询 status 为 D 的所有 documents,

1
db.inventory.find( { status: "D" } )

等价于执行 SQL

1
SELECT * FROM inventory WHERE status = "D"

Specify Conditions Using Query Operators

在查询的时候,可以使用 query operatorss 来指定 conditions,

1
{ <field1>: { <operator1>: <value1> }, ... }

比如说,我们要查询 status 是 “A” 或者 “D” 中的 documents,使用$in操作符;

1
db.inventory.find( { status: { $in: [ "A", "D" ] } } )

等价于执行如下的 SQL

1
SELECT * FROM inventory WHERE status in ("A", "D")

Specify AND Conditions

1
> db.inventory.find( { status: "A", qty: { $lt: 30 } } )

上面的查询试图找到 inventory 中 status 为 A 且 qty 字段值小于($lt) 30 的 documents;注意,MongoDB 查询过程中,在没有其它限定条件,比如$or的情况下,两个相邻的条件自动的被翻译为 AND 操作;

等价于执行 SQL,

1
SELECT * FROM inventory WHERE status = "A" AND qty < 30

Specify OR Conditions

1
> db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )

Specify AND Conditions 稍有不同,通过$or操作符,通过格式 $or: [ { ... }, { ... } ] 来通过 OR 连接两个不同的查询条件,

等价于执行 SQL,

1
SELECT * FROM inventory WHERE status = "A" OR qty < 30

Specify AND as well as OR Conditions

1
2
3
4
db.inventory.find( {
status: "A",
$or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )

相当于执行 SQL

1
SELECT * FROM inventory WHERE status = "A" AND ( qty < 30 OR item LIKE "p%")

查询(单个)嵌入式文档

内嵌文档完整匹配

比如,我们想对嵌入式文档{ h: 14, w: 21, uom: "cm" }做全值匹配,使用下面这个方式即可,

1
> db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )

表示是对 size 字段进行全值匹配;特别需要注意的是,匹配嵌入式文档的字段名的顺序不能颠倒,

1
db.inventory.find(  { size: { w: 21, h: 14, uom: "cm" } }  )

如果是像上述这样进行查询,是查询不到任何结果的;

最后,需要记忆的是,完整匹配内嵌文档的时候,键就是内嵌文档在外部文档中所对应的字段名既size

匹配内嵌文档字段

在内嵌文档字段上使用相等匹配

1
> db.inventory.find( { "size.uom": "in" } )

这样,我们在( size )内嵌文档的 uom 字段上使用了相等比较,返回其值等于 _in_ 的所有内嵌文档;

最后,需要记忆的是,匹配内嵌文档字段的时候,键对应的是内嵌文档中的字段,用size.uom表示;

在内嵌文档字段上使用查询操作符

使用查询操作符的标准格式为,

1
{ <field1>: { <operator1>: <value1> }, ... }

下面我们试图查询( size )嵌入文档中高度小于 15 的所有内嵌文档,

1
> db.inventory.find( { "size.h": { $lt: 15 } } )

使用 AND 条件

下面这个例子,我们通过AND方式既查询内嵌文档的字段并且同时查询 document 中的信息,

1
> db.inventory.find( { "size.h": { $lt: 15 }, "size.uom": "in", status: "D" } )

可见,可以同时结合内嵌文档和外部文档同时进行查询;

对文档中的 Array 进行查询

测试数据准备

假设我们有下面这些测试数据,该测试数据将会作用到下面的本小节相关的所有内容;

1
2
3
4
5
6
7
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 ] }
]);

使用队列进行查询

注意,这种查找方式需要使用[...]方括号的方式来表示输入的查询条件是一个队列;

全匹配

1
> db.inventory.find( { tags: ["red", "blank"] } )

上面这个查询将试图精确的匹配 Array tags ;要能精确全部匹配,注意两点,查询参数值的顺序且完全包含所有相关元素;全匹配后将会匹配并输出如下的结果,

1
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bb"), "item" : "notebook", "qty" : 50, "tags" : [ "red", "blank" ], "dim_cm" : [ 14, 21 ] }

部分匹配(包含关系)

另外,如果只要包含了 readblank 两个元素的队列都需要被匹配,并且不关心其顺序,那么我们可以使用 $all 操作符;

1
> db.inventory.find( { tags: { $all: ["red", "blank"] } } )

将会输出如下结果,凡是包含了 redblank 元素的相关 Array 都会返回;

1
2
3
4
{ "_id" : ObjectId("5976a8dfcb568ef80824a9ba"), "item" : "journal", "qty" : 25, "tags" : [ "blank", "red" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bb"), "item" : "notebook", "qty" : 50, "tags" : [ "red", "blank" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bc"), "item" : "paper", "qty" : 100, "tags" : [ "red", "blank", "plain" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bd"), "item" : "planner", "qty" : 75, "tags" : [ "blank", "red" ], "dim_cm" : [ 22.85, 30 ] }

使用普通值进行查询

注意,这种查询方式与通过队列进行查询的方式不同,各个查询条件对应的就是一个普通的值而非一个队列;

包含了某一个元素

通过 Array 中的某一个元素进行查找,使用如下的格式来进行查找,其中 <value> 是 Array 中的一个元素;

1
{ <field>: <value> }

来看一个例子,查找 tags 中包含了 red 的队列

1
> db.inventory.find( { tags: "red" } )

将会输出所有 tags 队列中包含了 red 元素的 documents,

1
2
3
4
{ "_id" : ObjectId("5976a8dfcb568ef80824a9ba"), "item" : "journal", "qty" : 25, "tags" : [ "blank", "red" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bb"), "item" : "notebook", "qty" : 50, "tags" : [ "red", "blank" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bc"), "item" : "paper", "qty" : 100, "tags" : [ "red", "blank", "plain" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bd"), "item" : "planner", "qty" : 75, "tags" : [ "blank", "red" ], "dim_cm" : [ 22.85, 30 ] }

使用查询操作符

使用单个查询操作符

使用查询操作符来对 Array 进行查找的标准格式如下,

1
{ <array field>: { <operator1>: <value1>, ... } }

比如,我们试图找到 dim_cm array 中至少包含一个大于 25 的元素的 documents,

1
> db.inventory.find( { dim_cm: { $gt: 25 } } )

输出结果,这样便输出了 dim_cm 队列中包含了大于 25 元素的 documents,只有一个满足条件,

1
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bd"), "item" : "planner", "qty" : 75, "tags" : [ "blank", "red" ], "dim_cm" : [ 22.85, 30 ] }

使用多个查询操作符

将过多个滤条件以 OR 的方式匹配队列元素

该条件在官网上称作符合过滤条件( Compound Filter Condition );

来看一个例子,下面的例子使用了复合过滤条件( Compound Filter Conditions ),

1
> db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } )

输出结果,

1
2
3
4
{ "_id" : ObjectId("5976a8dfcb568ef80824a9ba"), "item" : "journal", "qty" : 25, "tags" : [ "blank", "red" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bb"), "item" : "notebook", "qty" : 50, "tags" : [ "red", "blank" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bc"), "item" : "paper", "qty" : 100, "tags" : [ "red", "blank", "plain" ], "dim_cm" : [ 14, 21 ] }
{ "_id" : ObjectId("5976a8dfcb568ef80824a9be"), "item" : "postcard", "qty" : 45, "tags" : [ "blue" ], "dim_cm" : [ 10, 15.25 ] }

可以看到,复合过滤条件其实执行的是OR的关系查询,只要队列元素中某一个元素是大于 15 或者 OR 是小于 20 的 documents,都会被匹配并被作为结果输出;也就是说,这种查询方式是,只要队列中有某一个元素满足其中的一个条件既算是该 document 匹配了;

将多个过滤条件以 AND 的方式匹配队列元素

这里使用到了操作符$elemMatch,它的查询方式是,只要队列中有某一个元素同时满足所有的条件既算是该 document 匹配了;来看一个例子,

1
> db.inventory.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )

输出结果,

1
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bd"), "item" : "planner", "qty" : 75, "tags" : [ "blank", "red" ], "dim_cm" : [ 22.85, 30 ] }

可以看到,dim_cm 队列中的第一个元素 22.85 同时满足了查询条件{ $gt: 22, $lt: 30 },所以,该 document 将作为结果输出;

根据数组坐标定位元素进行查询

1
> db.inventory.find( { "dim_cm.1": { $gt: 25 } } )

该查询结果返回,

1
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bd"), "item" : "planner", "qty" : 75, "tags" : [ "blank", "red" ], "dim_cm" : [ 22.85, 30 ] }

上面的查询通过 <field> 的值dim_cm.1指定了将查询操作符 $gt 作用在 dim_cm 队列中的第二个元素上进行查询;注意,坐标是从 0 开始;

通过队列的长度进行匹配查询

1
> db.inventory.find( { "tags": { $size: 3 } } )

执行结果,

1
{ "_id" : ObjectId("5976a8dfcb568ef80824a9bc"), "item" : "paper", "qty" : 100, "tags" : [ "red", "blank", "plain" ], "dim_cm" : [ 14, 21 ] }

可以看到,当且仅当 tags 队列中有三个元素的 elements 才会返回;注意,这里是通过 $size 操作符来指定该查询动作的;

查询(多个)嵌入式文档

这里的多个实际上指的是一个 document 的字段包含多个嵌入式的文档 documents;

后记,要特别特别注意的是,这里所谓的多个嵌入式文档在数据结构上仍然是一个数组

测试数据准备

1
2
3
4
5
6
7
db.inventory.insertMany( [
{ item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
{ item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
{ item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
{ item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);

可以看到,instock 是由多个嵌入式 document 所组成;

全匹配

1
> db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )

返回的查询结果,

1
{ "_id" : ObjectId("5976da82cb568ef80824a9bf"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }

可见,只要多个内嵌 documents 中只要有一个完全匹配了上述的查询条件,则将会返回该 document;

这个非常类似于前面介绍的内嵌文档完整匹配的内容,只是这里针对一个字段,内嵌的是多个 documents 的情况;

要特别注意的是,要完全匹配某一个内嵌文档,顺序不能乱,元素不能少;如果是像下面这样去查询,是不会返回任何结果的,

1
> db.inventory.find( { "instock": { qty: 5, warehouse: "A" } } )

使用单个条件

使用 document 下标进行查询匹配

对多个嵌入文档进行查询的时候,可以通过指定下标来表示对第几个 document 进行匹配查询;如下,

1
> db.inventory.find( { 'instock.0.qty': { $lte: 20 } } )

上面这个查询的条件是,匹配instock的第一个嵌入文档,该文档中的 qty 字段值小于等于 20;注意,坐标是从0开始;

输出结果为,

1
2
3
{ "_id" : ObjectId("5976da82cb568ef80824a9bf"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c0"), "item" : "notebook", "instock" : [ { "warehouse" : "C", "qty" : 5 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c3"), "item" : "postcard", "instock" : [ { "warehouse" : "B", "qty" : 15 }, { "warehouse" : "C", "qty" : 35 } ] }

匹配所有嵌入文档

很多时候,我们并不非常清楚自己需要查询的 document 的坐标位置,这个时候,我们可以针对所有的嵌入文档进行查询;如下,

1
> db.inventory.find( { 'instock.qty': { $lte: 20 } } )

上述查询就没有指定坐标值,则表示对 instock 字段中的所有内嵌文档进行查询,得到如下结果,

1
2
3
4
5
{ "_id" : ObjectId("5976da82cb568ef80824a9bf"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c0"), "item" : "notebook", "instock" : [ { "warehouse" : "C", "qty" : 5 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c1"), "item" : "paper", "instock" : [ { "warehouse" : "A", "qty" : 60 }, { "warehouse" : "B", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c2"), "item" : "planner", "instock" : [ { "warehouse" : "A", "qty" : 40 }, { "warehouse" : "B", "qty" : 5 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c3"), "item" : "postcard", "instock" : [ { "warehouse" : "B", "qty" : 15 }, { "warehouse" : "C", "qty" : 35 } ] }

使用多个条件

将多个过滤条件以 OR 的方式匹配 Document

这个和之前讨论到的将过多个滤条件以 OR 的方式匹配队列元素是类同的,只是前面作用到的是队列中的每个元素,而这里作用到的是多个内嵌文档中的一个文档;

1
> db.inventory.find( { "instock.qty": { $gt: 10, $lte: 20 } } )

输出结果,

1
2
3
4
{ "_id" : ObjectId("5976da82cb568ef80824a9bf"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c1"), "item" : "paper", "instock" : [ { "warehouse" : "A", "qty" : 60 }, { "warehouse" : "B", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c2"), "item" : "planner", "instock" : [ { "warehouse" : "A", "qty" : 40 }, { "warehouse" : "B", "qty" : 5 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c3"), "item" : "postcard", "instock" : [ { "warehouse" : "B", "qty" : 15 }, { "warehouse" : "C", "qty" : 35 } ] }

可以看到,只要任意一个内嵌文档满足了两个条件中的一个,该外部 document 将作为结果输出;在来看一个例子,

1
> db.inventory.find( { "instock.qty": 5, "instock.warehouse": "A" } )

输出,

1
2
{ "_id" : ObjectId("5976da82cb568ef80824a9bf"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c2"), "item" : "planner", "instock" : [ { "warehouse" : "A", "qty" : 40 }, { "warehouse" : "B", "qty" : 5 } ] }

可以看到,只要任何一个内嵌文档满足了其中的一个条件,那么该外部 document 将会作为结果输出;

将多个过滤条件以 AND 的方式匹配 Document

好的,前文讲述了如何将多个条件以 OR 的方式来匹配 document,那么这里,我们看看如何将多个条件以 AND 的方式来作用到 Document;和将多个过滤条件以 AND 的方式匹配队列元素类似,这里也是通过使用$elemMatch操作符,来实现这种查询,来看这样一个例子,

1
> db.inventory.find( { "instock": { $elemMatch: { qty: 5, warehouse: "A" } } } )

输出结果如下,

1
{ "_id" : ObjectId("5976da82cb568ef80824a9bf"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }

可以看到,必须至少有一个内嵌 document 同时满足这两个查询条件,结果才会输出;同理,在看下面这个例子,

1
db.inventory.find( { "instock": { $elemMatch: { qty: { $gt: 10, $lte: 20 } } } } )

输出如下结果,

1
2
3
{ "_id" : ObjectId("5976da82cb568ef80824a9bf"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c1"), "item" : "paper", "instock" : [ { "warehouse" : "A", "qty" : 60 }, { "warehouse" : "B", "qty" : 15 } ] }
{ "_id" : ObjectId("5976da82cb568ef80824a9c3"), "item" : "postcard", "instock" : [ { "warehouse" : "B", "qty" : 15 }, { "warehouse" : "C", "qty" : 35 } ] }

限定返回字段

测试数据

假设,我们有如下的测试数据,

1
2
3
4
5
6
7
db.inventory.insertMany( [
{ item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
{ item: "notebook", status: "A", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
{ item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
{ item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
{ item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);

返回所有字段

默认情况下,没有对返回结果的字段做限制的前提下,返回结果将会返回所有的字段,

1
> db.inventory.find( { status: "A" } )

等价于执行 SQL,

1
SELECT * from inventory WHERE status = "A"

返回指定的字段集

通过将<field>设置为1将字段投影( projection )到输出结果中;看下面这个例子,

1
> db.inventory.find( { status: "A" }, { item: 1, status: 1 } )

输出结果,

1
2
3
{ "_id" : ObjectId("5976f17ecb568ef80824a9c4"), "item" : "journal", "status" : "A" }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c5"), "item" : "notebook", "status" : "A" }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c8"), "item" : "postcard", "status" : "A" }

可以看到,返回的结果集中包含了我们所设置的字段,itemstatus 以及一个默认输出的字段 _id

相当于执行下面的这段 SQL,

1
SELECT _id, item, status from inventory WHERE status = "A"

如何不输出 _id 字段

1
> db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )

这样,结果集中就不会再包含 _id 字段再包含进去了;相当于执行 SQL,

1
SELECT item, status from inventory WHERE status = "A"

指定不返回的字段集

1
> db.inventory.find( { status: "A" }, { status: 0, instock: 0 } )

该查询将会返回所有的结果字段除了 statusinstock

需要注意的是,除了 _id 这种特殊情况,你不能同时指定 inclusiong 和 exclusion 的语句;

限定(单个)嵌入文档的返回字段集

这里的单个是指,某个文档字段包含只包含一个内嵌的 document;

返回嵌入文档中的指定字段集

1
2
3
4
db.inventory.find(
{ status: "A" },
{ item: 1, status: 1, "size.uom": 1 }
)

返回,

1
2
3
{ "_id" : ObjectId("5976f17ecb568ef80824a9c4"), "item" : "journal", "status" : "A", "size" : { "uom" : "cm" } }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c5"), "item" : "notebook", "status" : "A", "size" : { "uom" : "in" } }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c8"), "item" : "postcard", "status" : "A", "size" : { "uom" : "cm" } }

正如结果中所看到的那样,针对 size 中的嵌入式文档,只会返回 uom 字段的结果;

不返回嵌入文档的某些字段集

不返回嵌入式文档的某些字段,但是其余的字段全部返回;

1
2
3
4
db.inventory.find(
{ status: "A" },
{ "size.uom": 0 }
)

返回,

1
2
3
{ "_id" : ObjectId("5976f17ecb568ef80824a9c4"), "item" : "journal", "status" : "A", "size" : { "h" : 14, "w" : 21 }, "instock" : [ { "warehouse" : "A", "qty" : 5 } ] }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c5"), "item" : "notebook", "status" : "A", "size" : { "h" : 8.5, "w" : 11 }, "instock" : [ { "warehouse" : "C", "qty" : 5 } ] }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c8"), "item" : "postcard", "status" : "A", "size" : { "h" : 10, "w" : 15.25 }, "instock" : [ { "warehouse" : "B", "qty" : 15 }, { "warehouse" : "C", "qty" : 35 } ] }

可以看到,返回的嵌入式文档中,除了 uom 字段值没有返回以外,其它的字段值都返回了;

限定(多个)嵌入文档的返回字段集

这里的多个是指,某个文档字段包含多个内嵌的 documents;

限定返回字段集

1
> db.inventory.find( { status: "A" }, { item: 1, status: 1, "instock.qty": 1 } )

instock 字段可以包含多个内嵌 documents,通过上述的查询,会同时返回所有与之匹配的内嵌文档结果,

1
2
3
{ "_id" : ObjectId("5976f17ecb568ef80824a9c4"), "item" : "journal", "status" : "A", "instock" : [ { "qty" : 5 } ] }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c5"), "item" : "notebook", "status" : "A", "instock" : [ { "qty" : 5 } ] }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c8"), "item" : "postcard", "status" : "A", "instock" : [ { "qty" : 15 }, { "qty" : 35 } ] }

通过限定操作符来限定返回字段集

MongoDB 只提供了 $elemMatch, $slice, and $ 这三个操作符来限定返回字段集;

以 $slice 为例,下面的这个例子只会返回 instock 内嵌文档集合中的最后一个元素;

1
db.inventory.find( { status: "A" }, { name: 1, status: 1, instock: { $slice: -1 } } )

返回,

1
2
3
{ "_id" : ObjectId("5976f17ecb568ef80824a9c4"), "status" : "A", "instock" : [ { "warehouse" : "A", "qty" : 5 } ] }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c5"), "status" : "A", "instock" : [ { "warehouse" : "C", "qty" : 5 } ] }
{ "_id" : ObjectId("5976f17ecb568ef80824a9c8"), "status" : "A", "instock" : [ { "warehouse" : "C", "qty" : 35 } ] }

查询空值或缺失字段

测试数据

1
2
3
4
db.inventory.insertMany([
{ _id: 1, item: null },
{ _id: 2 }
])

匹配 NULL

使用{ item : null }来匹配 documents,将会匹配两种情况,1、包含 item 字段且 item 值为 null;2、不包含 item 字段;

1
> db.inventory.find( { item: null } )

返回,

1
2
{ "_id" : 1, "item" : null }
{ "_id" : 2 }

另外,还可以使用 BSON Type 的方式来进行匹配,使用 { item : { $type: 10 } },其中,{ $type: 10 }表示的是 BSON Type Null;使用 BSON Type Null 来进行查询,只会匹配某个文档,包含此 item 字段,且其值为 null

1
> db.inventory.find( { item : { $type: 10 } } )

返回,可以看到,这次只会返回包含 item 字段的值;

1
{ "_id" : 1, "item" : null }

相关内容参考$type

匹配字段是否存在

1
> db.inventory.find( { item : { $exists: false } } )

通过{ item : { $exists: false } }语句表示查询和返回不存在 item 字段的结果集;上述查询将会返回,

1
{ "_id" : 2 }

相关内容参考$exits