学习MongoDB--(3-3):更新文档(结束篇)

上一篇详细讲解了MongoDB中的修改器的使用,今天把《MongoDB权威指南》第三章关于数据库更新的最后一点写一下。

【修改器速度】

修改器“$inc”能就地修改文档,不会引起文档大小的变化,所以操作会很快。而数组类型的修改器操作后可能引起文档大小的变化,因此会慢一些。修改器"$set"的操作如果引起了文档大小变化,则速度会慢,否则操作很快。MongoDB在为文档分配空间时,会预留一定的补白,用来适应文档大小的变化。但如果文档变化过大,这块补白空间不够,就会引发文档空间的重新分配,这样会拉慢操作速度。

如果在将来的应用中,发现数组修改器如"$push"成为文档更新的瓶颈,则应该考虑把内嵌数组提出来,作为一个独立的集合存在!

【upsert】

upsert是一个特殊的更新,要是没有文档符合更新条件,就会以这个条件和文档为基础创建一个新的文档,如果按照这个条件可以定位到一个文档,则执行更新操作。是集合update和insert操作的集合体。upsert的操作是原子性的。我们来看一下如何在更新时使用upsert:

> db.math.remove();
> db.math.find();
> db.math.update({"count":25},{"$inc":{"count":15}}, true);
> db.math.find();
{ "_id" : ObjectId("501fb4bb04fe6019ab47da1f"), "count" : 40 }
>

我们先调用集合的remove()方法清空集合的所有文档,然后调用update方法,这里我们使用了update方法的第三个参数,我们传递了true,这个true,就表明这个update操作时upsert操作。原先集合中没有文档,我们插入一条让count=25的文档,同时我们将其值增加15,所以执行完后,集合中有一个文档,count=40。

【save函数的使用】

save是集合的一个函数,只有一个参数,就是一个文档。如果该文档包含“_id”键,则save其实会转调upsert,如果文档中不存在"_id"键,save函数默认就是向集合中插入一条文档。

> db.math.remove();
> db.math.find();
> db.math.save({"count":10});
> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 10 }
> db.math.save({"count":10});
> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 10 }
{ "_id" : ObjectId("501fb5cae64fb552a4f66520"), "count" : 10 }
> var x = db.math.findOne({"_id":ObjectId("501fb5b5e64fb552a4f6651f")});
> x.count = 20;
20
> db.math.save(x);
> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 20 }
{ "_id" : ObjectId("501fb5cae64fb552a4f66520"), "count" : 10 }
>

【更新多个文档】

目前我用的MongoDB 2.0.6对于更新操作是,如果有多个文档符合更新操作的查询条件,则只会更新第一个匹配的文档。如果我们要一次更新多个文档,我们要启用update函数的第四个参数,传递true,则是全部更新,否则只更新第一条:

> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 10 }
{ "_id" : ObjectId("501fb5cae64fb552a4f66520"), "count" : 10 }
> var y = db.math.findOne({"_id":ObjectId("501fb5b5e64fb552a4f6651f")});
> db.math.update({"count":10},
... {"$inc":{"count":10}});
> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 20 }
{ "_id" : ObjectId("501fb5cae64fb552a4f66520"), "count" : 10 }
> y.count = 10;
10
> db.math.save(y);
> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 10 }
{ "_id" : ObjectId("501fb5cae64fb552a4f66520"), "count" : 10 }
> db.math.update({"count":10},
... {"$inc":{"count":10}}, false, true);
> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 20 }
{ "_id" : ObjectId("501fb5cae64fb552a4f66520"), "count" : 20 }
>

上面代码可以比较演示,启用update的第四个参数,并且传递true后,更新就会对所有匹配文档进行。在MongoDB Shell中执行完任何命令,都可以再通过运行getLastError命令,来看看,上次执行的命令影响了多少条文档:

> db.math.find();
{ "_id" : ObjectId("501fb5b5e64fb552a4f6651f"), "count" : 40 }
{ "_id" : ObjectId("501fb5cae64fb552a4f66520"), "count" : 40 }
> db.math.update({"count":40},
... {"$inc":{"count":10}}, false, true);
> db.runCommand({"getLastError" : 1});
{
        "updatedExisting" : true,
        "n" : 2,
        "connectionId" : 1,
        "err" : null,
        "ok" : 1
}
>

数据库调用runCommand({getLastError : 1})会返回一个文档,在这个文档中,“键”n的值就是上次操作影响的文档条数。

【返回已更新的文档】

上面通过db.runCommand({getLastError : 1})虽然能返回上次操作影响的文档数量,但并不会返回已更新的文档。可以通过findAndModify命令做到这点。这个命令和普通的更新操作不同,他稍微慢些,因为他要等到数据库响应后再返回,而其他更新操作都是瞬间完成(这个后面会有提及)。我们演示这样一个例子:我们有一个运行线程的集合(processes),其中有线程的状态和优先级信息,我们要找到状态为就绪(“READY”)并且优先级最后的线程,将其更新为运行态,并且返回更新后的文档:

> db.processes.find();
{ "_id" : ObjectId("501fc059e64fb552a4f66521"), "status" : "READY", "priority" :
 1 }
{ "_id" : ObjectId("501fc073e64fb552a4f66522"), "status" : "READY", "priority" :
 2 }
{ "_id" : ObjectId("501fc07ae64fb552a4f66523"), "status" : "READY", "priority" :
 3 }
{ "_id" : ObjectId("501fc07de64fb552a4f66524"), "status" : "READY", "priority" :
 4 }
