前一部分的文章:
MongoDB 快速入门实战教程基础篇 一 :文档的 CR操作
Update Operations
MongoDB 提供了几个方法用于更新文档,它们分别是:
db.collection.updateOne(<filter>, <update>, <options>)
db.collection.updateMany(<filter>, <update>, <options>)
db.collection.replaceOne(<filter>, <update>, <options>)
更新单个文档
updateOne()
方法会根据过滤器更新集合中的单个文档,其语法格式如下:
db.collection.updateOne(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ]
}
)
例如将 inven
集合中名为 韦德
的球员名称改为 热火韦德
,对应示例如下:
> db.inven.updateOne(
... {name: "韦德"},
... {$set: {name: "热火韦德"}}
... )
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
返回的结果文档包含操作状态 acknowledged
,匹配的文档数 matchedCount
和修改过的文档数 modifiedCount
。本次返回结果文档代表修改成功,韦德
的名字被改变了。
Upsert
在实际应用中,upsert
指令非常常见。当 upsert
的值为 true
时,如果 <filter>
并匹配到文档,那么本次操作就会将它当作新文档添加到集合中。例如当文档中没有名为 ABC
的球员时,会将 奥尼尔
添加到集合中,对应示例如下:
> db.inven.updateOne( {name: "ABC"}, {$set: {name: "奥尼尔"}}, {upsert: true})
{
"acknowledged" : true,
"matchedCount" : 0,
"modifiedCount" : 0,
"upsertedId" : ObjectId("5d15d6c718f0856b4385c123")
}
结果文档显示本次操作未匹配到文档,也未更新文档,但操作成功。相对于上一次的结果文档,本次结果文档中多出了 upsertedId
,这正是 奥尼尔
文档的 _id
。我们可以通过 find()
方法来验证:
> db.inven.find({name: "奥尼尔"})
{ "_id" : ObjectId("5d15d6c718f0856b4385c123"), "name" : "奥尼尔" }
果然, 奥尼尔
被添加到集合中。
更新多个文档
updateMany()
方法可以更新多个文档,其语法格式与 updateOne()
相同。假设要将球衣号大于 20 的所有球员球衣号设置为 33,对示例如下:
> db.inven.updateMany(
... {number: {$gt: 20}},
... {$set: {number: 33}}
... )
{ "acknowledged" : true, "matchedCount" : 2, "modifiedCount" : 2 }
其他指令如 upsert
使用时与之前相同。
update()
方法的语法格式与 updateOne()
相同,但它默认只更新单个文档。假设要将球衣号等于 33 的球员球衣号设置为 0,对应示例如下:
> db.inven.update( {number: {$eq: 33}}, {$set: {number: 0}} )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
返回文档显示只更新了 1 个文档,其他查询符合条件的文档并未被更新。如果想要更新多个文档,可以将指令 multi
设置为 true
,或者使用 updateMany()
。
替换文档
replaceOne()
方法会根据过滤器替换集合中的单个文档,其语法如下:
db.collection.replaceOne(
<filter>,
<replacement>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>
}
)
假设要为 奥尼尔
添加球衣号,对应示例如下:
> db.inven.replaceOne( {name: "奥尼尔"}, {name: "奥尼尔", number: 34})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
命令执行后,奥尼尔
文档从 {name: "奥尼尔"}
变成了 {name: "奥尼尔", number: 34}
。
Save
save()
是一个多用途的方法,它会根据 _id
是否存在而选择调用 insert()
或者 update()
。当文档中存在 _id
时,save()
等效于带有 upsert
指令的 update()
;当文档中不包含 _id
时,save()
等效于 insert()
,此时 MongoShell 将创建一个 ObjectId
,并将其分配给 _id
。save()
的语法格式如下:
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
奥尼尔
的 _id
为 ObjectId("5d16c699dca60c968c6d8f69")
,当 save()
方法中的文档包含 _id
时会更新文档内容,对应示例如下:
> db.inven.save({_id: "5d16c699dca60c968c6d8f69", name: "奥尼尔", status: "R"})
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : "5d16c699dca60c968c6d8f69"
})
{"name" : "奥尼尔" }
变成了 {"name" : "奥尼尔", "status" : "R" }
。要注意的是,ObjectId("5d16c699dca60c968c6d8f69")
在命令中的写法是 "5d16c699dca60c968c6d8f69"
。
更新操作符
MongoDB 共有四类更新操作符,它们是:字段更新操作符、数组更新操作符、修饰操作符和按位操作符。
字段更新操作符
MongoDB 中共有 9 个字段更新操作符,它们分别是:
名称 | 描述 |
---|---|
$currentDate |
将字段的值设置为当前日期,可以是 Date 或 Timestamp 。 |
$inc |
将指定字段的值与传入的值相加。 |
$min |
仅当指定的值小于现有字段值时才更新字段。 |
$max |
仅当指定的值大于现有字段值时才更新字段。 |
$mul |
将指定字段的值与传入的值相乘。 |
$rename |
重命名字段。 |
$set |
设置文档中字段的值。 |
$setOnInsert |
如果更新导致文档插入,则设置字段的值。对修改现有文档的更新操作没有影响。 |
$unset |
从文档中删除指定的字段。 |
我们将在本节中挑选几个典型的字段更新操作符进行学习,对于作用相似或语法类似的将不作赘述。例如 $min
和 $max
,我们只需要了解其中一个即可。
currentDate
$currentDate
的作用是将字段的值设为当前日期,其语法格式如下:
{ $currentDate: { <field1>: <typeSpecification1>, ... } }
其中,<typeSpecification1>
可以是一个布尔值、 {$type: "timestamp"}
或者 {$type: "date"}
。在开始学习之前,我们需要准备以下数据:
> db.registers.save({_id: 1, name: "async", pwd: "123456", regTime: new Date()})
此时,regTime
的值为 ISODate("2019-07-10T08:40:02.253Z")
。假设要更新 regTime
的值,我们可以使用如下命令:
> db.registers.update(
... {_id: 1},
... {$currentDate:{regTime: true}
... })
命令执行后,regTime
的值就会被改变,新的文档内容类似于:
{ "_id" : 1, "name" : "async", "pwd" : "123456", "regTime" : ISODate("2019-07-10T08:43:24.981Z") }
当然,我们也可以使用 {$type: "timestamp"}
这种语法,命令如下:
> db.registers.update(
... {_id: 1},
... {$currentDate:{ regTime: {$type: "timestamp"}}
... })
命令执行后,regTime
的值也会被改变。要注意的是,由于 Date
与 Timestamp
格式不同,所以 regTime
的值的格式将由原来的 ISODate
变成 Timestamp
。
inc
$inc
的作用是按指定的数量增加字段的值,其语法格式如下:
{ $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }
假设现在有一个这样的文档:
{
_id: 1,
sku: "abc123",
quantity: 10,
metrics: {
orders: 2,
ratings: 3.5
}
}
如果我们需要在此基础上将 quantity
和 metrics.orders
的值做加法,对应示例如下:
db.products.update(
{ sku: "abc123" },
{ $inc: { quantity: -2, "metrics.orders": 1 } }
)
命令执行后,文档将会变成下面这样:
{
"_id" : 1,
"sku" : "abc123",
"quantity" : 8,
"metrics" : {
"orders" : 3,
"ratings" : 3.5
}
}
如果传入的不是数字,而是字符串或其它类型,我们将得到错误提示:
> db.products.update({sku: "abc123" },{$inc: {quantity: "async"}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 14,
"errmsg" : "Cannot increment with non-numeric argument: {quantity: \"async\"}"
}
})
$mul
的作用是将指定字段的值与传入的值相乘,这与 $inc
类似,此处不再赘述。$mul
的语法和介绍可查阅官方文档 $mul。
min
$min
的描述是“仅当指定的值小于现有字段值时才更新字段”,其语法格式如下:
{ $min: { <field1>: <value1>, ... } }
假设有以下数据:
{ _id: 1, highScore: 800, lowScore: 200 }
最高分 highScore
为 800
,最低分 lowScore
为 200
。以下 $min
操作将用指定的 150
与 200
进行比对,由于 150 < 200
,原文档中 lowScore
的值会被更新为指定的 150
。更新语句如下:
> db.scores.update( { _id: 1 }, { $min: { lowScore: 250 } } )
命令执行后,文档将会变成:
{ _id: 1, highScore: 800, lowScore: 150 }
示例中只演示了数字类型的比较,实际上 BSON 都可以进行比较,比较顺序参考 BSON comparison order。$max
将指定值与现有字段值进行比较,如果指定值大于现有字段值,则更新对应字段值。这与 $min
类似,此处不再赘述。$max
的语法和介绍可查阅官方文档 $max。
unset
网友评论