使用Mongodb中的聚合管道重新格式化集合

时间:2017-01-13 10:55:19

标签: mongodb aggregation-framework

我有一组下一个对象:

{
   "_did": "213412323234",
   "metadata": [
      {
         "id": "Language",
         "value": "EN"
      },
      {
         "id": "City",
         "value": "New York"
      },
      {
         "id": "Gender",
         "value": "Male"
      }
   ]
}

在使用了一些管道运算符(也许是$ project)之后,我期待这样的事情:

{
  "id": "213412323234",
  "metadata": {
    "Language": "EN",
    "City": "New York",
    "Gender": "Male"
  }
}

3 个答案:

答案 0 :(得分:3)

如果已知metadata字段中元素的位置和嵌入式文档的键,则可以使用聚合。

db.foo.aggregate([
  {
    $project : {
      data : {
        "Language" : {
          $arrayElemAt : ["$metadata", 0]
        },
        "City" : {
          $arrayElemAt : ["$metadata", 1]
        },
        "Gender" : {
          $arrayElemAt : ["$metadata", 2]
        }
      }
    }
  },
  {
    $project : {
      metadata : {
        Language : "$data.Language.value",
        City : "$data.City.value",
        Gender : "$data.Gender.value"
      }
    }
  }
])

结果:

{ "_id" : "213412323234", "metadata" : { "Language" : "EN", "City" : "New York", "Gender" : "Male" } }

如果上述两个前提条件中的任何一个未知,您可以使用mapReduce

db.foo.mapReduce(function () {
  var key = this._id;
  var metadata = this.metadata;
  var valueEmit = {};
  for (var i = 0; i < metadata.length; i++) {
    valueEmit[metadata[i].id] = metadata[i].value;
  }
  emit(key, valueEmit)
},
function(key, values) {
  // do nothing as it won't be called 
  // for if value is only one 
  // for a key
},
{
  out : {replace : "foobar"}
})

结果:

> db.foobar.find()
{ "_id" : "213412323234", "value" : { "Language" : "EN", "City" : "New York", "Gender" : "Male" } }

请注意,密钥已更改为value而非元数据。可以使用$project aggregation集合中的foobar轻松修复此问题。

答案 1 :(得分:1)

根据this功能请求及其解决方案,将为此功能添加新功能 - 名为db.foo.aggregate([ {$project: { metadata: { $arrayToObject: { $map: { input: "$metadata", as: "pair", in: ["$$pair.id", "$$pair.value"] } } } }} ]) 的功能。

AddHours

但此刻,没有解决方案。我建议你改变你的数据结构。据我所知,有许多功能请求标记为 Major ,但多年来都没有。

答案 2 :(得分:1)

尝试此查询脚本

var results = db.meta.aggregate([{
  $project: {
    id: "$_did",
    _id: 0,
    keys: "$metadata.id",
    values: "$metadata.value"
  }
}])

function object(keys, values) {
  var obj = {};
  for (var index in keys) {
    obj[keys[index]] = values[index];
  }
  return obj;
}

results = results.map(function (res) {
  return {
    id: res.id,
    metadata: object(res.keys, res.values)
  }
})