> db.runCommand({"findAndModify":"processes",
... "query":{"status":"READY"},
... "sort":{"priority":-1},
... "update":{"$set":{"status":"RUNNING"}},
... "new":true});
{
        "lastErrorObject" : {
                "updatedExisting" : true,
                "n" : 1,
                "connectionId" : 1,
                "err" : null,
                "ok" : 1
        },
        "value" : {
                "_id" : ObjectId("501fc07de64fb552a4f66524"),
                "priority" : 4,
                "status" : "RUNNING"
        },
        "ok" : 1
}

执行完findAndModify命令后,会返回一个文档,其中有个键:"value",其值为一个内嵌文档,就是更新后的文档。刚刚运行命令时,发现了一个现象,Shell中文档的键肯定为串,不加双引号也可以,但建议加上吧,这里顺带提一下。我们接着说一下这个findAndModify命令,其中涉及如下的键,含义分别为:

1.  findAndModify : 集合名。

2.  query : 查询文档,用于定位这个命令作用的文档。

3.  sort : 这个命令只会作用在一条文档上,如果匹配了多条,按这个排序,取第一条进行更新。我们从上例看,排序也接受一个文档 {"priority" : -1}, 如果值为负的,则倒叙,如果值为正的,则正序。
4.  update : 修改器文档,对匹配的一条文档执行的操作。

5.  remove : 布尔类型,表明是否是对匹配的文档进行删除操作,remove 和 update必须也只能存在一个。

6.  new : 布尔类型,返回的文档中键“value”指向的内嵌文档是上述匹配文档修改前还是修改后的。如果为true,则是修改后的,否则是修改前的,但匹配的文档肯定也是被修改了。

findAndModify 这个命令有些限制,一次只能更新一条文档。并且只能执行更新或删除操作。执行这个命令,如果没有匹配到任何文档,则不会执行任何更新操作,返回的文档中键“value”的值为null。

【瞬间完成】

我们目前看到的各种数据库操作:插入,删除,更新(除了刚刚的findAndModify命令),都是瞬间完成的,就是客户端发送命令后,即刻返回,不会等待数据库返回任何操作结果或抛出异常。我们称这种操作为:“离弦之箭”。这种方式最大的好处就是快!对于处理一些对数据完整性要求不是很高的大数据量操作如大量日志的记录是特别适合的。但对于一些对安全要求较高的数据库操作如订单系统,我们就不能依赖离弦之箭了,我们在一次数据库操作后,就必须执行一个"getLastError"命令,来看看刚才这次操作的结果,结果中包含是否成功,如果失败,还会包含异常原因。

这种安全操作的后果,就是性能的损失,操作后我们必须等待数据库的响应。在实际应用中,如果数据重要,就采用安全方式,否则果断采用“离弦之箭”吧!

【请求和连接】

MongoDB会为每一个数据库连接创建一个请求队列,客户端发送的请求都会先放到这个队列中(对于“离弦之箭”的请求就会直接返回),MongoDB会从队列中挨个取出请求来执行。对于同一个连接,前面请求的操作结果对后面请求肯定是可见的。

 

关于 MongoDB 文档的更新就到此为止了。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
为什么会这样[user_mongo@nosql01 replicaset]$ cd /opt [user_mongo@nosql01 opt]$ ll total 0 drwxr-xr-x. 3 root root 25 Mar 16 17:08 servers drwxr-xr-x. 2 root root 51 Mar 16 17:10 software [user_mongo@nosql01 opt]$ tar -zxvf /opt/software/mongodb-linux-x86_64-rhel70-4.4.12.tgz -C /opt/servers/mongodb_demo/replicaset/ mongodb-linux-x86_64-rhel70-4.4.12/LICENSE-Community.txt tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/LICENSE-Community.txt: Cannot open: No such file or directory mongodb-linux-x86_64-rhel70-4.4.12/MPL-2 tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/MPL-2: Cannot open: No such file or directory mongodb-linux-x86_64-rhel70-4.4.12/README tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/README: Cannot open: No such file or directory mongodb-linux-x86_64-rhel70-4.4.12/THIRD-PARTY-NOTICES tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/THIRD-PARTY-NOTICES: Cannot open: No such file or directory mongodb-linux-x86_64-rhel70-4.4.12/bin/install_compass tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/bin/install_compass: Cannot open: No such file or directory mongodb-linux-x86_64-rhel70-4.4.12/bin/mongo tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/bin/mongo: Cannot open: No such file or directory mongodb-linux-x86_64-rhel70-4.4.12/bin/mongod tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/bin/mongod: Cannot open: No such file or directory mongodb-linux-x86_64-rhel70-4.4.12/bin/mongos tar: mongodb-linux-x86_64-rhel70-4.4.12: Cannot mkdir: Permission denied tar: mongodb-linux-x86_64-rhel70-4.4.12/bin/mongos: Cannot open: No such file or directory tar: Exiting with failure status due to previous errors [user_mongo@nosql01 opt]$ tar -zcvf /opt/software/mongodb-linux-x86_64-rhel70-4.4.12.tgz -C /opt/servers/mongodb_demo/replicaset/ tar: Cowardly refusing to create an empty archive Try `tar --help' or `tar --usage' for more information.
最新发布
06-01
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值