您的位置:首页 > 数据库 > Mongodb

mongodb高级操作(1)-update

2016-05-19 00:50 561 查看
前面我们已经介绍了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}
>deletea1.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":"xxxx@qq.com"}
>
>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":"xxxx@qq.com","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":"xxxx@qq.com","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":"xxxx@qq.com"}


使用"$set"修改内嵌文档:

>db.test.find()
{"_id":ObjectId("573957c55f74882a9bfa2d9e"),"name":"brent","age":30,"email":"xxxx@qq.com"}
{"_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":"xxxx@qq.com"}
{"_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":"xxxx@qq.com"}
{"_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":"xxxx@qq.com"}
{"_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":"xxxx@qq.com","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":"xxxx@qq.com","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:"verygood"}}})
WriteResult({"nMatched":1,"nUpserted":0,"nModified":1})
>db.test.find()
{"_id":ObjectId("573957c55f74882a9bfa2d9e"),"name":"brent","age":30,"email":"xxxx@qq.com","phone":888,"comments":[{"name":"jack","content":"good"},{"name":"tom","content":"verygood"}]}
{"_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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"jack",
			"content":"good"
	},
{
			"name":"tom",
			"content":"verygood"
	},
"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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"jack",
			"content":"good"
	},
{
			"name":"tom",
			"content":"verygood"
	},
"a",
"b",
"c",
"a",
"b",
"c",
"jack"
]


感觉ne就是notequal的意思

其实按照我的理解上面那句话就是当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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"jack",
			"content":"good"
	},
{
			"name":"tom",
			"content":"verygood"
	},
"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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"jack",
			"content":"good"
	},
{
			"name":"tom",
			"content":"verygood"
	},
"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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"jack",
			"content":"good"
	},
{
			"name":"tom",
			"content":"verygood"
	},
"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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"jack",
			"content":"good"
	},
{
			"name":"tom",
			"content":"verygood"
	},
"b",
"c",
"b",
"c",
"jack",
"andy",
"lily"
]
}



$基于位置的数组修改

如果数组有多个值,而我们只想对其中一部分进行操作,有两种方法操作数组中的值:通过位置或者定位操作符$

数组下标都是以0开始的,可以将下标直接作为键的选择元素,下面这个例子给comments这个数组的第一个元素添加一个num:1的元素

>db.test.findOne()
{
	"_id":ObjectId("573957c55f74882a9bfa2d9e"),
	"name":"brent",
	"age":30,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"tom",
			"content":"verygood"
	},
"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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"tom",
			"content":"verygood",
			"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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"tom",
			"content":"verygood",
			"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,
	"email":"xxxx@qq.com",
	"phone":888,
	"comments":[
{
			"name":"jack",
			"content":"verygood",
			"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}


saveshell帮助程序

save是一个shell函数,如果文档不存在,它会自动创建文档,如果存在它就更新这个文档,他只有一个参数:文档.

如果这个文档中含有_id则会调用upsert,如果没有则调用insert

>vara=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必须有一个,而且只能有一个,如果没有匹配的文档,这个命令会返回一个错误
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: