使用新字段插入数据或使用 updateOne mongodb 有条件地更新
Insert data with new fields OR update conditionally using updateOne mongodb
我有一个格式如下的文档:
{
"f1": "v1",
"f2": {
"id": 1,
"sub": "subv",
"updatedAt": 123
}
}
我有另一个来源给我一个 inputf2
对象。
我想编写一个更新插入查询来查找具有匹配过滤器 {"f2.id": inputf2.id}
的文档。 I̶f̶ ̶f̶o̶u̶n̶d̶ ̶a̶n̶d̶ ̶i̶f̶ ̶f̶2̶.̶u̶p̶d̶a̶t̶e̶d̶A̶t̶ ̶<̶ ̶i̶n̶p̶u̶t̶f̶2̶.̶u̶p̶d̶a̶t̶e̶d̶A̶t̶ ̶t̶h̶e̶n̶ ̶u̶p̶d̶a̶t̶e̶ ̶w̶h̶o̶l̶e̶ ̶f̶2̶ ̶t̶o̶ ̶i̶n̶p̶u̶t̶f̶2̶.̶ ̶O̶t̶h̶e̶r̶w̶i̶s̶e̶,̶ ̶i̶n̶s̶e̶r̶t̶ ̶a̶ ̶n̶e̶w̶ ̶d̶o̶c̶u̶m̶e̶n̶t̶ ̶w̶i̶t̶h̶ ̶n̶e̶w̶ ̶f̶i̶e̶l̶d̶ ̶f̶1̶ ̶(̶v̶1̶ ̶i̶s̶ ̶a̶ ̶h̶a̶r̶d̶c̶o̶d̶e̶d̶ ̶v̶a̶l̶u̶e̶)̶.
编辑更清晰的逻辑:
- 如果找到 id AND f2.updatedAt < inputf2.updatedAt => 将整个 f2 更新为 inputf2。
- 如果找到了 id AND f2.updatedAt >= inputf2.updatedAt => 什么也不做(没有更新f2, 不创建新文档).
- 如果未找到 id => 使用新字段 f1 创建新文档(v1 是硬编码值)
示例:
输入:
{"id": 1,"sub": "newsubv","updatedAt": 100}
因为100 < 123.id = 1 不会被更新。 输出:
{
"f1": "v1",
"f2": {"id": 1,"sub": "subv","updatedAt": 123}
}
输入:
{"id": 1,"sub": "newsubv","updatedAt": 150}
因为150 > 123.id = 1 会被更新。 输出:
{
"f1": "v1",
"f2": {"id": 1,"sub": "newsubv","updatedAt": 150}
}
输入:
{"id": 2,"sub": "newsubv","updatedAt": 100}
因为在db中没有找到input id = 2。无论 updatedAt
是什么,它都会被插入。 输出:
{
"f1": "v1",
"f2": {"id": 1,"sub": "subv","updatedAt": 123}
},
{
"f1": "v1",
"f2": {"id": 2,"sub": "newsubv","updatedAt": 100}
}
我尝试了两种类型的更新,更新文档或更新聚合管道,但似乎其中任何一种都符合我的要求。
有更新文件
我可以使用 $setOnInsert
但不能将 f2
设置为 f2.updatedAt < inputf2.updatedAt
条件:
db.collection.update({
"f2.id": "1"
},
{
"$set": {
// Can not check updatedAt = 100 < 123 and then do nothing
"f2": {
"id": 1
"sub": "newsubv",
"updatedAt": 100
}
},
"$setOnInsert": {
"f1": "v1"
}
},
{"upsert": true});
有更新聚合管道
我可以通过将 f2
克隆到新字段 oldf2
,用 f2.updatedAt < inputf2.updatedAt
条件更新 f2
,然后应用条件并删除 oldf2
.但是在聚合中,没有$setOnItem
函数:
db.test.updateOne(
{"f2.id": 1},
[
{$set: {"oldf2": "$f2"}},
{
$set: {
"f2": {
$cond: [{$lt: ["$f2.updatedAt", 100]}, {
"id": 1,
"sub": "newsubv",
"updatedAt": 100
}, "$oldf2"]
}
}
},
{$set: {"oldf2": "$$REMOVE"}},
// How to apply something like $setOnInsert function?
],
{"upsert":true})
我必须使用 updateOne
因为有 f2
项目的列表,我想在 bulkWrite
.
中批量更新这些项目
已注明:f2.id
是集合中的唯一字段
任何人都可以帮我写这个逻辑的查询吗?非常感谢你们。
您几乎可以像以前一样使用第一种方法,只是有一个条件。
编辑:但是如果你想在插入的情况下改变f2.id
,你应该分开f2
,像这样:
db.collection.update({
"f2.id": 1,
"f2.updatedAt": {
$lte: inputf2.updatedAt // Here you can check updatedAt = 100 < 123 and then stop updating
}
},
{
"$set": {
"f2.sub": "newsubv",
"f2.updatedAt": 100
},
"$setOnInsert": {
"f1": "v1",
"f2.id": newF2,
}
},
{
"upsert": true
})
你可以在 playground 上看到它
您可以将 inputf2.updatedAt
设置为 100 或 150 以查看我在此处粘贴的两种情况:
如果f2.updatedAt
>= inputf2.updatedAt
,我们将在数据库中得到两个不同的文档。新的将有 inputf2.updatedAt
,但有新的 ID,因为这应该是一个唯一的 ID:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"f1": "v1",
"f2": {
"id": 1,
"sub": "subv",
"updatedAt": 123
}
},
{
"_id": ObjectId("625b1873cdbb97ce928d8898"),
"f1": "v1",
"f2": {
"id": 2,
"sub": "newsubv",
"updatedAt": 100
}
}
]
否则,f2.updatedAt
< inputf2.updatedAt
: 数据库上的文档将被更新,但将保留其原始 ID:
{
"_id": ObjectId("5a934e000102030405000000"),
"f1": "v1",
"f2": {
"id": 1,
"sub": "newsubv",
"updatedAt": 150
}
}
顺便说一句,在批量更新中使用此方法没有问题
终于可以写查询了。这是我的解决方案:
db.test.updateOne(
{"f2.id": 1},
[{
$set: {
"f2": { $cond: [
{$lt: ["$f2.updatedAt", 100]}, // time check condition
{"id": 1,"sub": "newsubv","updatedAt": 100}, // overwrite value
"$f2" // no change, return origin value
]},
"f1": {$ifNull: ["$f1", "v1"]}, // this can do the same as $setOnInsert
}
}],
{"upsert":true}
)
测试用例:
输入 1:Mongo Playground
{"id": 1,"sub": "newsubv","updatedAt": 100}
输入 2:Mongo Playground
{"id": 1,"sub": "newsubv","updatedAt": 200}
输入 3:Mongo Playground
{"id": 2,"sub": "newsubv","updatedAt": 100}
我有一个格式如下的文档:
{
"f1": "v1",
"f2": {
"id": 1,
"sub": "subv",
"updatedAt": 123
}
}
我有另一个来源给我一个 inputf2
对象。
我想编写一个更新插入查询来查找具有匹配过滤器 {"f2.id": inputf2.id}
的文档。 I̶f̶ ̶f̶o̶u̶n̶d̶ ̶a̶n̶d̶ ̶i̶f̶ ̶f̶2̶.̶u̶p̶d̶a̶t̶e̶d̶A̶t̶ ̶<̶ ̶i̶n̶p̶u̶t̶f̶2̶.̶u̶p̶d̶a̶t̶e̶d̶A̶t̶ ̶t̶h̶e̶n̶ ̶u̶p̶d̶a̶t̶e̶ ̶w̶h̶o̶l̶e̶ ̶f̶2̶ ̶t̶o̶ ̶i̶n̶p̶u̶t̶f̶2̶.̶ ̶O̶t̶h̶e̶r̶w̶i̶s̶e̶,̶ ̶i̶n̶s̶e̶r̶t̶ ̶a̶ ̶n̶e̶w̶ ̶d̶o̶c̶u̶m̶e̶n̶t̶ ̶w̶i̶t̶h̶ ̶n̶e̶w̶ ̶f̶i̶e̶l̶d̶ ̶f̶1̶ ̶(̶v̶1̶ ̶i̶s̶ ̶a̶ ̶h̶a̶r̶d̶c̶o̶d̶e̶d̶ ̶v̶a̶l̶u̶e̶)̶.
编辑更清晰的逻辑:
- 如果找到 id AND f2.updatedAt < inputf2.updatedAt => 将整个 f2 更新为 inputf2。
- 如果找到了 id AND f2.updatedAt >= inputf2.updatedAt => 什么也不做(没有更新f2, 不创建新文档).
- 如果未找到 id => 使用新字段 f1 创建新文档(v1 是硬编码值)
示例:
输入:
{"id": 1,"sub": "newsubv","updatedAt": 100}
因为100 < 123.id = 1 不会被更新。 输出:
{
"f1": "v1",
"f2": {"id": 1,"sub": "subv","updatedAt": 123}
}
输入:
{"id": 1,"sub": "newsubv","updatedAt": 150}
因为150 > 123.id = 1 会被更新。 输出:
{
"f1": "v1",
"f2": {"id": 1,"sub": "newsubv","updatedAt": 150}
}
输入:
{"id": 2,"sub": "newsubv","updatedAt": 100}
因为在db中没有找到input id = 2。无论 updatedAt
是什么,它都会被插入。 输出:
{
"f1": "v1",
"f2": {"id": 1,"sub": "subv","updatedAt": 123}
},
{
"f1": "v1",
"f2": {"id": 2,"sub": "newsubv","updatedAt": 100}
}
我尝试了两种类型的更新,更新文档或更新聚合管道,但似乎其中任何一种都符合我的要求。
有更新文件
我可以使用 $setOnInsert
但不能将 f2
设置为 f2.updatedAt < inputf2.updatedAt
条件:
db.collection.update({
"f2.id": "1"
},
{
"$set": {
// Can not check updatedAt = 100 < 123 and then do nothing
"f2": {
"id": 1
"sub": "newsubv",
"updatedAt": 100
}
},
"$setOnInsert": {
"f1": "v1"
}
},
{"upsert": true});
有更新聚合管道
我可以通过将 f2
克隆到新字段 oldf2
,用 f2.updatedAt < inputf2.updatedAt
条件更新 f2
,然后应用条件并删除 oldf2
.但是在聚合中,没有$setOnItem
函数:
db.test.updateOne(
{"f2.id": 1},
[
{$set: {"oldf2": "$f2"}},
{
$set: {
"f2": {
$cond: [{$lt: ["$f2.updatedAt", 100]}, {
"id": 1,
"sub": "newsubv",
"updatedAt": 100
}, "$oldf2"]
}
}
},
{$set: {"oldf2": "$$REMOVE"}},
// How to apply something like $setOnInsert function?
],
{"upsert":true})
我必须使用 updateOne
因为有 f2
项目的列表,我想在 bulkWrite
.
已注明:f2.id
是集合中的唯一字段
任何人都可以帮我写这个逻辑的查询吗?非常感谢你们。
您几乎可以像以前一样使用第一种方法,只是有一个条件。
编辑:但是如果你想在插入的情况下改变f2.id
,你应该分开f2
,像这样:
db.collection.update({
"f2.id": 1,
"f2.updatedAt": {
$lte: inputf2.updatedAt // Here you can check updatedAt = 100 < 123 and then stop updating
}
},
{
"$set": {
"f2.sub": "newsubv",
"f2.updatedAt": 100
},
"$setOnInsert": {
"f1": "v1",
"f2.id": newF2,
}
},
{
"upsert": true
})
你可以在 playground 上看到它
您可以将 inputf2.updatedAt
设置为 100 或 150 以查看我在此处粘贴的两种情况:
如果f2.updatedAt
>= inputf2.updatedAt
,我们将在数据库中得到两个不同的文档。新的将有 inputf2.updatedAt
,但有新的 ID,因为这应该是一个唯一的 ID:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"f1": "v1",
"f2": {
"id": 1,
"sub": "subv",
"updatedAt": 123
}
},
{
"_id": ObjectId("625b1873cdbb97ce928d8898"),
"f1": "v1",
"f2": {
"id": 2,
"sub": "newsubv",
"updatedAt": 100
}
}
]
否则,f2.updatedAt
< inputf2.updatedAt
: 数据库上的文档将被更新,但将保留其原始 ID:
{
"_id": ObjectId("5a934e000102030405000000"),
"f1": "v1",
"f2": {
"id": 1,
"sub": "newsubv",
"updatedAt": 150
}
}
顺便说一句,在批量更新中使用此方法没有问题
终于可以写查询了。这是我的解决方案:
db.test.updateOne(
{"f2.id": 1},
[{
$set: {
"f2": { $cond: [
{$lt: ["$f2.updatedAt", 100]}, // time check condition
{"id": 1,"sub": "newsubv","updatedAt": 100}, // overwrite value
"$f2" // no change, return origin value
]},
"f1": {$ifNull: ["$f1", "v1"]}, // this can do the same as $setOnInsert
}
}],
{"upsert":true}
)
测试用例:
输入 1:Mongo Playground
{"id": 1,"sub": "newsubv","updatedAt": 100}
输入 2:Mongo Playground
{"id": 1,"sub": "newsubv","updatedAt": 200}
输入 3:Mongo Playground
{"id": 2,"sub": "newsubv","updatedAt": 100}