1、概述
大家好,我是欧阳方超,可以关注我的公众号“欧阳方超”,后续内容将在公众号首发。今天介绍一些mongodb中的小操作。
2、更新字段值
依据集合a中的字段更新另一个集合b。
假设user1集合中有如下数据:
{
_id: ObjectId('65f907e7457c037e6a7d17a1'),
name: 'tom',
address: 'addresstom'
}
{
_id: ObjectId('65f907f0457c037e6a7d17a2'),
name: 'peter',
address: 'addresspeter'
}
{
_id: ObjectId('65f907f5457c037e6a7d17a3'),
name: 'jaker',
address: 'addressjaker'
}
集合newUser中有如下数据:
{
_id: ObjectId('65f9063a457c037e6a7d179e'),
oldname: 'tom',
newname: 'tomnew'
}
{
_id: ObjectId('65f90650457c037e6a7d179f'),
oldname: 'peter',
newname: 'peternew'
}
{
_id: ObjectId('65f9065a457c037e6a7d17a0'),
oldname: 'jaker',
newname: 'jakernew'
}
2.1、使用out进行更新
有这样一个需求,想对user1集合中的name进行更新,依据newUser集合中的数据进行更新。可以使用下面的命令完成这种需求(注意需要谨慎使用 $out 阶段,以避免意外清空目标集合,建议将本篇后面介绍的merge更新看完弄懂之后再结合情况更新自己的mongodb库中的数据):
db.user1.aggregate([
{
$lookup:
{
from: "newUser",
localField: "name",
foreignField: "oldname",
as: "matched_docs"
}
},
{ $unwind: "$matched_docs" },
{ $set:
{
"name": "$matched_docs.newname"
}
},
{ $out: "user1" }
])
这段代码是一个 MongoDB 的聚合操作,用于在集合 user1 中根据另一个集合 newUser 的数据更新字段 name。让我详细解释一下每个阶段的作用:
$lookup 阶段:这个阶段用于在当前集合 user1 中查找另一个集合 newUser 中与当前文档的 name 字段匹配的文档,并将匹配的结果存储在一个新的字段 matched_docs 中。
$unwind 阶段:由于 $lookup 阶段返回的结果是一个数组,使用 $unwind 阶段将数组展开,使每个匹配的文档单独处理。
$set 阶段:在这个阶段,将当前文档中的 name 字段更新为匹配文档中的 newname 字段的值。
$out 阶段:最后,使用 $out 阶段将更新后的结果写回到原始集合 user1 中,实现了根据另一个集合中的数据更新当前集合中字段的目的。
这个聚合操作利用了 MongoDB 的强大功能,通过多个阶段逐步处理数据,实现了跨集合数据更新的需求。
这些阶段是可以单独执行的,如果要看到每步发生的变化,可以单独执行以看其效果,比如只执行lookup阶段,将上面的命令简化一下:
db.user1.aggregate([
{
$lookup:
{
from: "newUser",
localField: "name",
foreignField: "oldname",
as: "matched_docs"
}
}
])
执行结果为:
{
_id: ObjectId('65f907e7457c037e6a7d17a1'),
name: 'tom',
address: 'addresstom',
matched_docs: [
{
_id: ObjectId('65f9063a457c037e6a7d179e'),
oldname: 'tom',
newname: 'tomnew'
}
]
}
{
_id: ObjectId('65f907f0457c037e6a7d17a2'),
name: 'peter',
address: 'addresspeter',
matched_docs: [
{
_id: ObjectId('65f90650457c037e6a7d179f'),
oldname: 'peter',
newname: 'peternew'
}
]
}
{
_id: ObjectId('65f907f5457c037e6a7d17a3'),
name: 'jaker',
address: 'addressjaker',
matched_docs: [
{
_id: ObjectId('65f9065a457c037e6a7d17a0'),
oldname: 'jaker',
newname: 'jakernew'
}
]
}
执行包含了从lookup到out阶段通过这个命令,可以根据一个集合中的特定字段值更新另一个集合中对应字段的值。
不过这样会导致user1表中多了一个字段,这是因为我们将聚合得到的所有字段都回写到了user1集合,如下:
{
_id: ObjectId('65f907e7457c037e6a7d17a1'),
name: 'tomnew',
address: 'addresstom',
matched_docs: {
_id: ObjectId('65f9063a457c037e6a7d179e'),
oldname: 'tom',
newname: 'tomnew'
}
}
{
_id: ObjectId('65f907f0457c037e6a7d17a2'),
name: 'peternew',
address: 'addresspeter',
matched_docs: {
_id: ObjectId('65f90650457c037e6a7d179f'),
oldname: 'peter',
newname: 'peternew'
}
}
{
_id: ObjectId('65f907f5457c037e6a7d17a3'),
name: 'jakernew',
address: 'addressjaker',
matched_docs: {
_id: ObjectId('65f9065a457c037e6a7d17a0'),
oldname: 'jaker',
newname: 'jakernew'
}
}
如果不想要这个多出来的字段,可以调整下更新时的命令,使用投影操作,在聚合的结果中指定不显示哪些字段:
db.user1.aggregate([
{
$lookup:
{
from: "newUser",
localField: "name",
foreignField: "oldname",
as: "matched_docs"
}
},
{ $unwind: "$matched_docs" },
{ $set:
{
"name": "$matched_docs.newname"
}
},
{ $project: { matched_docs: 0 } },
{ $out: "user1" }
])
2.1、使用merge进行有条件更新
注意注意,当多次执行上述MongoDB聚合操作时,会导致user1集合中的数据为空,原因是在每次执行该聚合操作时,最后一个阶段{ $out: “user1” }会将结果输出到user1集合中,覆盖原有数据。这意味着每次执行该聚合操作都会清空user1集合并将新的结果写入,设想某次聚合得到的结果为空,由于user1集合已被清空,把空数据写入到空集合中,最终导致的结果就是user1集合为空。
使用 $merge 阶段来将结果与现有数据进行合并更新是一种有效的方法,可以避免使用聚合结果完全覆盖整个目标集合。以下是具体的步骤和示例代码:
创建聚合操作: 在聚合操作中,使用 $merge 阶段将聚合结果与现有数据进行合并更新。
指定目标集合和匹配条件: 在 $merge 阶段中指定目标集合和匹配条件,以确定如何将结果合并到目标集合中。
选择合适的合并选项: 在 $merge 阶段中选择适当的合并选项,如 whenMatched, whenNotMatched, pipeline, 等,以定义更新策略。
下面是一个示例代码,演示如何在 MongoDB 聚合操作中使用 $merge 阶段来将结果与现有数据进行合并更新:
db.user1.aggregate([
{
$lookup:
{
from: "newUser",
localField: "name",
foreignField: "oldname",
as: "matched_docs"
}
},
{ $unwind: "$matched_docs" },
{ $set:
{
"name": "$matched_docs.newname"
}
},
{$project:{matched_docs:0}},
{
$merge:
{
into: "user1", // 目标集合
on: "_id", // 匹配条件
whenMatched: "merge", // 当匹配时执行合并操作
whenNotMatched: "insert" // 当不匹配时执行插入操作
}
}
])
上面的命令重复执行,user1集合中的数据只被更新一次。
2、更新字段的部分值
更改字段的部门内容,比如将字段值中包含"你好"的部分替换为"您好",可以使用下面的命令:
db.collection_name.update({},
[
{
$addFields: {
field_name: {
$replaceOne: {
input: "$field_name",
find: "你好",
replacement: "您好"
}
}
}
}
],
{ multi: true }
)
更新哪个字段,将 field_name 替换为要更新的字段名称,确保将 multi: true 选项包含在更新中,以便更新所有匹配的文档。
实现效果与mysql中replace函数类似:
UPDATE 表名 SET 字段名= REPLACE( 替换前的字段值, '替换前关键字', '替换后关键字' ) WHERE 字段名 REGEXP "替换前的字段值";
3、总结
本篇介绍了mongodb中更新字段值的方法,这里更新字段值有两种含义,一种是依据一个集合中的数据更新另一个集合中的数据,一种是将一个集合中某个字段的值进行部分更新,希望对有相关操作的人能有所帮助。需要注意的是,由于out操作会先将目标集合清空数据, 再讲聚合结果覆盖到目标集合,所有当聚合结果可能是空的情况时要谨慎操作,一种替代方式就是使用merge操作对数据进行有条件更新。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。