如何在aggregation / make $ map中添加属性到子文档数组,如$ addFields?

时间:2017-07-09 18:54:47

标签: mongodb aggregation-framework

> db.things.find()
{ "_id" : 1, "a" : [ ] }
{ "_id" : 2, "a" : [ { "x" : 2, "y" : 1 }, { "x" : 0, "y" : 1 } ] }
{ "_id" : 3, "a" : [ { "x" : 2, "y" : 5, "abitraryPropertyWhichShouldBeKept" : true } ] }

我想在magnitude的每个子文档中添加一个计算属性,比如a。我不想手动指定xy,并且可能还有其他属性需要保留。到目前为止我的尝试(我必须手动指定xy,我不应该这样做):

db.things.aggregate([
  {
    $addFields: {
     a: {
       $map: {
         input: '$a',
         as: 'item',
         'in': {
           x: '$$item.x',
           y: '$$item.y',
           magnitude: {
             $sqrt: {
               $add: [
                 { $pow: ['$$item.x', 2], },
                 { $pow: ['$$item.y', 2] }],
               },
             },
           },
         },
       },
     },
  },
])

结果会丢掉我想要保留的额外密钥:

{ "_id" : 1, "a" : [ ] }
{ "_id" : 2, "a" : [ {
  "x" : 2, "y" : 1, "magnitude" : 2.23606797749979 }, {
  "x" : 0, "y" : 1, "magnitude" : 1 } ] }
{ "_id" : 3, "a" : [ { "x" : 2, "y" : 5, "magnitude" : 5.385164807134504 } ] }

我能想到的另一种方法是将$unwind$addFields$group联系起来。但是为了防止$unwind删除文档1,我必须在每个a数组的末尾添加一个虚拟条目,然后在将它们再次组合在一起时将其丢弃。

是否有类似$map的内容,但其行为类似于$addFields,以便我可以在嵌套文档中添加属性?我是否真的必须指定子文档的所有可能键才能使用$map?如果我不提前知道嵌套子文档中的键,那么我唯一的选择是$unwind是否具有使用它所需的所有额外功能和环境?

当前解决方法

这是我的$unwind ... $group尝试。但是,正如你所看到的,我需要大量的阶段,如果我可以得到$addFields - 就像$map中的行为一样,或者某些表达式运算符会将对象合并在一起,就像JavaScript一样Object.assign()

db.things.aggregate([
  { // Add dummy to preserve documents during imminent $unwind
    $addFields: {
      a: {
        $concatArrays: [ '$a', [ null, ], ],
      },
    },
  },
  { $unwind: '$a', },
  { // Do actual calculation on single array element
    $addFields: {
      'a.magnitude': {
        $sqrt: {
          $add: [
            { $pow: ['$a.x', 2] },
            { $pow: ['$a.y', 2] },
          ],
        },
      },
    },
  },
  {
    $group: {
      _id: '$_id',
      a: { $push: '$a', },
      item: { $first: '$$ROOT', },
    },
  },
  { // Remove dummy element
    $addFields: {
      'item.a': {
        $slice: [ '$a', { $add: [ { $size: '$a', }, -1] } ],
      },
    },
  },
  { $replaceRoot: { newRoot: '$item', }, },
])

结果可以正确保留原始数据中任何未预料到的密钥:

{ "_id" : 3, "a" : [ {
  "x" : 2, "y" : 5, "abitraryPropertyWhichShouldBeKept" : true,
  "magnitude" : 5.385164807134504 } ] }
{ "_id" : 2, "a" : [ {
  "x" : 2, "y" : 1, "magnitude" : 2.23606797749979 }, {
  "x" : 0, "y" : 1, "magnitude" : 1 } ] }
{ "_id" : 1, "a" : [ ] }

1 个答案:

答案 0 :(得分:3)

您可以在3.5.6开发版本中使用mergeObjects聚合运算符,该版本将在即将发布的3.6版本中使用。

db.things.aggregate([
  {
    "$addFields": {
      "a": {
        "$map": {
          "input": "$a",
          "as": "item",
          "in": {
            "$mergeObjects": [
              "$$item",
              {
                "magnitude": {
                  "$sqrt": {
                    "$add": [
                      {
                        "$pow": [
                          "$$item.x",
                          2
                        ]
                      },
                      {
                        "$pow": [
                          "$$item.y",
                          2
                        ]
                      }
                    ]
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
])

使用3.4.4&更高的产量释放。

$map中使用$arrayToObject$objectToArray来保留现有键值,$concatArrays合并计算出的键值数组。

db.things.aggregate([
  {
    "$addFields": {
      "a": {
        "$map": {
          "input": "$a",
          "as": "item",
          "in": {
            "$arrayToObject": {
              "$concatArrays": [
                {
                  "$objectToArray": "$$item"
                },
                [
                  {
                    "k": "magnitude",
                    "v": {
                      "$sqrt": {
                        "$add": [
                          {
                            "$pow": [
                              "$$item.x",
                              2
                            ]
                          },
                          {
                            "$pow": [
                              "$$item.y",
                              2
                            ]
                          }
                        ]
                      }
                    }
                  }
                ]
              ]
            }
          }
        }
      }
    }
  }
])