前面我们已经介绍了mongodb的基本操作: mongodb基本操作
下面是介绍一些高级的增删改查操作.
1.更新文档
文档替换
最简单的方法就是文档完全替换,如下就是一个完全替换的例子,先把需要更新的文档找出来并且赋值,然后修改所赋的值,最后再进行update:
> a1=db.test.findOne()
{ "_id" : ObjectId("5738785d132e1e47e535a177"), "x" : 3.14, "y" : 5.5 }
> a1
{ "_id" : ObjectId("5738785d132e1e47e535a177"), "x" : 3.14, "y" : 5.5 }
> delete a1.x
true
> a1
{ "_id" : ObjectId("5738785d132e1e47e535a177"), "y" : 5.5 }
> a1.content={name:'job',age:11}
{ "name" : "job", "age" : 11 }
> a1
{
"_id" : ObjectId("5738785d132e1e47e535a177"),
"y" : 5.5,
"content" : {
"name" : "job",
"age" : 11
}
}
> db.test.update({"_id" : ObjectId("5738785d132e1e47e535a177")},a1)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
使用修改器
通常文档只会有一部分需要更新,可以使用原子性的更新修改器对指定文档中的某些键进行更新
"$set"修改器
"$set"修改器用来指定一个字段的值,如果这个字段不存在则创建它,例如先给test表添加一个phone属性:
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]" }
>
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$set":{phone:666}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]", "phone" : 666 }
然后用修改器再将phone该为888
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$set":{phone:888}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]", "phone" : 888 }
使用"$set"修改器还可以修改字段的属性,例如可以把phone改成一个数组,还可以用
"$unset"将键完全删除:
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$unset":{phone:888}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]" }
使用"$set"修改内嵌文档:
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]" }
{ "_id" : ObjectId("57395fa85f74882a9bfa2d9f"), "id" : { "phone" : 888, "address" : "abc" } }
>
> db.test.update({"_id" : ObjectId("57395fa85f74882a9bfa2d9f")},{"$set":{"id.phone":666}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]" }
{ "_id" : ObjectId("57395fa85f74882a9bfa2d9f"), "id" : { "phone" : 666, "address" : "abc" } }
"$inc"增加和减少
"$inc"修改器用来增加和减少已有键的值,只能用于整形,长整形或者双精度浮点型的值,用在其它类型的值上面会报错.对于不存在的键,像"$set"那样也会自动创建相应的键,并且值为给定的值
例如我们用"$inc"修改器给age值+1,原来是30,现在变成了31
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]" }
{ "_id" : ObjectId("57395fa85f74882a9bfa2d9f"), "id" : { "phone" : 666, "address" : "abc" } }
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$inc":{age:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 31, "email" : "[email protected]" }
{ "_id" : ObjectId("57395fa85f74882a9bfa2d9f"), "id" : { "phone" : 666, "address" : "abc" } }
如果要减少,则将age:1里的1改成-1
"$push"数组修改器-添加元素
如果数组已经存在,那么"$push"会向已有的数组末尾添加一个元素,如果没有就创建一个新的数组 ,下面这个例子会创建一个表示comments的数组:
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]", "phone" : 888 }
{ "_id" : ObjectId("57395fa85f74882a9bfa2d9f"), "id" : { "phone" : 666, "address" : "abc" } }
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$push":{comments:
... {name:"jack",content:"good"}
... }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]", "phone" : 888, "comments" : [ { "name" : "jack", "content" : "good" } ] }
{ "_id" : ObjectId("57395fa85f74882a9bfa2d9f"), "id" : { "phone" : 666, "address" : "abc" } }
继续给comments添加值:
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$push":{comments: {name:"tom",content:"very good"} }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find()
{ "_id" : ObjectId("573957c55f74882a9bfa2d9e"), "name" : "brent", "age" : 30, "email" : "[email protected]", "phone" : 888, "comments" : [ { "name" : "jack", "content" : "good" }, { "name" : "tom", "content" : "very good" } ] }
{ "_id" : ObjectId("57395fa85f74882a9bfa2d9f"), "id" : { "phone" : 666, "address" : "abc" } }
还可以将"$push"和"$each"一起使用,一次添加多个值
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$push":{comments: {"$each":["a","b","c"]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
>
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "jack",
"content" : "good"
},
{
"name" : "tom",
"content" : "very good"
},
"a",
"b",
"c"
]
}
还可以将"$slice"和"$push"组合在一起使用,这样就保证数组不会超出设定好的最大长度,这实际上得到了一个最多包含N个元素的数组:
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$push":{comments: {"$each":["a","b","c"],"$slice":-10}}})
这个例子会限制数组只包含最后加入的10个元素,"$slice"的值必须是负整数.
还可以使用"$sort"对数组中的对象进行排序,注意"$slice"必须和"$each"配合使用.
"$ne"和"$addToSet"将数组作为数据集使用
当将数组作为数据集使用进行的更新的时候,要保证数组内的元素不能重复,当update的数据重复即不做,如果不重复则进行更新,可以使用"$ne",也可以使用"$addToSet"
使用"$ne"的方法:
> db.test.update({"comments":{"$ne":"jack"}},{"$push":{"comments":"jack"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "jack",
"content" : "good"
},
{
"name" : "tom",
"content" : "very good"
},
"a",
"b",
"c",
"a",
"b",
"c",
"jack"
]
感觉ne就是not equal的意思
其实按照我的理解上面那句话就是当comment这个数组内的元素ne(不等于)jack的时候,使用push在comment里将jack这个元素添加到commnts数组中.
使用"$addToSet"的方法:
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$addToSet":{"comments":"andy"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "jack",
"content" : "good"
},
{
"name" : "tom",
"content" : "very good"
},
"a",
"b",
"c",
"a",
"b",
"c",
"jack",
"andy"
]
}
个人觉得"$addToset"的方法更好用一点,而且格式和上面介绍的集中类似.而且
"$addToset"可以和"$each"组合起来进行批量的添加
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$addToSet":{"comments":{"$each":["andy","lily","lucy"]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "jack",
"content" : "good"
},
{
"name" : "tom",
"content" : "very good"
},
"a",
"b",
"c",
"a",
"b",
"c",
"jack",
"andy",
"lily",
"lucy"
]
}
"$pop"和"$pull"删除数组元素
可以使用"$pop"方法将数组进行出列操作{"$pop:{"key":1}}从
数组key的末尾元素开始出列,key为-1从头部开始出列,下面这个例子将lucy删除掉了:
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$pop":{"comments":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "jack",
"content" : "good"
},
{
"name" : "tom",
"content" : "very good"
},
"a",
"b",
"c",
"a",
"b",
"c",
"jack",
"andy",
"lily"
]
}
如果不是从开头或者末尾删除,可以使用"$pull",
它会删除所有匹配的文档,如下删除了所有的a:
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$pull":{"comments":"a"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "jack",
"content" : "good"
},
{
"name" : "tom",
"content" : "very good"
},
"b",
"c",
"b",
"c",
"jack",
"andy",
"lily"
]
}
$基于位置的数组修改
如果数组有多个值,而我们只想对其中一部分进行操作,有两种方法操作数组中的值:通过位置或者定位操作符$
数组下标都是以0开始的,可以将下标直接作为键的选择元素,下面这个例子给comments这个数组的第一个元素添加一个num:1的元素
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "tom",
"content" : "very good"
},
"jack",
"andy",
"lily"
]
}
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$set":{"comments.0.num":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
>
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "tom",
"content" : "very good",
"num" : 1
},
"jack",
"andy",
"lily"
]
}
再对这个num加上1:
> db.test.update({"_id" : ObjectId("573957c55f74882a9bfa2d9e")},{"$inc":{"comments.0.num":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "tom",
"content" : "very good",
"num" : 2
},
"jack",
"andy",
"lily"
]
}
这种方法的弊端是需要先查询出数据,然后根据数据你才知道需要修改的数组的下标.为了客服这个困难mongodb提供了定位操作符$,用来定位匹配的元素,
定位符只匹配第一个的元素,例如我们把上面的tom改成jack:
> db.test.update({"comments.name":"tom"},{"$set":{"comments.$.name":"jack"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.findOne()
{
"_id" : ObjectId("573957c55f74882a9bfa2d9e"),
"name" : "brent",
"age" : 30,
"phone" : 888,
"comments" : [
{
"name" : "jack",
"content" : "very good",
"num" : 2
},
"jack",
"andy",
"lily"
]
}
upsert
upsert是一种特殊的更新,要是没有找到符合条件的更新那么就创建一个新的文档,如果找到就正常更新.
update的第三个参数即控制update是普通的update还是upsert,默认为false,即普通的update,如果设置成true,则为upsert
下面的这个例子查找name为bob的,如果有就给age加1,如果没有则创建name为bob,并且设置age为1,使用upsert实现:
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 28 }
> db.test.update({"name":"bob"},{"$inc":{"age":1}},true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("573c86d3017c5eb7d08aed6d")
})
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 28 }
{ "_id" : ObjectId("573c86d3017c5eb7d08aed6d"), "name" : "bob", "age" : 1 }
>
有的时候需要创建文档的同时创建字段并为他赋值,但是在之后的更新中这个字段的值不再改变,这就是
"$setOnInsert"的作用.
"$setOnInsert"只会在文档插入的时候设置字段的值.
下面这个例子第一次使用upsert,因为没有name为tom的所以插入age为10,第二次再使用upsert想设置tom的age为20,因为使用
"$setOnInsert"而且tom已经存在那么就不会再更新了
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 28 }
{ "_id" : ObjectId("573c86d3017c5eb7d08aed6d"), "name" : "bob", "age" : 1 }
> db.test.update({"name":"tom"},{"$setOnInsert":{"age":10}},true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("573c88fe017c5eb7d08aed6e")
})
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 28 }
{ "_id" : ObjectId("573c86d3017c5eb7d08aed6d"), "name" : "bob", "age" : 1 }
{ "_id" : ObjectId("573c88fe017c5eb7d08aed6e"), "name" : "tom", "age" : 10 }
> db.test.update({"name":"tom"},{"$setOnInsert":{"age":20}},true)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 28 }
{ "_id" : ObjectId("573c86d3017c5eb7d08aed6d"), "name" : "bob", "age" : 1 }
{ "_id" : ObjectId("573c88fe017c5eb7d08aed6e"), "name" : "tom", "age" : 10 }
save shell帮助程序
save是一个shell函数,如果文档不存在,它会自动创建文档,如果存在它就更新这个文档,他只有一个参数:文档.
如果这个文档中含有_id则会调用upsert,如果没有则调用insert
> var a= db.test.findOne()
> a
{
"_id" : ObjectId("573c858c323f7f2e2ccb0e17"),
"name" : "brent",
"age" : 28
}
> a.age=40
40
> a
{
"_id" : ObjectId("573c858c323f7f2e2ccb0e17"),
"name" : "brent",
"age" : 40
}
> db.test.save(a)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 40 }
{ "_id" : ObjectId("573c86d3017c5eb7d08aed6d"), "name" : "bob", "age" : 1 }
{ "_id" : ObjectId("573c88fe017c5eb7d08aed6e"), "name" : "tom", "age" : 10 }
更新多个文档
mongodb默认情况下如果匹配到了多个文档,在更新的时候默认只会更新第一个文档,其他文档不发生变化,如果要更新所有的文档则需要将update的第四个参数设置为true,默认为false
例如下面的例子,第一次更新不设置第四个参数,返回只update了一条数据,第二次设置第四个参数为true,则会匹配更新所有数据:
> db.test.find({"name":"brent"} )
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 40 }
{ "_id" : ObjectId("573c8bd3323f7f2e2ccb0e18"), "name" : "brent", "age" : 28 }
> db.test.update({"name":"brent"},{"$inc":{"age":1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.update({"name":"brent"},{"$inc":{"age":1}},false,true)
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
> db.test.find({"name":"brent"} )
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 42 }
{ "_id" : ObjectId("573c8bd3323f7f2e2ccb0e18"), "name" : "brent", "age" : 29 }
>
如果想知道多文档更新到底更新了多少文档,可以运行
getLastError命令,(可以理解为返回最后一次操作的相关信息),建n的值就是被更新的文档的数量:
> db.test.update({"name":"brent"},{"$inc":{"age":1}},false,true)
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
> db.runCommand({getLastError:1})
{
"connectionId" : 1,
"updatedExisting" : true,
"n" : 2,
"syncMillis" : 0,
"writtenTo" : null,
"err" : null,
"ok" : 1
}
返回被更新的文档
getLastError只能获得更新的相关信息,并不能返回被更新的文档,可以通过
findAndModify命令得到被更新的文档.
下面这个例子,查询status为ready的文档,进行处理,处理完成之后将ready改成done,先来看看当前的数据:
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 43, "status" : "done" }
{ "_id" : ObjectId("573c86d3017c5eb7d08aed6d"), "name" : "bob", "age" : 1, "status" : "ready" }
{ "_id" : ObjectId("573c88fe017c5eb7d08aed6e"), "name" : "tom", "age" : 10, "status" : "ready" }
{ "_id" : ObjectId("573c8bd3323f7f2e2ccb0e18"), "name" : "brent", "age" : 30, "status" : "done" }
下面这条语句是将集合test中status为ready的一个文档修改为running:
> ps=db.runCommand({"findAndModify":"test", #集合名
... "query":{"status":"ready"},
... "sort":{"age":-1},
... "update":{"$set":{"status":"running"}}}).value
然后你可以对这条数据进行一些处理
Process(ps).......
最后处理完成之后,将这个文档status设置为done
> db.test.update({"_id":ps._id},{"$set":{"status":"done"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test.find()
{ "_id" : ObjectId("573c858c323f7f2e2ccb0e17"), "name" : "brent", "age" : 43, "status" : "done" }
{ "_id" : ObjectId("573c86d3017c5eb7d08aed6d"), "name" : "bob", "age" : 1, "status" : "ready" }
{ "_id" : ObjectId("573c88fe017c5eb7d08aed6e"), "name" : "tom", "age" : 10, "status" : "done" }
{ "_id" : ObjectId("573c8bd3323f7f2e2ccb0e18"), "name" : "brent", "age" : 30, "status" : "done" }
这样就提供了一个很好的操作队列和原子性的方法,而且你可以写一个循环把集合中的文档类似于放到一个队列中处理.
findAndModify有很多参数可以使用,例如上面我们使用到了query,sort,update
findAndModify
字符串集合名
query
查询文档,用于检索文档的条件
sort
排序结果集,我们处理的时候可能需要按照某个顺序来进行处理
update
修改器文档,用于对匹配的文件进行修改
remove
布尔类型,表示是否删除文档(remove和update必须指定中的一个)
remove的语法如下:
.....
"remove"
:true
}).value
new
布尔类型,表示返回更新前的文档还是更新后的,默认返回更新前的文档
fields
文档中需要返回的字段(可选)
upsert
布尔类型,当是true的时候表示这是一个upsert,默认为false
update和remove必须有一个,而且只能有一个,如果没有匹配的文档,这个命令会返回一个错误