我有一个包含许多类似结构化文档的集合,两个文档看起来像
输入:
{
"_id": ObjectId("525c22348771ebd7b179add8"),
"cust_id": "A1234",
"score": 500,
"status": "A"
"clear": "No"
}
{
"_id": ObjectId("525c22348771ebd7b179add9"),
"cust_id": "A1234",
"score": 1600,
"status": "B"
"clear": "No"
}
Run Code Online (Sandbox Code Playgroud)
默认情况下clear,所有文档都是"No",
要求:我必须添加所有文件的分数cust_id,只要它们属于status "A"和status "B".如果score超过2000那么我必须更新所有文档的clear属性"Yes"相同cust_id.
预期产量:
{
"_id": ObjectId("525c22348771ebd7b179add8"),
"cust_id": "A1234",
"score": 500,
"status": "A"
"clear": "Yes"
}
{
"_id": ObjectId("525c22348771ebd7b179add9"),
"cust_id": "A1234",
"score": 1600,
"status": "B"
"clear": "Yes"
}
Run Code Online (Sandbox Code Playgroud)
是的,因为1600 + 500 = 2100,2100> 2000.
我的方法:我只能通过聚合函数得到总和,但未能更新
db.aggregation.aggregate([
{$match: {
$or: [
{status: 'A'},
{status: 'B'}
]
}},
{$group: {
_id: '$cust_id',
total: {$sum: '$score'}
}},
{$match: {
total: {$gt: 2000}
}}
])
Run Code Online (Sandbox Code Playgroud)
请建议我如何进行.
Sam*_*Sam 14
经过很多麻烦,尝试mongo shell我终于找到了解决问题的方法.
Psudocode:
# To get the list of customer whose score is greater than 2000
cust_to_clear=db.col.aggregate(
{$match:{$or:[{status:'A'},{status:'B'}]}},
{$group:{_id:'$cust_id',total:{$sum:'$score'}}},
{$match:{total:{$gt:500}}})
# To loop through the result fetched from above code and update the clear
cust_to_clear.result.forEach
(
function(x)
{
db.col.update({cust_id:x._id},{$set:{clear:'Yes'}},{multi:true});
}
)
Run Code Online (Sandbox Code Playgroud)
如果您对同一问题有任何不同的解决方案,请发表评论.
您需要分两步执行此操作:
cust_id总分大于200的客户()clear为Yes你已经有了第一部分的好解决方案.第二部分应该作为update()对数据库的单独调用来实现.
Psudocode:
# Get list of customers using the aggregation framework
cust_to_clear = db.col.aggregate(
{$match:{$or:[{status:'A'},{status:'B'}]}},
{$group:{_id:'$cust_id', total:{$sum:'$score'}}},
{$match:{total:{$gt:2000}}}
)
# Loop over customers and update "clear" to "yes"
for customer in cust_to_clear:
id = customer[_id]
db.col.update(
{"_id": id},
{"$set": {"clear": "Yes"}}
)
Run Code Online (Sandbox Code Playgroud)
这并不理想,因为您必须为每个客户进行数据库调用.如果您经常需要执行此类操作,则可以修改架构以包含每个文档中的总分.(这必须由您的应用程序维护.)在这种情况下,您可以使用单个命令执行更新:
db.col.update(
{"total_score": {"$gt": 2000}},
{"$set": {"clear": "Yes"}},
{"multi": true}
)
Run Code Online (Sandbox Code Playgroud)
简短回答:为了避免循环数据库查询,只需将$merge添加到末尾并指定您的集合,如下所示:
db.aggregation.aggregate([
{$match: {
$or: [
{status: 'A'},
{status: 'B'}
]
}},
{$group: {
_id: '$cust_id',
total: {$sum: '$score'}
}},
{$match: {
total: {$gt: 2000}
}},
{ $merge: "<collection name here>"}
])
Run Code Online (Sandbox Code Playgroud)
详细说明:当前的解决方案是循环访问数据库查询,这在时间效率方面并不好,而且代码也很多。Mitar 的答案不是通过聚合进行更新,而是相反 => 在 Mongo 的更新中使用聚合。如果您想知道这样做的专业人士是什么,那么您可以使用所有聚合管道,而不是仅限于其文档中指定的少数几个。
以下是一个不适用于 Mongo 更新的聚合示例:
db.getCollection('foo').aggregate([
{ $addFields: {
testField: {
$in: [ "someValueInArray", '$arrayFieldInFoo']
}
}},
{ $merge : "foo" }]
)
Run Code Online (Sandbox Code Playgroud)
这将使用新的测试字段输出更新后的集合,如果“someValueInArray”位于“arrayFieldInFoo”中,则该字段为 true,否则为 false。目前对于 Mongo.update 来说这是不可能的,因为 $in 不能在更新聚合内部使用。
更新:从 $out 更改为 $merge,因为 $out 仅在更新整个集合时才起作用,因为 $out 用聚合结果替换整个集合。$merge 仅当聚合与文档匹配时才会覆盖(更安全)。
使用Mongo 4.2,现在可以使用带有聚合管道的更新来完成此操作。示例2给出了如何进行条件更新的示例:
db.runCommand(
{
update: "students",
updates: [
{
q: { },
u: [
{ $set: { average : { $avg: "$tests" } } },
{ $set: { grade: { $switch: {
branches: [
{ case: { $gte: [ "$average", 90 ] }, then: "A" },
{ case: { $gte: [ "$average", 80 ] }, then: "B" },
{ case: { $gte: [ "$average", 70 ] }, then: "C" },
{ case: { $gte: [ "$average", 60 ] }, then: "D" }
],
default: "F"
} } } }
],
multi: true
}
],
ordered: false,
writeConcern: { w: "majority", wtimeout: 5000 }
}
)
Run Code Online (Sandbox Code Playgroud)
另一个例子:
db.c.update({}, [
{$set:{a:{$cond:{
if: {}, // some condition
then:{} , // val1
else: {} // val2 or "$$REMOVE" to not set the field or "$a" to leave existing value
}}}}
]);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
32102 次 |
| 最近记录: |