12

相关问题是Efficiently convert rows to columns in sql server。但答案是特定于 SQL 的。

我想要相同的结果,即在 MongoDB 中不聚合任何内容(截至目前)的情况下将行转为列。

该集合看起来如下所示。这些是 Facebook 页面属性的统计数据:

时间戳 | 道具名称 | 道具价值
--------------------------------
1371798000000 | page_fans | 100
--------------------------------
1371798000000 | page_posts | 50
--------------------------------
1371798000000 | 页面故事 | 25
--------------------------------

我需要这样的答案:

时间戳 | page_fans | page_posts | page_stories
--------------------------------
1371798000000 | 100 | 50 | 25
--------------------------------

列名是预先确定的。它们不必动态生成。但问题是如何在 MongoDB 中实现这一点。

我相信聚合对这个目的没有用。我需要使用 MapReduce 吗?但在那种情况下,我猜我没有什么可以减少的?那么另一种选择可能是在代码中获取这些值并使用编程语言(例如Java)进行操作

任何见解都会有所帮助。提前致谢 :)!!!

编辑(基于 Schaliasos 的输入):

输入 JSON:

{
        "_id" : ObjectId("51cd366644aeac654ecf8f75"),
        "name" : "page_storytellers",
        "pageId" : "512f993a44ae78b14a9adb85",
        "timestamp" : NumberLong("1371798000000"),
        "value" : NumberLong(30871),
        "provider" : "Facebook"
}
{
        "_id" : ObjectId("51cd366644aeac654ecf8f76"),
        "name" : "page_fans",
        "pageId" : "512f993a44ae78b14a9adb85",
        "timestamp" : NumberLong("1371798000000"),
        "value" : NumberLong(1291509),
        "provider" : "Facebook"
}
{
        "_id" : ObjectId("51cd366644aeac654ecf8f77"),
        "name" : "page_fan_adds",
        "pageId" : "512f993a44ae78b14a9adb85",
        "timestamp" : NumberLong("1371798000000"),
        "value" : NumberLong(2829),
        "provider" : "Facebook"
}

预期输出 JSON:

{
        "timestamp" : NumberLong("1371798000000"),
        "provider" : "Facebook",
        "page_storytellers" : NumberLong(30871),
        "page_fans" : NumberLong("1371798000000"),
        "page_fan_adds" : NumberLong("1371798000000")
}
4

2 回答 2

11

现在,您可以利用新的聚合运算符 $arrayToObject来旋转 MongoDB 键。此运算符在 MongoDB v3.4.4+ 中可用

例如,给定一个示例数据:

db.foo.insert({ provider: "Facebook", timestamp: '1371798000000', name: 'page_storytellers', value: 20871})
db.foo.insert({ provider: "Facebook", timestamp: '1371798000000', name: 'page_fans', value: 1291509})
db.foo.insert({ provider: "Facebook", timestamp: '1371798000000', name: 'page_fan_adds', value: 2829})
db.foo.insert({ provider: "Google", timestamp: '1371798000000', name: 'page_fan_adds', value: 1000})

您可以使用以下聚合管道

db.foo.aggregate([
  {$group:
     {_id:{provider:"$provider", timestamp:"$timestamp"}, 
      items:{$addToSet:{name:"$name",value:"$value"}}}
  }, 
  {$project:
     {tmp:{$arrayToObject: 
       {$zip:{inputs:["$items.name", "$items.value"]}}}}
  }, 
  {$addFields:
     {"tmp.provider":"$_id.provider", 
      "tmp.timestamp":"$_id.timestamp"}
  }, 
  {$replaceRoot:{newRoot:"$tmp"}
  }
]);

输出将是:

{
  "page_fan_adds": 1000,
  "provider": "Google",
  "timestamp": "1371798000000"
},
{
  "page_fan_adds": 2829,
  "page_fans": 1291509,
  "page_storytellers": 20871,
  "provider": "Facebook",
  "timestamp": "1371798000000"
}

另请参阅$group$project$addFields$zip$replaceRoot

于 2017-04-26T03:19:03.103 回答
10

我使用聚合做了类似的事情。这有帮助吗?

db.foo.insert({ timestamp: '1371798000000', propName: 'page_fans', propValue: 100})
db.foo.insert({ timestamp: '1371798000000', propName: 'page_posts', propValue: 25})
db.foo.insert({ timestamp: '1371798000000', propName: 'page_stories', propValue: 50})

db.foo.aggregate({ $group: { _id: '$timestamp', result: { $push: { 'propName': '$propName', 'propValue': '$propValue' } }}})

{
    "result" : [
        {
            "_id" : "1371798000000",
            "result" : [
                {
                    "propName" : "page_fans",
                    "propValue" : 100
                },
                {
                    "propName" : "page_posts",
                    "propValue" : 50
                },
                {
                    "propName" : "page_stories",
                    "propValue" : 25
                }
            ]
        }
    ],
    "ok" : 1
}

您可能希望在$sum此过程中使用运算符。看这里

于 2013-07-01T09:19:21.373 回答