四时宝库

程序员的知识宝库

NoSQL之MongoDB——数据查询操作中需要注意的几个问题

下面的方法也能从一个集合中读取文档:

· db.collection.findOne

· 在聚集管道中,$match 管道支持MongoDB 查询。

1读取隔离

3.2版本新增

为了读取副本集和副本集分片,读关注(read concern)允许客户端选则读隔离级别。

2投影字段以返回查询结果

默认返回文档中所有字段。为了限制返回结果的数据量,可以在查询操作中使用投影器文档。

投影器文档

投影器文档限制了查询操作返回所有匹配到的文档的字段。投影器文档指定了返回结果中包含或排除哪些字段,其格式为:{ field1: <value>, field2: <value> ... }

<value>可以是下面的任何值:

  • 1或true表示字段被包含在返回的结果文档中。
  • 0或false表示字段不包含在返回的结果文档中。
  • 当<value>为表达式时,要使用投影器操作符。

注:

对于_id字段,为使其包含在返回结果中,不用明确指定“_id:1”。db.collection.find() 方法返回结果中总是包含_id字段,除非指定“ _id: 0 ”。

投影器不能同时使用包含规范和排除规范,除对_id做排除以外。在明确指定包含规范的投影器中,仅可对_id字段指定排除规范。

示例集合

在mongo shell中,使用db.collection.find()来检索本页的集合,如果一个游标没有赋给一个var变量,那么游标自动迭代20次以打印查询结果中的前20个文档。

在mongo shell中执行下面的语句来填充users 集合。

注:

如果在集合users 中,已有文档的_id字段值和待插入文档的_id字段值相同,那么要先将集合users删除。

db.users.insertMany(
 [
 {
 _id: 1,
 name: "sue",
 age: 19,
 type: 1,
 status: "P",
 favorites: { artist: "Picasso", food: "pizza" },
 finished: [ 17, 3 ],
 badges: [ "blue", "black" ],
 points: [
 { points: 85, bonus: 20 },
 { points: 85, bonus: 10 }
 ]
 },
 {
 _id: 2,
 name: "bob",
 age: 42,
 type: 1,
 status: "A",
 favorites: { artist: "Miro", food: "meringue" },
 finished: [ 11, 25 ],
 badges: [ "green" ],
 points: [
 { points: 85, bonus: 20 },
 { points: 64, bonus: 12 }
 ]
 },
 {
 _id: 3,
 name: "ahn",
 age: 22,
 type: 2,
 status: "A",
 favorites: { artist: "Cassatt", food: "cake" },
 finished: [ 6 ],
 badges: [ "blue", "red" ],
 points: [
 { points: 81, bonus: 8 },
 { points: 55, bonus: 20 }
 ]
 },
 {
 _id: 4,
 name: "xi",
 age: 34,
 type: 2,
 status: "D",
 favorites: { artist: "Chagall", food: "chocolate" },
 finished: [ 5, 11 ],
 badges: [ "red", "black" ],
 points: [
 { points: 53, bonus: 15 },
 { points: 51, bonus: 15 }
 ]
 },
 {
 _id: 5,
 name: "xyz",
 age: 23,
 type: 2,
 status: "D",
 favorites: { artist: "Noguchi", food: "nougat" },
 finished: [ 14, 6 ],
 badges: [ "orange" ],
 points: [
 { points: 71, bonus: 20 }
 ]
 },
 {
 _id: 6,
 name: "abc",
 age: 43,
 type: 1,
 status: "A",
 favorites: { food: "pizza", artist: "Picasso" },
 finished: [ 18, 12 ],
 badges: [ "black", "blue" ],
 points: [
 { points: 78, bonus: 8 },
 { points: 57, bonus: 7 }
 ]
 }
 ]
)

返回匹配到的全部字段

使用db.collection.find()方法检索而不使用投影器,将返回文档的全部字段。

例如,从users 集合中检索字段status 的值为“A”的文档。

db.users.find( { status: "A" } )

查询结果:

