【问题标题】:How to set nth value from an array as default value of $zip, if nth element of zipping array is empty in MongoDB?如果 MongoDB 中压缩数组的第 n 个元素为空,如何将数组中的第 n 个值设置为 $zip 的默认值?
【发布时间】:2020-07-11 14:52:38
【问题描述】:

经过一些聚合管道阶段后,我的 MongoDB 文档具有以下格式(示例):

{
  "data": [
    {
      "_id": "group1",
      "start": [
        "a",
        "b",
        "c"
      ],
      "end": [
        "l",
        "m",
        "n",
        "o"
      ],
      "mid": [
        "a1",
        "a2",
        "a3",
        "a4"
      ]
    },
    {
      "_id": "group2",
      "start": [
        "p",
        "q",
        "r",
        "s"
      ],
      "end": [
        "h",
        "i",
        "j"
      ],
      "mid": [
        "b1",
        "b2",
        "b3"
      ]
    }
  ]
}

我需要将我的数据转换成以下格式:

{
  "new_data": [
    {
      "_id": "group1",
      "val": [
        {
          "start": "a",
          "end": "l"
        },
        {
          "start": "b",
          "end": "m"
        },
        {
          "start": "c",
          "end": "n"
        },
        {
          "start": "a4",
          "end": "o"
        }
      ]
    },
    {
      "_id": "group2",
      "val": [
        {
          "start": "p",
          "end": "h"
        },
        {
          "start": "q",
          "end": "i"
        },
        {
          "start": "r",
          "end": "j"
        },
        {
          "start": "s",
          "end": "default value"
        }
      ]
    }
  ]
}

问题: 特殊情况如何处理?

在转置 startend 数组时,

  1. 如果start的第n个值为NULL,则取mid数组中的第n个值作为start
  2. 如果end的第n个值为NULL,则使用一些“默认值”作为end值(这部分有解决方案)

mid 的长度 = end 的长度

这是我尝试过的:

db.collection.aggregate([
{
..... some pipeline stages
},
        {
          $unwind:
            {path:"$data"}
        },
        {
          "$project": {
            "data.mid":1,
            "data._id":1,
            "val": {
              "$map": {
                "input": {
                  "$objectToArray": {
                    "$arrayToObject": {
                      "$zip": {
                        "inputs": [
                          "$data.start","$data.end"],
                        useLongestLength: true,
                        defaults: [ *I don't know what to write here*, "default value"] // Need help
                      }
                    }
                  }
                },
                "as": "el",
                "in": {
                  "start": "$$el.k",
                  "end": "$$el.v"
                }
              }
            }
          }
        },
        { "$group": {
          "_id": "$data._id",
          "new_data": {
            "$push": { "transformed_data":"$val" }
          }
        }}
        
        ]).pretty()

如何实现这个用例?

【问题讨论】:

  • 我认为$zip 只允许一个默认值,所以如果长度差大于一个,您将使用mid 中的哪个值,例如start: ["a"], end: ["x", "y", "z"], mid: ["j", "k", "l"]。或者如果mid 的长度比end 短,例如mid: ["j"]mid: ["j", "k"]
  • 如果start: ["a"]end: ["x", "y", "z"]mid: ["j", "k", "l"]:那么val:[{ "start": "a","end": "x" },{"start": "k","end": "y" },{"start": "l","end": "z" }]在我的情况下长度为mid=长度为end。这个用例可以使用 mongoDB 管道实现吗?
  • 在聚合的最后阶段,您按$data._id 分组,我认为这会将"group1""group2" 的2 个文档输出为_id,但您只想作为输出一个包含数组newData 的文档。那么在最后阶段你真正想用什么来分组呢?
  • 其实两者都适合我。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

我认为使用$zip 是不可能的,因为$zip 会将缺失值替换为常量值。

相反,我们可以使用较大数组的长度迭代一个范围。它类似于@abid aliother answer 中建议的方法。但是使用聚合。

db.collection.aggregate([
  {
    $unwind: "$data"
  },
  {
    $project: {
      doc_id: "$_id",
      _id: "$data._id",
      val: {
        $map: {
// constructing a range of the larger array of data.start and data.end, we will get [0, 1, ..., n-1] where n is the size of the larger array
          input: { $range: [0, { $max: [{ $size: "$data.start" }, { $size: "$data.end" }] }] },
          in: {
            start: { // if data.start[i] is null, take data.mid[i] where is the index we are iterating, represented by "$$this"
              $ifNull: [
                {
                  $arrayElemAt: ["$data.start", "$$this"]
                },
                {
                  $arrayElemAt: ["$data.mid", "$$this"]
                }
              ]
            },
            end: { // if data.end[i] is null take a defaultt value
              $ifNull: [
                {
                  $arrayElemAt: ["$data.end", "$$this"]
                },
                "default_value"
              ]
            }
          }
        }
      }
    }
  },
  { // this stage onward is for reshaping the data to the expected output
    $group: {
      _id: "$doc_id",
      new_data: { $push: "$$ROOT" }
    }
  },
  {
    $project: { "new_data.doc_id": false }
  }
])

Mongo Playground

【讨论】:

    【解决方案2】:

    这可以用简单的方式回答

    if(start.length>=n){
      if(mid.length<n){ 
     const value = mid[n];//giving nth value from mid array
      }
    }
    else{
    const value = start[n];
    }
    
    
    if(end.length>=n){
    const value = start[0];//default value
    }
    else{
    const value = end[n];
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-05
      • 2020-07-21
      • 2015-09-12
      • 1970-01-01
      • 2013-03-09
      • 2015-12-11
      相关资源
      最近更新 更多