【问题标题】:How to use $update/ $set operator in aggregation pipeline mongodb?如何在聚合管道 mongodb 中使用 $update/ $set 运算符?
【发布时间】: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 字段。有两种方法我试过了,都不起作用:

  1. { $set: { "Name" :matchingrecords.Text} } 添加到上述管道。这会将Name 设置为字符串matchingrecords.Text,而不是其中的值。另外,添加$ 也无济于事!

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

有一个简单的技巧!

解决方案(对我有用!):

  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

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

【问题讨论】:

  • 问题不是很清楚。一些注意事项:(1)聚合查询不更新集合数据;只转换数据。 (2) 您可以在 update 查询中使用聚合管道:(a) 从 MongoDB 版本 4.2 开始,以及 (b) 聚合阶段 $addFields(其别名 $ set)、$project (它的别名 $unset) 和 $replaceRoot (它的别名 $replaceWith) 只能使用
  • 你能提供一些你收集的数据样本吗?
  • @matthPen,我提供了一个简单的技巧,你能建议一个查询来完成我的步骤吗?
  • 我认为有一个解决方案是在自我收藏时设置一个 $out 阶段,但必须非常小心地使用它,因为它会取代您的整个收藏。这个想法是对所有文档进行查找,更新那些匹配更新条件的文档,并项目字段以输出与原始格式相同的文档。提示是不匹配更新条件的文档必须在输出中,即使它们没有被修改。但是根据您的收藏(所有文档是否都尊重相同的结构?要更新的文档的百分比等),它可能比您的 hack 效率低(因为“无过滤器”是强制性的)

标签: mongodb mongodb-query aggregation-framework nosql-aggregation


【解决方案1】:

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

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

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

Update 命令不接受聚合管道运算符。根据@prasad_共享的reference,支持从mongo@4.2 开始的仅有的几个运算符

那么如何解决这个问题呢?

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

首先,从聚合开始,然后添加$addFields 阶段,为每个文档添加一个具有您要设置的值的新字段。然后运行另一个更新命令如下

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

【讨论】:

    猜你喜欢
    • 2016-09-11
    • 2018-06-09
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    相关资源
    最近更新 更多