首页 > 代码库 > MongoDB权威指南第二版学习笔记(四)—增删改文档下

MongoDB权威指南第二版学习笔记(四)—增删改文档下

$slice

如果希望数组的最大长度是固定的,那么可以将 $slice 和 $push 组合在一起使用,就可以保证数组不会超出设定好的最大长度。$slice 的值必须是负整数。

假设$slice的值为10,如果$push 后的数组的元素个数小于10,那么所有元素都会保留。反之,只有最后那10个元素会保留。因此,$slice 可以用来在文档中创建一个队列。

db.class.insert({"班级":"1班"})WriteResult({ "nInserted" : 1 })> db.class.update(... {"班级":"1班"},... {"$push":{"students":{... "$each":["zs","ls","ww"],... "$slice":-5}}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.class.findOne(){        "_id" : ObjectId("5854b5a0e7d717fcb974637b"),        "班级" : "1班",        "students" : [                "zs",                "ls",                "ww"        ]}> db.class.update(...  {"班级":"1班"},...  {"$push":{"students":{... "$each":["yyb","rhr","www","qqq","eee","rrr"],... "$slice":-5}}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.class.findOne(){        "_id" : ObjectId("5854b5a0e7d717fcb974637b"),        "班级" : "1班",        "students" : [                "rhr",                "www",                "qqq",                "eee",                "rrr"        ]}> 

也可以在清理元素之前使用$sort,只要向数组中添加子对象就需清理,先排序后保留指定的个数。

> db.class.update({},{ "班级" : "一班"})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })>  db.class.update(...  {"班级":"一班"},... {"$push":{"students":...  {"$each":[{"name":"aaa","age":1},{"name":"bbb","age":2},{"name":"ccc","age":3}],...  "$slice":-2,...  "$sort":{"age":-1}}}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.class.findOne(){        "_id" : ObjectId("5854b5a0e7d717fcb974637b"),        "班级" : "一班",        "students" : [                {                        "name" : "bbb",                        "age" : 2                },                {                        "name" : "aaa",                        "age" : 1                }        ]}> 

$ne与$addToSet

如果想将数组作为数据集使用,保证数组内的元素不会重复。可以在查询文档中用$ne或者$addToSet来实现。有些情况$ne根本行不通,有些时候更适合用$addToSet

> db.papers.insert({"authors cited":["yyb"]})WriteResult({ "nInserted" : 1 })> db.papers.update({"authors cited":{"$ne":"Richie"}}, {"$push":{"authors cited":"Richie"}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.papers.findOne(){        "_id" : ObjectId("5854c900e7d717fcb974637e"),        "authors cited" : [                "yyb",                "Richie"        ]}> db.papers.update({"authors cited":{"$ne":"Richie"}}, {"$push":{"authors cited":"Richie"}})WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })> 
 db.user.findOne(){        "_id" : ObjectId("5854cb40e7d717fcb974637f"),        "username" : "joe",        "emails" : [                "joe@example.com",                "joe@gmail.com",                "joe@yahoo.com"        ]}> db.user.update(... ... {"username":"joe"},... ... {"$addToSet":{"emails":"joe@gmail.com"}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })>  db.user.update(... ... ... {"username":"joe"},... ... ... {"$addToSet":{"emails":"joe@hotmail.com"}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.user.findOne(){        "_id" : ObjectId("5854cb40e7d717fcb974637f"),        "username" : "joe",        "emails" : [                "joe@example.com",                "joe@gmail.com",                "joe@yahoo.com",                "joe@hotmail.com"        ]}> 

$addToSet$each组合起来,可以添加多个不同的值。而用$ne$push组合就不能实现。

$addToSet与$push的区别:前者添加到数组中时会去重,后者不会。

>db.user.insert({ "username" : "joe"})>  db.user.update(... {"username" : "joe"},... ... {"$addToSet":...  {"emails" :{"$each": [... "joe@example.com",...  "joe@gmail.com",... "joe@yahoo.com",... "joe@hotmail.com",...  "joe@hotmail.com"]}}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.user.findOne(){        "_id" : ObjectId("5854ce5ce7d717fcb9746380"),        "username" : "joe",        "emails" : [                "joe@example.com",                "joe@gmail.com",                "joe@yahoo.com",                "joe@hotmail.com"        ]}> 

从数组中删除元素

$pop

可以从数组任何一端删除元素。

{“$pop”:{“key”:1}}从数组末尾删除一个元素

{“$pop”:{“key”:-1}}从数组头部删除一个元素

> db.class.findOne(){        "_id" : ObjectId("5854b5a0e7d717fcb974637b"),        "班级" : "一班",        "students" : [                {                        "name" : "bbb",                        "age" : 2                },                {                        "name" : "aaa",                        "age" : 1                }        ]}> db.class.update(... {"班级" : "一班"},... {"$pop":{"students":1}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.class.findOne(){        "_id" : ObjectId("5854b5a0e7d717fcb974637b"),        "班级" : "一班",        "students" : [                {                        "name" : "bbb",                        "age" : 2                }        ]}> 

$pull

有时需要基于特定条件来删除,而不仅仅是依据元素位置,这时可以使用$pull。$pull会将所有匹配的文档删除,而不是只删除一个。

> db.list.insert(... {"todo":["dishes","laundry","dry cleaning"]})WriteResult({ "nInserted" : 1 })> db.list.update({},{"$pull":{"todo":"laundry"}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.list.findOne(){        "_id" : ObjectId("585690afc5b0525a48a441b4"),        "todo" : [                "dishes",                "dry cleaning"        ]}> 

数组操作符只能用于包含数组值的键。例如:不能将一个整数插入数组,也不能将一个字符串从数组中弹出。要修改标量值,使用$set$inc。

基于位置的数组修改器

有两种方法操作数组中的值:通过位置或者定位操作符($)

位置

通过数组位置来操作。数组都是以0开头的,可以将下标直接作为键来选择元素。

> db.blog.insert(... {...     "content": "...",...     "comments": [...         {...             "comment": "good post",...             "author": "john",...             "votes": 0...         },...         {...             "comment": "i thought it was too short",...             "author": "claire",...             "votes": 3...         },...         {...             "comment": "free watches",...             "author": "alice",...             "votes": -1...         }...     ]... })WriteResult({ "nInserted" : 1 })> db.blog.update({"content":"..."},{"$inc":{"comments.0.votes":1}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.blog.findOne(){        "_id" : ObjectId("585694e4c5b0525a48a441b5"),        "content" : "...",        "comments" : [                {                        "comment" : "good post",                        "author" : "john",                        "votes" : 1                },                {                        "comment" : "i thought it was too short",                        "author" : "claire",                        "votes" : 3                },                {                        "comment" : "free watches",                        "author" : "alice",                        "votes" : -1                }        ]}>

定位操作符$

如果无法知道要修改的数组的下标,可以使用定位操作符$,用来定位查询文档已经匹配的元素,并进行更新。

> db.blog.update(... {"comments.author":"john"},... {"$set":{"comments.$.author":"jim"}})WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.blog.findOne(){        "_id" : ObjectId("585694e4c5b0525a48a441b5"),        "content" : "...",        "comments" : [                {                        "comment" : "good post",                        "author" : "jim",                        "votes" : 1                },                {                        "comment" : "i thought it was too short",                        "author" : "claire",                        "votes" : 3                },                {                        "comment" : "free watches",                        "author" : "alice",                        "votes" : -1                }        ]}> 

upsert

upsertupdate()的第三个参数。表示没有则创建,有则正常更新。

> db.analytics.update({"url":"/blog"},{"$inc":{"pageviews":1}},true)WriteResult({        "nMatched" : 0,        "nUpserted" : 1,        "nModified" : 0,        "_id" : ObjectId("58569d3cb6687ca8dfad4e01")})> db.analytics.findOne(){        "_id" : ObjectId("58569d3cb6687ca8dfad4e01"),        "url" : "/blog",        "pageviews" : 1}> db.analytics.update({"url":"/blog"},{"$inc":{"pageviews":1}},true)WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })>  db.analytics.findOne(){        "_id" : ObjectId("58569d3cb6687ca8dfad4e01"),        "url" : "/blog",        "pageviews" : 2}> 

$setOnInsert

在创建文档的同时创建字段并为它赋值,但是在之后的所有更新操作中在,这个字段的值都不在改变。

$setOnInsert只会在文档插入时设置字段的值。

在预置或者初始化计数器时,或者对于不使用ObjectIds的集合来说,“$setOnInsert”是非常有用的。

> db.user.update({},{"$setOnInsert":{"createAt":new Date()}},true)WriteResult({        "nMatched" : 0,        "nUpserted" : 1,        "nModified" : 0,        "_id" : ObjectId("58569fe1b6687ca8dfad4e02")})> db.user.findOne(){        "_id" : ObjectId("58569fe1b6687ca8dfad4e02"),        "createAt" : ISODate("2016-12-18T14:40:33.273Z")}> db.user.update({},{"$setOnInsert":{"createAt":new Date()}},true)WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })> db.user.findOne(){        "_id" : ObjectId("58569fe1b6687ca8dfad4e02"),        "createAt" : ISODate("2016-12-18T14:40:33.273Z")}> 

save

一个shell函数,不存在创建,反之则更新文档。

它只有一个参数:文档。要是这个文档含有“_id”键,save会调用upsert。否则会调用insert。在shell中快速对文档进行修改。

> db.user.save({"x":10,"y":20})WriteResult({ "nInserted" : 1 })> var x=db.user.findOne()> x.num=4343> db.user.save(x)WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })> db.user.findOne(){        "_id" : ObjectId("5856a230c5b0525a48a441be"),        "x" : 10,        "y" : 20,        "num" : 43}

更新多个文档

默认情况下,只会更新匹配的第一个文档。要更新多个文档,需要将update的第4个参数设置为true

想要知道多文档更新到底更新了多少文档,可以运行getLastError命令。键n的值就是被更新文档的数量。

> db.coll.find(){ "_id" : ObjectId("5856994bc5b0525a48a441b9"), "x" : "a" }{ "_id" : ObjectId("58569966c5b0525a48a441ba"), "x" : "bbb" }{ "_id" : ObjectId("5856996fc5b0525a48a441bb"), "x" : "c" }> db.coll.update({},{"$set":{"x":10}},false,true )WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 3 })> db.runCommand({getLastError:1}){        "connectionId" : 1,        "updatedExisting" : true,        "n" : 3,        "syncMillis" : 0,        "writtenTo" : null,        "err" : null,        "ok" : 1}> 

MongoDB权威指南第二版学习笔记(四)—增删改文档下