Mongo shell 更新文档操作
- 官方文档地址:https://www.mongodb.com/docs/v6.0/reference/update-methods/
- 中文文档地址:https://mongodb.net.cn/manual/reference/update-methods/
MongoDB提供了以下更新集合中文档的方法:
方法 | 描述 |
---|---|
db.collection.updateOne() | 即使多个文档可能与指定的过滤器匹配,也最多更新一个与指定的过滤器匹配的文档。 3.2版中的新功能。 |
db.collection.updateMany() | 更新所有与指定过滤器匹配的文档。 3.2版中的新功能。 |
db.collection.replaceOne() | 即使多个文档可能与指定过滤器匹配,也最多替换一个与指定过滤器匹配的文档。 3.2版中的新功能。 |
db.collection.update() | 更新或替换与指定过滤器匹配的单个文档,或更新与指定过滤器匹配的所有文档。 默认情况下,该 db.collection.update() 方法更新单个文档。要更新多个文档,请使用 multi 选项。 |
另外的方法
以下方法还可以更新集合中的文档:
- db.collection.findOneAndReplace()
- db.collection.findOneAndUpdate()
- db.collection.findAndModify()
- db.collection.save()
- db.collection.bulkWrite()
有关更多方法和示例,请参见各个方法的参考页。
db.collection.update() 语法
db.collection.update(<query>, <update>, <options>)
更新整篇文档
- 如果
<update>
文档不包含任何更新操作符,db.collection.update() 将会使用<update>
文档直接替换集合中符合<query>
筛选条件的文档。- 文档主键 _id 不可以更改。
- 在使用
<update>
文档替换整篇被更新文档时,只有 第一篇 符合<query>
筛选条件的文档会被更新。
更新特定字段
- 如果
<update>
文档中只包含更新操作符,db.collection.update() 将会使用<update>
文档更新集合中符合<query>
筛选条件文档中的特定字段
文档更新操作符
1. $set
更新或者新增字段
语法:{ $set: { <newField>: <expression>, ... } }
- 如果向现有的数组字段范围以外的位置添加新值,数组字段的长度会扩大,未被赋值的数组成员将被设置为 "null"
示例:
# 更新 jack 的银行账户余额和开户行信息
> db.accounts.find({name:"jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"balance" : 1000,
"contact" : [
"111",
"Alabama",
"US"
]
}
# 更新 jack 的银行账户余额和开户行信息
> db.accounts.update(
{name:"jack"},
{$set:
{
balance: 200,
info: {dataOpened: new Date("2018-06-08T16:30:00Z"), branch: "branch1"}
}
}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.getCollection('accounts').find({name: "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"balance" : 200,
"contact" : [
"111",
"Alabama",
"US"
],
"info" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z"),
"branch" : "branch1"
}
}
# 更新内嵌字段(内嵌数组字段)
> db.accounts.update(
... {name:"jack"},
... {$set:
... {
... "info.branch": "branch2",
... "contact.1": "222"
... }
... }
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.getCollection('accounts').find({name: "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"balance" : 200,
"contact" : [
"111",
"222",
"US"
],
"info" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z"),
"branch" : "branch2"
}
}
# 内嵌数组字段增加值
> db.accounts.update(
... {name: "jack"},
... {
... $set: {"contact.5": "222"}
... }
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.getCollection('accounts').find({name: "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"balance" : 200,
"contact" : [
"111",
"222",
"US",
null,
null,
"222"
],
"info" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z"),
"branch" : "branch2"
}
}
>
2. $unset
删除字段
语法:{ $unset: { <field1>: "", ... } }
$unset
命令中对字段的赋任何值,对字段的删除没有任何影响$unset
命令中字段根本不存在,那么文档内容将保持不变- 使用
$unset
命令删除数组的某一元素时,这个元素不会被删除,只会被赋值 "null", 而数组的长度不会改变。
示例:```
# 删除 jack 的银行账户余额和开户地点
> db.accounts.update(
... {name: "jack"},
... {
... $unset: {balance: "", "info.branch": ""}
... }
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.getCollection('accounts').find({name: "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"contact" : [
"111",
"222",
"US",
null,
null,
"222"
],
"info" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z")
}
}
3. $rename
重命名字段
语法:{$rename: { <field1>: <newName1>, <field2>: <newName2>, ... } }
- 如果
$rename
重命名的字段不存在,那么文档内容不会被改变 - 如果新的重命名字段名已存在,那么原有的这个字段也会被覆盖
- 内嵌文档字段的重命名
- 通过重命名可以实现移动字段的效果
- 重命名数组中内嵌文档的字段是不可以的
- 新的重命名字段也不能是数组中的元素
$rename
命令中的新旧字段都不能指向数组中的元素
当 \(rename 的新字段存在时,\)rename 命令会先 $unset 新旧字段,然后 $set 新字段
// 1. 重名字段示例
> db.accounts.find({"name": "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"contact" : [
"111",
"222",
"US",
null,
null,
"222"
],
"info" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z")
}
}
> db.accounts.update({"name": "jack"}, {$rename: {"info": "info_re"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.accounts.find({"name": "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"contact" : [
"111",
"222",
"US",
null,
null,
"222"
],
"info_re" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z")
}
}
// 2. 重名字段不存在示例
> db.accounts.update({"name": "jack"}, {$rename: {"info_1": "info_xxx"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.accounts.find({"name": "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"contact" : [
"111",
"222",
"US",
null,
null,
"222"
],
"info_re" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z")
}
}
// 3. 如果新的重命名字段名已存在,那么原有的这个字段也会被覆盖 示例
> db.accounts.find({"name": "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"contact" : [
"111",
"222",
"US",
null,
null,
"222"
],
"info_re" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z")
}
}
>
> db.accounts.update({"name": "jack"}, {$rename: {"info_re": "contact"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "jack"}).pretty()
{
"_id" : ObjectId("63287530f8526f895273d037"),
"name" : "jack",
"contact" : {
"dataOpened" : ISODate("2018-06-08T16:30:00Z")
}
}
// 4. 通过重命名可以实现移动字段的效果
> db.accounts.find({"name": "blice"}).pretty()
{
"_id" : ObjectId("6329d648f5bd549a110fc5d0"),
"name" : "blice",
"balance" : 90,
"contact" : [
"1111",
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
}
],
"info" : {
"dateOpene" : ISODate("2017-01-02T16:00:00Z"),
"branch" : "branch1"
}
}
> db.accounts.update({"name": "blice"}, {$rename: {"info.branch": "branch", "balance": "info.blance"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "blice"}).pretty()
{
"_id" : ObjectId("6329d648f5bd549a110fc5d0"),
"name" : "blice",
"contact" : [
"1111",
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
}
],
"info" : {
"dateOpene" : ISODate("2017-01-02T16:00:00Z"),
"blance" : 90
},
"branch" : "branch1"
}
>
// 重命名数组中内嵌文档的字段是不可以的
> db.accounts.update({"name": "blice"}, {$rename: {"contact.3.primaryEmail": "primaryEmail"}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 2,
"errmsg" : "The source field cannot be an array element, 'contact.3.primaryEmail' in doc with _id: ObjectId('6329d648f5bd549a110fc5d0') has an array field called 'contact'"
}
})
// 新的重命名字段也不能是数组中的元素
> db.accounts.update({"name": "blice"}, {$rename: {"branch": "contact.3.branch"}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 2,
"errmsg" : "The destination field cannot be an array element, 'contact.3.branch' in doc with _id: ObjectId('6329d648f5bd549a110fc5d0') has an array field called 'contact'"
}
})
4. inc
$mul
加减字段值(正数增加,负数减少)
命令:
{ $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }
{ $mul: { <field1>: <number1>, ... } }
inc
加减字段值- 正数增加,负数减少
- 如果字段不存在,会增加相应的字段,值为指定增加或减少的值
$mul
相乘字段值- 大于1相乘,0~1小数相除
- 如果字段不存在,会增加相应的字段,值为0
inc
$mul
只能应用在数字类型字段上
5. $min
$max
比较之后更新字段
命令:
{ $min: { <field1>: <value1>, ... } }
{ $max: { <field1>: <value1>, ... } }
$min
比较原本的值和指定值,最终取这两个中最小的值$max
比较原本的值和指定值,最终取这两个中最大的值$min
$max
不仅可以在数字类型字段使用,其他类型也可以使用,比如:时间类型,字符串类型 ...- 如果字段不存在,会新增字段,并且值设为
$min
$max
指定的值 - 如果被更新的字段类型和更新的类型不一致,
$min
$max
命令会安照BSON数据类型排序 规则进行比较
数组更新操作符
1. $addToSet
如果元素不存在将向数组添加元素
*命令: { $addToSet: { <field1>: <value1>, ... } }
- 如果要插入的值已经存在于数组中,则不会被再添加重复值。
- 注意: 使用
$addToSet
插入数组和文档时,插入值中字段的顺序和已有值重复的时候,才算作重复值被忽略,例如数组中元素是 文档或者数组时,如果顺序不一样,则不会认为是重复值。 $addToSet
会将数组类型当做元素插入数组字段中,成为内嵌数组- 如果想多个元素直接添加到数组字段中,则需要使用
$each
操作符 - 如果数组字段不存在,这个字段将被添加到原文档中
// - `$addToSet` 会将数组类型当做元素插入数组字段中,成为内嵌数组
// - 如果想多个元素直接添加到数组字段中,则需要使用 `$each` 操作符
> db.accounts.find({"name": "blice"}, {"contact": 1, "_id": 0}).pretty()
{
"contact" : [
"1111",
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
}
]
}
>
> db.accounts.update({"name": "blice"}, {$addToSet: {"contact": ["test1", "test2"]}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "blice"}, {"contact": 1, "_id": 0}).pretty()
{
"contact" : [
"1111",
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
]
]
}
> db.accounts.update({"name": "blice"}, {$addToSet: {"contact": {$each: ["test1", "test2"]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "blice"}, {"contact": 1, "_id": 0}).pretty()
{
"contact" : [
"1111",
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
],
"test1",
"test2"
]
}
2. $pop
从数组的首或者尾移除元素
命令: { $pop: { <field>: <-1 | 1>, ... } }
- 可以对数组中的数组元素进行
$pop
操作 - 如果数组中所有元素都清除了,依然会保留空数组,字段不会被清除
> db.accounts.find({"name": "blice"}, {"contact": 1, "_id": 0}).pretty()
{
"contact" : [
"1111",
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
],
"test1",
"test2"
]
}
>
> db.accounts.update({"name": "blice"}, {$pop: {"contact": 1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "blice"}, {"contact": 1, "_id": 0}).pretty()
{
"contact" : [
"1111",
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
],
"test1"
]
}
> db.accounts.update({"name": "blice"}, {$pop: {"contact": -1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "blice"}, {"contact": 1, "_id": 0}).pretty()
{
"contact" : [
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
],
"test1"
]
}
3. $pull
从数组有选择的移除元素
命令: { $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }
- 如果数组元素本身就是一个内嵌数组,可以使用 $elemMatch 来对内嵌数组进行筛选
$pull
命令会删去包含指定的文档字段和字段值的文档元素,字段排列顺序不需要完全匹配,区别于$pullAll
> db.accounts.find({"name": "bili"}, {contact: 1, "_id": 0}).pretty()
{
"contact" : [
"beijing",
"China",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
],
"test1"
]
}
>
> db.accounts.update({"name": "bili"}, {$pull: {"contact": {$regex: /Chi/}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "bili"}, {contact: 1, "_id": 0}).pretty()
{
"contact" : [
"beijing",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
],
"test1"
]
}
>
> db.accounts.find({"name": "bili"}, {contact: 1, "_id": 0}).pretty()
{
"contact" : [
"beijing",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
[
"test1",
"test2"
],
"test1"
]
}
> db.accounts.update({"name": "bili"}, {$pull: {"contact": {$elemMatch: {$eq: "test1"}}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.accounts.find({"name": "bili"}, {contact: 1, "_id": 0}).pretty()
{
"contact" : [
"beijing",
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
"test1"
]
}
4. $pullAll
从数组有选择的移除元素
语法: { $pullAll: { <field1>: [ <value1>, <value2> ... ], ... } }
$pullAll
和$pull
命令对比:
{ $pullAll: { <field1>: [ <value1>, <value2>]} }
相当于
{ $pull: { <field1>: {$in: [<value1>, <value2>]}}}
$pullAll
和$pull
,如果要删除的元素是一个数组(内嵌数组),数组元素的值和排列顺序都必须和被删除的数组完全一样$pullAll
和$pull
, 如果要删除的元素是一个文档(内嵌文档),$pullAll
需要数组元素的值和排列顺序都必须和被删除的数组完全一样,才会删除,而$pull
只需文档类型的元素包含指定值,就会被删除
5. $push
向数组中添加元素
语法: { $push: { <field1>: <value1>, ... } }
$push
和$addToSet
命令一样,如果指定数组字段不存在于文档中,这个字段将会添加原文档中。- 如果过滤到多个文档时,
$push
将会对每个文档进行操作,详见官方文档示例 - 配合
$each
可以向数组字段中添加多个元素 - 更多用法详见官方文档示例
6. $
充当占位符以更新与查询条件匹配的第一个元素。
- 示例详见官方文档
7. $[]
充当占位符,为匹配查询条件的文档更新数组中的所有元素。
> db.accounts.find({"name": "bili"}, {"contact": 1}).pretty()
{
"_id" : ObjectId("634440158bad614c1c907498"),
"contact" : [
[
"11111",
"2222"
],
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
"test1"
]
}
> db.accounts.update({"name": "bili"}, {$set: {"contact.0.$[]": "00"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
>
> db.accounts.find({"name": "bili"}, {"contact": 1}).pretty()
{
"_id" : ObjectId("634440158bad614c1c907498"),
"contact" : [
[
"00",
"00"
],
{
"primaryEmail" : "xxx@gmail.com",
"secondaryEmail" : "yyy@gmail.com"
},
"test1"
]
}
更新操作 <options>
db.collection.update(<query>, <update>, <options>)
<options>
文档提供了 update 命令的更多选项{multi: <boolean>}
更新多个文档- 在默认情况下,即使筛选条件对应了多篇文档,update 命令任然只会更新 一篇 文档
- 注意,MongoDB 只能保证 单个文档 操作的原子性,不能保证多个文档操作的原子性
- 如果需要保证多个文档操作的原子性,就需要使用 MongoDB > 4.0 版本引入的事务功能