将_ids转换为新对象的键

时间:2016-03-31 02:05:37

标签: mongodb mongodb-query aggregation-framework

我有很多文件:

{
  _id: '1abc',
  colors: [
    { value: 'red', count: 2 },
    { value: 'blue', count: 3}
  ]
},    
{
  _id: '2abc',
  colors: [
    { value: 'red', count: 7 },
    { value: 'blue', count: 34},
    { value: 'yellow', count: 12}
  ]
}

是否可以使用aggregate()来获取以下内容?

{
  _id: 'null',
  colors: {
    "1abc": [
      { value: 'red', count: 2 },
      { value: 'blue', count: 3}
    ],
    "2abc": [
      { value: 'red', count: 7 },
      { value: 'blue', count: 34},
      { value: 'yellow', count: 12}
    ]
  }
}

基本上,是否可以转换所有原始文件' _id成为单个新聚合文档中新对象的键?

到目前为止,在尝试使用$group时,我无法使用变量值,例如$_id,位于作业的左侧。我错过了什么或者根本不可能吗?

我可以使用Javascript轻松完成此操作但速度难以忍受。因此,为什么我要查看是否可以使用mongo原生aggregate(),这可能会更快。

如果不可能......我会感谢任何可以指向充分替代方案(改变结构等等)的建议。谢谢!

1 个答案:

答案 0 :(得分:1)

就像在评论中所说的那样,虽然你可以用聚合框架甚至mapReduce来制作"服务器"重塑这种反应,这样做很愚蠢。

让我们考虑一下案例:

聚合

db.collection.aggregate([
  { "$match": { "_id": { "$in": ["1abc","2abc"] } } },
  { "$group": {
    "_id": null,
    "result": { "$push": "$$ROOT" }
  }},
  { "$project": {
    "colors": {
      "1abc": {
        "$arrayElemAt": [
          { "$map": {
            "input": {
              "$filter": {
                "input": "$result",
                "as": "r",
                "cond": { "$eq": [ "$$r._id", "1abc" ] },
              }
            },
            "as": "r",
            "in": "$$r.colors"
          }},
          0
        ]
      },
      "2abc": {
        "$arrayElemAt": [
          { "$map": {
            "input": {
              "$filter": {
                "input": "$result",
                "as": "r",
                "cond": { "$eq": [ "$$r._id", "2abc" ] },
              }
            },
            "as": "r",
            "in": "$$r.colors"
          }},
          0
        ]
      }
    }
  }}
])

因此聚合框架纯粹不会动态生成"密钥"一份文件。如果你想以这种方式处理,那么你需要知道所有的"值"您将用于在结果中创建密钥。

使用$group将所有内容放入一个文档后,您可以使用结果数组来访问"键"的数据。这里的基本操作符是:

  • $filter获取"值"的数组的匹配元素你想要的。

  • $map仅返回已过滤数组中的特定属性

  • $arrayElemAt只抓取从生成的映射数组中滤出的单个元素

所以在很多情况下它实际上并不实用,并且声明的编码也相当复杂。

的MapReduce

db.collection.mapReduce(
  function() {
    var obj = { "colors": {} };
    obj.colors[this._id] = this.colors;
    emit(null,obj);
  },
  function(key,values) {
    var obj = { "colors": {} };

    values.forEach(function(value) {
      Object.keys(value.colors).forEach(function(key) {
        obj.colors[key] = value.colors[key];
      });
    })

    return obj;
  },
  { "out": { "inline": 1 } }
)

因为它实际上是用"语言编写的。那么你就有能力循环结构和#34;构建东西"以更有活力的方式。

然而,仔细检查应该告诉你"减速机"这里的功能除了作为"所有结果"的处理器之外没有做任何事情。已经被塞进了#34;但每个发出的文件。

这意味着"迭代值"输入到reducer与#34;迭代光标"实际上并没有什么不同,这导致了下一个结论。

光标迭代

var result  = { "colors": {} };

db.collection.find().forEach(function(doc) {
  result.colors[doc._id] = doc.colors;
})

printjson(result)

这个的简单性应该说明真实。它完全正是在做你想要的东西" shoehorn"进入一个服务器操作,仅此而已,并简单地#34;卷起袖子"并继续完成手头的任务。

这里的关键点是这个过程都不​​需要任何"聚合"实际上,通过简单地迭代光标并构建响应文档就无法实现这一点。

这就是为什么你总是需要看看你在做什么并选择正确的方法。 "服务器端"聚合的主要任务是减少"结果,因此您不需要迭代游标。但这里什么都没有减少"任何东西。它只是所有数据,转换为不同的格式。

因此,这种"转换的简单方法"是迭代光标并构建转换后的版本"所有结果"反正。