【发布时间】:2020-12-27 05:07:02
【问题描述】:
我正在尝试根据某些标准更新我的coll1 集合中名为name 的字段。我首先创建了一个聚合管道,它根据我的标准过滤掉文档。
var local_filter = { "$or" :[
{'fullText': {'$eq': "404 Error"}},
{'fullText': {'$eq': "Unknown Error"}},
{'fullText': {'$eq': "503 Error"}},
{'fullText': {'$eq': "400 Error"}},
{'fullText': {'$eq': "500 Error"}},
{'fullText': {'$eq': "Read timed out"}},
{'fullText': {'$eq': "410 Error"}},
{'fullText': {'$eq': "403 Error"}},
{"fullText": {'$eq':""}},
]}
var foreign_filter= { "$and" :[
{'matchingrecords.Text': {'$ne': "404 Error"}},
{'matchingrecords.Text': {'$ne': "Unknown Error"}},
{'matchingrecords.Text': {'$ne': "503 Error"}},
{'matchingrecords.Text': {'$ne': "400 Error"}},
{'matchingrecords.Text': {'$ne': "500 Error"}},
{'matchingrecords.Text': {'$ne': "Read timed out"}},
{'matchingrecords.Text': {'$ne': "410 Error"}},
{'matchingrecords.Text': {'$ne': "403 Error"}},
{"matchingrecords.Text": {'$ne': ""}},
{"matchingrecords.Text": {'$ne':'null'}}
]}
db.coll1.aggregate([
{$match:local_filter //9474
},
{$lookup: {
from: "coll2",
localField: "_id", //from coll1
foreignField: "_id", //from coll2
as: "matchingrecords"
}//4518
},
{ $match: foreign_filter
},
{ $match: {matchingrecords: {$ne:[]} }
},//3645
{
$count: "totalCount"
}
])//3645
所以,我现在在 coll1 中获得了 3645 个文档,我需要更新 name 字段。有两种方法我试过了,都不起作用:
将
{ $set: { "Name" :matchingrecords.Text} }添加到上述管道。这会将Name设置为字符串matchingrecords.Text,而不是其中的值。另外,添加$也无济于事!使用aggregation with Update,我在
u子句中传递了我的聚合管道。
db.runCommand(
{
update: "coll1",
updates: [
{
q: { },
u: [// You can pass you aggregation pipeline here
{$match: local_filter//9474
},
{$lookup: {
from: "coll2",
localField: "_id",
foreignField: "_id",
as: "matchingrecords"
}//4518
},
{ $match: foreign_filter
},
{ $match: {matchingrecords: {$ne:[]} }
},//3645
{ $set: { "Name" : 'matchingrecords.Text' } }
],
multi: true
}
],
ordered: false,
writeConcern: { w: "majority", wtimeout: 5000 }
})
它抱怨$match operator isn't allowed in update!
{
"n" : 0.0,
"nModified" : 0.0,
"writeErrors" : [
{
"index" : 0.0,
"code" : 72.0,
"errmsg" : "$match is not allowed to be used within an update"
}
],
"ok" : 1.0
}
关于如何更新我的 3645 文档有什么建议吗?
有一个简单的技巧!
解决方案(对我有用!):
- 使用
coll1,创建一个包含 3645 个文档的新集合。
db.coll1.aggregate([
{$match:filter //9474
},
{$lookup: {
from: "coll2",
localField: "_id",
foreignField: "_id",
as: "matchingrecords"
}//4518
},
{ $match: foreign_filter
},
{ $match: {matchingrecords: {$ne:[]} }
},//3645
{ $unwind: { path: "$matchingrecords", preserveNullAndEmptyArrays: true }
},
{ $project : <what All fields you Need?>
},
{ $out: "child_coll1"
}//get 3645 in the a new collection
- 使用
coll1,在单独的集合中获取不匹配的文档
db.coll1.aggregate([
{$lookup: {
from: "child_coll1",
localField: "_id",
foreignField: "_id",
as: "matchingrecords"
}//
},
{ $match: {matchingrecords: {$eq:[]} }
},//30944
{ $unwind: { path: "$matchingrecords", preserveNullAndEmptyArrays: true }
},
{ $out: "child_coll2"
}//get out 30944 docs other than 3645
])
- 只需合并来自 1 和 2 的新集合
db.child_coll1.find().forEach(function(doc){
db.child_coll2.insert(doc);
});
- 删除除
child_coll2以外的所有集合,可以重命名为coll1
这不是一个优雅的解决方案,只是一个完成任务的技巧!有没有人在一个查询中有更好/优雅的解决方案?
【问题讨论】:
-
问题不是很清楚。一些注意事项:(1)聚合查询不更新集合数据;只转换数据。 (2) 您可以在
update查询中使用聚合管道:(a) 从 MongoDB 版本 4.2 开始,以及 (b) 聚合阶段 $addFields(其别名 $ set)、$project (它的别名 $unset) 和 $replaceRoot (它的别名 $replaceWith) 只能使用。 -
你能提供一些你收集的数据样本吗?
-
@matthPen,我提供了一个简单的技巧,你能建议一个查询来完成我的步骤吗?
-
我认为有一个解决方案是在自我收藏时设置一个 $out 阶段,但必须非常小心地使用它,因为它会取代您的整个收藏。这个想法是对所有文档进行查找,更新那些匹配更新条件的文档,并项目字段以输出与原始格式相同的文档。提示是不匹配更新条件的文档必须在输出中,即使它们没有被修改。但是根据您的收藏(所有文档是否都尊重相同的结构?要更新的文档的百分比等),它可能比您的 hack 效率低(因为“无过滤器”是强制性的)
标签: mongodb mongodb-query aggregation-framework nosql-aggregation