$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
upsert是update()的第三个参数。表示没有则创建,有则正常更新。
> 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=43 43 > 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 } >
返回被更新的文档
通过findAndModify命令得到被更新的文档。返回的是修改之前的文档。
对于操作队列以及执行其他需要进行原子性取值和赋值的操作非常方便。
ps=db.runCommand({"findAndModify":"processes", "query":{"status":"ready"}, "sort":{"pirority":-1}, "update":{"$set":{"status":"Running"}}}) { "lastErrorObject" : { "updatedExisting" : true, "n" : 1 }, "value" : { "_id" : ObjectId("5857c939d7a32a888bd79c47"), "pirority" : 2, "status" : "ready" }, "ok" : 1 }
> db.processes.findOne("_id":ps.value._id) 2016-12-19T19:57:04.904+0800 E QUERY [thread1] SyntaxError: missing ) after argument list @(shell):1:26 > db.processes.findOne({"_id":ps.value._id}) { "_id" : ObjectId("5857c939d7a32a888bd79c47"), "pirority" : 2, "status" : "Running" } >
findAndModify可以使用update键也可以使用remove键。Remove键表示将匹配的文档从集合里面删除。
> ps=db.runCommand({"findAndModify":"processes", "query":{"status":"ready"}, "sort":{"priority":-1}, "remove":true}).value { "_id" : ObjectId("5857c9b1d7a32a888bd79c48"), "pirority" : 2, "status" : "ready" } > db.processes.findOne({"_id":ps._id}) null