{
 "_id" : 2,
 "name" : "bob",
 "age" : 42,
 "type" : 1,
 "status" : "A",
 "favorites" : { "artist" : "Miro", "food" : "meringue" },
 "finished" : [ 11, 25 ],
 "badges" : [ "green" ],
 "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 64, "bonus" : 12 } ]}
{
 "_id" : 3,
 "name" : "ahn",
 "age" : 22,
 "type" : 2,
 "status" : "A",
 "favorites" : { "artist" : "Cassatt", "food" : "cake" },
 "finished" : [ 6 ],
 "badges" : [ "blue", "red" ],
 "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ]}
{
 "_id" : 6,
 "name" : "abc",
 "age" : 43,
 "type" : 1,
 "status" : "A",
 "favorites" : { "food" : "pizza", "artist" : "Picasso" },
 "finished" : [ 18, 12 ],
 "badges" : [ "black", "blue" ],
 "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ]}

只返回指定字段和_id字段

例如,结果集中只包含name, status和_id字段

db.users.find( { status: "A" }, { name: 1, status: 1 } )

查询结果为:

{ "_id" : 2, "name" : "bob", "status" : "A" }
{ "_id" : 3, "name" : "ahn", "status" : "A" }
{ "_id" : 6, "name" : "abc", "status" : "A" }

只返回指定字段

db.users.find( { status: "A" }, { name: 1, status: 1, _id: 0 } )

查询结果为:

{ "name" : "bob", "status" : "A" }
{ "name" : "ahn", "status" : "A" }
{ "name" : "abc", "status" : "A" }

排除某些字段

db.users.find( { status: "A" }, { favorites: 0, points: 0 } )

查询结果为:

{
 "_id" : 2,
 "name" : "bob",
 "age" : 42,
 "type" : 1,
 "status" : "A",
 "finished" : [ 11, 25 ],
 "badges" : [ "green" ]}{
 "_id" : 3,
 "name" : "ahn",
 "age" : 22,
 "type" : 2,
 "status" : "A",
 "finished" : [ 6 ],
 "badges" : [ "blue", "red" ]}
{
 "_id" : 6,
 "name" : "abc",
 "age" : 43,
 "type" : 1,
 "status" : "A",
 "finished" : [ 18, 12 ],
 "badges" : [ "black", "blue" ]}

返回嵌入式文档中指定字段

使用圆点操作符指定嵌入式文档中的字段。

例如,设定投影器,返回_id 字段, name字段, status字段, 和嵌入式文档favorites 中的food 字段,food字段被包含在匹配到的文档的字段favorites 中。

db.users.find(
 { status: "A" },
 { name: 1, status: 1, "favorites.food": 1 }
)

查询结果为:

{ "_id" : 2, "name" : "bob", "status" : "A", "favorites" : { "food" : "meringue" } }
{ "_id" : 3, "name" : "ahn", "status" : "A", "favorites" : { "food" : "cake" } }
{ "_id" : 6, "name" : "abc", "status" : "A", "favorites" : { "food" : "pizza" } }

排除嵌入式文档中的指定字段

使用圆点操作符将嵌入式文档中的字段值设置为0。

例如,排除嵌入式文档favorites中的food字段,其他字段都返回

db.users.find(
 { status: "A" },
 { "favorites.food": 0 }
)

查询结果为:

{
 "_id" : 2,
 "name" : "bob",
 "age" : 42,
 "type" : 1,
 "status" : "A",
 "favorites" : { "artist" : "Miro" },
 "finished" : [ 11, 25 ],
 "badges" : [ "green" ],
 "points" : [ { "points" : 85, "bonus" : 20 }, { "points" : 64, "bonus" : 12 } ]
}
{
 "_id" : 3,
 "name" : "ahn",
 "age" : 22,
 "type" : 2,
 "status" : "A",
 "favorites" : { "artist" : "Cassatt" },
 "finished" : [ 6 ],
 "badges" : [ "blue", "red" ],
 "points" : [ { "points" : 81, "bonus" : 8 }, { "points" : 55, "bonus" : 20 } ]
}
{
 "_id" : 6,
 "name" : "abc",
 "age" : 43,
 "type" : 1,
 "status" : "A",
 "favorites" : { "artist" : "Picasso" },
 "finished" : [ 18, 12 ],
 "badges" : [ "black", "blue" ],
 "points" : [ { "points" : 78, "bonus" : 8 }, { "points" : 57, "bonus" : 7 } ]
}

投射器作用于数组中的嵌入式文档

使用圆点操作符投射数组中嵌入式文档的指定字段。

例如,指定投射器,返回name字段 、status 字段和bonus 字段;_id 字段默认返回。

db.users.find( { status: "A" }, { name: 1, status: 1, "points.bonus": 1 } )

查询结果:

{ "_id" : 2, "name" : "bob", "status" : "A", "points" : [ { "bonus" : 20 }, { "bonus" : 12 } ] }
{ "_id" : 3, "name" : "ahn", "status" : "A", "points" : [ { "bonus" : 8 }, { "bonus" : 20 } ] }
{ "_id" : 6, "name" : "abc", "status" : "A", "points" : [ { "bonus" : 8 }, { "bonus" : 7 } ] }

在返回的数组中投射指定的数组元素

对于包含数组的字段,MongoDB提供了下面的投影器操作符:$elemMatch, $slice, 和$.

例如,使用 $slice投影操作符来返回scores 数组中最后一个元素。

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

查询结果为:

{ "_id" : 2, "name" : "bob", "status" : "A", "points" : [ { "points" : 64, "bonus" : 12 } ] }
{ "_id" : 3, "name" : "ahn", "status" : "A", "points" : [ { "points" : 55, "bonus" : 20 } ] }
{ "_id" : 6, "name" : "abc", "status" : "A", "points" : [ { "points" : 57, "bonus" : 7 } ] }

$elemMatch, $slice, 和$是投射指定元素(多个)且使其包含在返回结果中的仅有的方式。例如,不能使用数组索引投射任何元素,投影器{ "ratings.0": 1 },不会投射数组中的第一个元素。

3查询null或缺失的字段

在MongoDB 中,不同的操作符对待null值是不同的。

本页中的例子在mongo shell中执行db.collection.find()方法。为了填充集合users,在mongo shell中执行:

db.users.insert(
 [
 { "_id" : 900, "name" : null },
 { "_id" : 901 }
 ]
)

相等过滤器

查询匹配文档{ name : null }检索出这样的文档:文档包含值为null的name字段,或者文档不包含name字段。

给出如下查询:

db.users.find( { name: null } )

查询返回结果为:

{ "_id" : 900, "name" : null }
{ "_id" : 901 }

如果索引是稀疏的,那么只会匹配null值而不是缺失的字段。

2.6版本中的变更:如果使用稀疏索引导致不完整的结果,MongoDB 将不会使用索引,除非使用hint()指定索引。

类型检测

使用{ name : { $type: 10 } }匹配出name 字段为null的文档。例如name 字段值为Null(BSON类型)。

db.users.find( { name : { $type: 10 } } )

查询结果为:

{ "_id" : 900, "name" : null }

存在检测

使用{ name : { $exists: false } }匹配出不包含某一字段的文档:

db.users.find( { name : { $exists: false } } )

查询结果为:

{ "_id" : 901 }

3.1在mongo shell中迭代游标

db.collection.find() 方法返回游标,为了使用文档,你需要迭代游标。然而,如果返回的游标没有赋给var类型的变量,那么游标会自动迭代20次以打印结果集中前20个文档。

下面的例子描述了手动迭代游标来使用文档或迭代器索引的方式。

手动迭代游标

在mongo shell,当你将由 find()方法返回的游标赋给var类型变量时,游标不会迭代。

你可以在mongo shell中调用游标变量,迭代20次并打印匹配文档,例子如下:

var myCursor = db.users.find( { type: 2 } );
myCursor

你可以使用游标方法next() 来使用文档,例子如下:

var myCursor = db.users.find( { type: 2 } );
while (myCursor.hasNext()) {
 print(tojson(myCursor.next()));
}

作为一种替代的打印方式,考虑使用printjson()这一帮助方法来替代print(tojson()):

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

你可以使用游标方法forEach()来迭代游标并使用文档,例子如下:

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

迭代器索引

在mongo shell,你可以使用toArray()方法来迭代游标并返回数组中的文档,例子如下:

var myCursor = db.inventory.find( { type: 2 } );
var documentArray = myCursor.toArray();
var myDocument = documentArray[3];

toArray() 方法将游标返回的所有文档都加载到内存中,它会耗尽游标。

另外,一些驱动提供了通过使用游标索引来获得文档的方法(例如,cursor[index])。先调用toArray()方法,然后使用结果集索引,这是一种便捷的方式。

考虑如下的例子:

var myCursor = db.users.find( { type: 2 } );
var myDocument = myCursor[1];
同myCursor[1] 等价的方式为:
myCursor.toArray() [1];

游标行为

关闭无效的游标

默认地,游标的不活跃时期超过十分钟时或者客户端耗尽游标,服务器会自动关闭游标。为在mongo shell中重设置这一行为,可以使用cursor.noCursorTimeout()方法:

var myCursor = db.users.find().noCursorTimeout();

设置了noCursorTimeout 选项以后,必须使用cursor.close() 方法手动关闭游标,或者通过耗尽游标结果的方式关闭游标。

游标隔离

当使用游标获得文档的同时,其他的查询操作可能正在执行。对于MMAPv1 存储引擎,对一个文档的交替写操作,如果文档已经改变了,可能导致游标返回同一文档两次。为了控制这种情况,请参考snapshot mode。

游标批处理

MongoDB 成批地返回查询结果。批大小不会超过BSON文档的最大值。对于大多数查询来说,第一批返回101个文档,或者超过1MB的文档,随后的批大小为4MB,为了重置批大小,可以使用batchSize() 和limit()方法。

没有索引的情况下,如果查询包含排序操作,服务器要将所有的文档加载到内存中以执行排序。

当你使用游标迭代并且达到了已返回那批的末尾时,如果还有更多的数据,cursor.next() 方法将会执行获取更多操作来检索下一批。当你迭代游标时,为了查看有多少文档在某一批中,你可以使用objsLeftInBatch()方法,例如:

var myCursor = db.inventory.find();
var myFirstDocument = myCursor.hasNext() ? myCursor.next() : null;
myCursor.objsLeftInBatch();

游标信息

db.serverStatus() 方法返回的文档中包含metrics 字段,metrics 字段包含具有如下信息的metrics.cursor 字段:

· 从服务器最近一次重启到当期时间的超时游标数量。

· 设置DBQuery.Option.noTimeout选项的已打开游标数量,设置DBQuery.Option.noTimeout可防止一段不活跃期后游标超时。

· 被“定住”的已打开游标。

· 已打开游标的总数。

如下的例子中,调用db.serverStatus()方法,使用返回结果中的字段metrics 和metrics 中的字段cursor:

db.serverStatus().metrics.cursor

执行结果为:

{
 "timedOut" : <number>
 "open" : {
 "noTimeout" : <number>,
 "pinned" : <number>,
 "total" : <number>
 }
}

本文内容主要译自官方文档

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接