MongoDB
提供以下方法来更新集合中的文档:
方法 | 说明 |
---|---|
db.collection.updateOne() | 即使多个文档可能与指定筛选器匹配,最多也只更新与指定筛选器匹配的单个文档。 |
db.collection.updateMany() | 更新所有与指定过滤器匹配的文档。 |
db.collection.replaceOne() | 即使多个文档可能与指定筛选器匹配,最多也只替换与指定筛选器匹配的单个文档。 |
updateOne
和 updateMany
都将筛选文档作为第一个参数,将变更文档作为第二个参数,后者对要进行的更改进行描述。replaceOne
同样将筛选文档作为第一个参数,但第二个参数是一个用来替换所匹配的筛选文档的新文档。
更新文档是原子操作:如果两个更新同时发生,那么首先到达服务器的更新会先被执行,然后再执行下一个更新。因此,相互冲突的更新可以安全地迅速接连完成,而不会破坏任何文档:最后一次更新将“成功”。如果不想使用默认行为,则可以考虑使用文档版本控制模式。
replaceOne
会用新文档完全替换匹配的文档。这对于进行大规模模式迁移的场景非常有用。
restaurant
集合包含以下文档:
{ "_id" : 1, "name" : "Central Perk Cafe", "Borough" : "Manhattan" },
{ "_id" : 2, "name" : "Rock A Feller Bar and Grill", "Borough" : "Queens", "violations" : 2 },
{ "_id" : 3, "name" : "Empire State Pub", "Borough" : "Brooklyn", "violations" : 0 }
如下操作将替换一个 name: "Central Perk Cafe"
的单个文档:
video> db.restaurant.replaceOne(
... { "name" : "Central Perk Cafe" },
... { "name" : "Central Pork Cafe", "Borough" : "Manhattan" }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
如果未找到匹配项,那么操作将返回:
{
acknowledged: true,
insertedId: null,
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 0
}
如果未找到匹配项,设置 upsert: true
则会插入此文档:
video> db.restaurant.replaceOne(
... { "name" : "Pizza Rat's Pizzaria" },
... { "_id": 4, "name" : "Pizza Rat's Pizzaria", "Borough" : "Manhattan", "violations" : 8 },
... { upsert: true }
... )
{
acknowledged: true,
insertedId: 4,
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 1
}
通常文档只会有一部分需要更新。可以使用原子的更新运算符update operator
更新文档中的特定字段。更新运算符是特殊的键,可用于指定复杂的更新操作,比如更改、添加或删除键,甚至可以操作数组和内嵌文档。
现在集合包含以下文档:
{ "_id" : 1, "name" : "Central Perk Cafe", "Borough" : "Manhattan", "violations" : 3 },
{ "_id" : 2, "name" : "Rock A Feller Bar and Grill", "Borough" : "Queens", "violations" : 2 },
{ "_id" : 3, "name" : "Empire State Pub", "Borough" : "Brooklyn", "violations" : 4 },
{ "_id" : 4, "name" : "Pizza Rat's Pizzaria", "Borough" : "Manhattan", "violations" : 7 }
$set
$set
用来设置一个字段的值。如果这个字段不存在,则创建该字段。这对于更新模式或添加用户定义的键来说非常方便。
> db.restaurant.updateOne({"_id": 1}, {"$set": {"points": 75, "violations": 5}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.restaurant.findOne({"_id": 1})
{
_id: 1,
name: 'Central Perk Cafe',
Borough: 'Manhattan',
violations: 5,
points: 75
}
应该始终使用 $
修饰符来增加、修改或删除键。常见的错误做法是把键的值通过更新设置成其他值,如以下操作所示:
> db.restaurant.updateOne({"_id": 1}, {"points": 75, "violations": 5})
MongoInvalidArgumentError: Update document requires atomic operators
这会事与愿违。更新的文档必须包含更新运算符。
$inc
运算符可以用来修改已存在的键值或者在该键不存在时创建它。对于更新分析数据、因果关系、投票或者其他有数值变化的地方,使用这个会非常方便。
> db.restaurant.updateOne({"_id": 2}, {"$inc": {"points": 80}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.restaurant.findOne({"_id": 2})
{
_id: 2,
name: 'Rock A Feller Bar and Grill',
Borough: 'Queens',
violations: 2,
points: 80
}
_id
为2的文档中points
键之前不存在,因此 $inc
创建了这个键,并将值设置成了增加量:80。
和 $set
用法类似,$inc
是专门用来对数字进行递增和递减操作的。$inc
只能用于整型、长整型或双精度浮点型的值。如果用在其他任何类型(包括很多语言中会被自动转换为数值的类型,比如 null
、bool
以及数字构成的字符串)的值上,则会导致操作失败:
> db.strcounts.insertOne({"count": "1"})
{
acknowledged: true,
insertedId: ObjectId('6690f1ba4b5a0e2aed26d089')
}
> db.strcounts.update({}, {"$inc": {"count": 1}})
DeprecationWarning: Collection.update() is deprecated. Use updateOne, updateMany, or bulkWrite.
MongoServerError: Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('6690f1ba4b5a0e2aed26d089')} has the field 'count' of non-numeric type string
同样,$inc
键的值必须为数字类型。不能使用字符串、数组或者其他非数字类型的值:
> db.restaurant.updateOne({"_id": 2}, {"$inc": {"points": "80"}})
MongoServerError: Cannot increment with non-numeric argument: {points: "80"}
如果需要修改其他类型的值,可以使用$set
或者下面要讲到的数组运算符。
updateMany
updateOne
只会更新找到的与筛选条件匹配的第一个文档。如果匹配的文档有多个,它们将不会被更新。要修改与筛选器匹配的所有文档,请使用 updateMany
。updateMany
遵循与 updateOne
同样的语义并接受相同的参数。关键的区别在于可能会被更改的文档数量。
创建集合:
> db.restaurant.insertMany([
... { "_id" : 1, "name" : "Central Perk Cafe", "violations" : 3 },
... { "_id" : 2, "name" : "Rock A Feller Bar and Grill", "violations" : 2 },
... { "_id" : 3, "name" : "Empire State Sub", "violations" : 5 },
... { "_id" : 4, "name" : "Pizza Rat's Pizzaria", "violations" : 8 }
... ])
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4 } }
更新所有 violations
大于 4
的文档:
> db.restaurant.updateMany(
... { violations: { $gt: 4 } },
... { $set: { "Review" : true } }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 2,
modifiedCount: 2,
upsertedCount: 0
}
> db.restaurant.find()
[
{ _id: 1, name: 'Central Perk Cafe', violations: 3 },
{ _id: 2, name: 'Rock A Feller Bar and Grill', violations: 2 },
{ _id: 3, name: 'Empire State Sub', violations: 5, Review: true },
{ _id: 4, name: "Pizza Rat's Pizzaria", violations: 8, Review: true }
]