如何将两个数组连接成一个对象数组

Jam*_*ang 3 mongodb aggregation-framework

在 mongodb 3.4.5 中aggregate,有以下文档:

{id:1, a:['x','y','z'], b:[2,3,4]}
Run Code Online (Sandbox Code Playgroud)

并想将其更改为

{id:1, field: [{a:'x', b:2}, {a:'y', b:3}, {a:'z', b:4}]}
Run Code Online (Sandbox Code Playgroud)

aggregate阶段性怎么做?

我试图使用mongodb 3.4.5 的$arrayToObject功能,但不幸的是......

Nei*_*unn 9

您实际上想要$zip$arrayElemAt$map此处:

db.collection.aggregate([
  { "$project": {
     "field": { 
       "$map": {
         "input": { "$zip": { "inputs": [ "$a", "$b" ] } },
         "as": "el",
         "in": { 
           "a": { "$arrayElemAt": [ "$$el", 0 ] }, 
           "b": { "$arrayElemAt": [ "$$el", 1 ] }
         }
       }
     }
  }}
])  
Run Code Online (Sandbox Code Playgroud)

其中产生:

{
        "field" : [
                {
                        "a" : "x",
                        "b" : 2
                },
                {
                        "a" : "y",
                        "b" : 3
                },
                {
                        "a" : "z",
                        "b" : 4
                }
        ]
}
Run Code Online (Sandbox Code Playgroud)

所述$zip确实的“成对”,并且$map处理每一对使用$arrayElemAt取各指数为新密钥。

作为绝对索引的替代,您可以同时使用$arrayToObject$objectToArray

db.collection.aggregate([
  { "$project": {
     "field": {
       "$map": {
         "input": { "$objectToArray": { 
           "$arrayToObject": {
             "$zip": { "inputs": [ "$a", "$b" ] }
           }
         }},
         "as": "el",
         "in": { "a": "$$el.k", "b": "$$el.v" }
       }
     }
  }}
])  
Run Code Online (Sandbox Code Playgroud)

它做同样的事情,但它有点多余,因为$zip无论如何都是成对工作的,所以我们已经知道结果是“成对”的01索引。


详细步骤

首先,您要$zip创建对:

{ "$project": {
  "_id": 0,
  "field": { "$zip": { "inputs": [ "$a", "$b" ] } }
}}
Run Code Online (Sandbox Code Playgroud)

从每个数组中生成“成对”:

{ "field" : [ [ "x", 2 ], [ "y", 3 ], [ "z", 4 ] ] }
Run Code Online (Sandbox Code Playgroud)

$map从这里应该是不言而喻的,而不是使我们展示的$arrayToObject步骤:

  { "$project": {
    "field": { "$arrayToObject": "$field" }
  }}
Run Code Online (Sandbox Code Playgroud)

这使得这些数组对变成“键”和“值”:

{ "field" : { "x" : 2, "y" : 3, "z" : 4 } }
Run Code Online (Sandbox Code Playgroud)

然后是转换$objectToArray

  { "$project": {
    "field": { "$objectToArray": "$field" }
  }}
Run Code Online (Sandbox Code Playgroud)

这使得一个带有键"k"和的对象数组"v"

{ 
  "field" : [
    { "k" : "x", "v" : 2 },
    { "k" : "y", "v" : 3 },
    { "k" : "z", "v" : 4 } 
  ]
}
Run Code Online (Sandbox Code Playgroud)

然后传递给$map重命名“键”:

  { "$project": {
    "field": {
      "$map": {
        "input": "$field",
         "as": "el",
         "in": { "a": "$$el.k", "b": "$$el.v" }
      }
    }
  }}
Run Code Online (Sandbox Code Playgroud)

并给出最终输出:

{ 
  "field" : [ 
    { "a" : "x", "b" : 2 },
    { "a" : "y", "b" : 3 },
    { "a" : "z", "b" : 4 }
  ]
}
Run Code Online (Sandbox Code Playgroud)

作为单独的管道阶段(您不应该这样做),但对于整个示例:

db.collection.aggregate([
  { "$project": {
    "_id": 0,
    "field": { "$zip": { "inputs": [ "$a", "$b" ] } }
  }},
  { "$project": {
    "field": { "$arrayToObject": "$field" }
  }},
  { "$project": {
    "field": { "$objectToArray": "$field" }
  }},
  { "$project": {
    "field": {
      "$map": {
        "input": "$field",
         "as": "el",
         "in": { "a": "$$el.k", "b": "$$el.v" }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

但我没有 MongoDB 3.4 或更高版本

然后,如果您实际上并未将数据用于持续的聚合操作,那么使用几乎任何语言来执行此操作都非常简单。

例如,在 MongoDB shell 中使用 JavaScript 迭代游标:

db.collection.find().map(doc => {
  doc.field = doc.a.map((e,i) => [e, doc.b[i]]).map(e => ({ a: e[0], b: e[1] }));
  delete doc.a;
  delete doc.b;
  return doc;
})
Run Code Online (Sandbox Code Playgroud)

做完全相同的事情并且与初始聚合函数示例中执行的操作相同。

现代 shell 版本或其他 JavaScript 引擎(您甚至可以与较旧的 MongoDB 版本一起使用)可以更清晰地做到这一点:

db.collection.find().map(({ a, b }) => 
  ({ field: a.map((e,i) => [e, b[i]]).map(([a,b]) => ({ a, b })) })
)
Run Code Online (Sandbox Code Playgroud)

或者坦率地说只是..

db.collection.find().map(({ a, b }) =>
  ({ field: a.map((a,i) => ({ a, b: b[i] })) })
)
Run Code Online (Sandbox Code Playgroud)

由于实际上没有必要复制您需要对聚合框架执行的所有步骤,因为您可以直接通过匹配索引转置数组元素。