如何在mongodb聚合管道中使用$update/$set运算符



我正试图根据某些条件更新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字段。我尝试过两种方法,但都不起作用:

  1. 添加{ $set: { "Name" :匹配记录。将文本} }发送到管道上方。这将用字符串matchingrecords.Text而不是其中的值来设置Name。此外,添加$也没有帮助!

  2. 使用带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份文档,有什么建议吗?

得到一个简单的破解

解决方案(对我有效!(:

  1. 使用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
  1. 使用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. 只需合并1&2
db.child_coll1.find().forEach(function(doc){
db.child_coll2.insert(doc); 
});
  1. 删除除child_coll2之外的所有集合,可以将其重命名为coll1

这不是一个优雅的解决方案,只是一个完成任务的技巧!有人在一个查询中有更好/优雅的解决方案吗

为什么您的第一个聚合解决方案不起作用

$set不是聚合管道的有效阶段运算符。

为什么使用update命令的第二个解决方案不起作用

Update命令不接受聚合管道运算符。根据@prasad_共享的参考,仅支持两个操作员启动mongo@4.2

那么如何解决这个问题呢

检查您是否可以将$replaceRoot用于您的用例。如果没有,你可以使用下面的破解。

首先,像第一次那样从聚合开始,添加$addFields阶段,为每个文档添加一个新字段,其中包含要设置的值。然后按照运行另一个更新命令

db.coll1.aggregate([
{ 
// your match queries 
},
{
$addFields: { myNewName: "myvalue" } 
}
]).toArray().forEach(function(myDoc){
db.coll1.update({ _id: myDoc.id }, { $set: {Name: myDoc.myNewName } })
})

最新更新