MongoDB mapReduce()查询将数据聚合到记录内的列表中

时间:2015-11-05 21:01:39

标签: mongodb mongodb-query aggregation-framework

我有一个包含以下形式记录的MongoDB集合:

{
    "_id" : ObjectId("562d6d9c3a2e9c0adbb02f14"),
    "slug" : "1:955553",
    "subslug" : "1:955553:02",
    "score" : "0.615",
    "position_start" : "1",
    "position_end" : 955553,
    "name" : "AGRN",
    "ref" : "A"
},
{
    "_id" : ObjectId("562d6d9c3a2e9c0adbb02f15"),
    "slug" : "2:15553",
    "subslug" : "2:15553:01",
    "score" : "0.915",
    "position_start" : "1002",
    "position_end" : 15553,
    "name" : "MMFR",
    "ref" : "C"
}
{
    "_id" : ObjectId("562d6d9c3a2e9c0adbb02f16"),
    "slug" : "1:955553",
    "subslug" : "1:955553:01",
    "score" : "0.715",
    "position_start" : 1,
    "position_end" : 955553,
    "name" : "AGRN",
    "ref" : "A"
},

我想通过slug汇总这个集合和组(注意这里的第一个和第三个记录有相同的slug)。

我正在尝试将我的数据聚合到一个新的集合中,如下所示:

{
    "_id" : "<?>",
    "slug" : "1:955553",
    "components" : [ 
       { 
         "subslug": "1:955553:01",
         "score": 0.615,
         "position_start": 1,
         "position_end": 955553,
         "name": AGRN,
         "ref": "A"
       },
       { 
         "subslug": "1:955553:02",
         "score": 0.715,
         "position_start": 1,
         "position_end": 955553,
         "name": AGRN,
         "ref": "A"
       },

    ]
},
{
    "_id" : "<?>",
    "slug" : "2:15553",
    "components" : [ 
       { 
         "subslug": "2:15553:01",
         "score": 0.915,
         "position_start": 1002,
         "position_end": 15553,
         "name": MMFR,
         "ref": "C"
       }
    ]
}

我正在尝试使用 mapReduce()来完成此任务,但我在解决这些功能的细节方面遇到了一些困难。

我有以下查询:

db.getCollection('vest').mapReduce(
    function() {
        emit(this.slug, { 'components': {$push: this } });
    },
    function(key, components) {
        return components[0];
    },
    {
        out: 'mytable'
    }
)

然而,不幸的是,这构建了一个看起来像这样的表:

{
    "_id" : "1:955553",
    "value" : {
        "components" : {
            "$push" : {
                "_id" : ObjectId("562d6d9c3a2e9c0adbb02f14"),
                "slug" : "1:955553",
                "subslug" : "1:955553:01",
                "position_start" : 1,
                "position_end" : 955553,
                "gene" : "AGRN",
                "ref" : "A"
            }
        }
    }
}

这不是我需要的。我尝试使用$push附加components数组,但$pushmapReduce()显然未得到尊重。

任何人都可以给我任何关于如何获取上面的输入集合数据并创建所需输出集合的指针吗?我的mapReduce()查询是否在正确的轨道上?

2 个答案:

答案 0 :(得分:2)

最好使用 aggregation framework 进行此类操作,该操作应比map-reduce操作快几倍。

通常,您将构建一个包含3个阶段的聚合管道:

  • $group 阶段 - 此管道步骤按while字段对文档进行分组,然后应用累加器运算符 $push 创建slug数组,该数组是将表达式应用于上述组中每个文档的结果。
  • $project 阶段 - 这将重新整形流中的每个文档,例如添加新字段或删除现有字段。
  • $out 阶段 - 此最后一步将汇总管道的结果文档写入新集合。

因此,使用上述操作,运行以下聚合管道将在名为components的新集合中为您提供所需的结果:

mytable

使用上面的示例数据查询此集合

db.vest.aggregate([
    {
        "$group": {
            "_id": "$slug",
            "components": {
                "$push": {
                    "subslug": "$subslug",
                     "score": "$score",
                     "position_start": "$position_start",
                     "position_end": "$position_end",
                     "name": "$name",
                     "ref": "$ref"
                }
            }
        }
    },
    {
        "$project": {
            "_id": 0, "slug": "$_id", "components": 1
        }
    },
    { "$out": "mytable" }
])

将为您提供所需的输出:

示例输出

db.mytable.find()

答案 1 :(得分:1)

你真的不应该使用mapReduce。您应该使用.aggregate()方法来访问aggregation pipeline。您所需要的只是$group您的文件&#34; slug&#34;并使用$push累加器运算符返回所有其他字段的数组。 $project阶段用于排除`_id&#39;聚合结果中的字段。

话虽如此,你可以使用$out运算符将汇总管道的结果文档发送到@ chridam的答案中提到的另一个集合但是因为

  

您无法将分片集合指定为输出集合。可以对管道的输入集合进行分片。

     

$ out运算符无法将结果写入capped collection.

您应该使用"Bulk"操作将结果写入新的集合。

var bulk = db.newcollection.initializeUnorderedBulkOp();
db.collection.aggregate([
    { "$group": { 
        "_id": "$slug", 
        "components": {
            "$push": {
                "subslug": "$subslug",
                "score": "$score", 
                "position_start": "$position_start", 
                "position_end": "$position_end",
                "name": "$name", 
                "ref": "$ref"
            }
        }
    }},
    { "$project": { 
        "slug": "$_id",
        "components": 1, 
        "_id": 0
    }}
]).forEach(function(doc) {
        bulk.insert(doc);
})

bulk.execute();

然后db.newcollection.find()产生类似这样的东西:

{
        "_id" : ObjectId("563bc8a6bf93306f8f6638ce"),
        "components" : [
                {
                        "slug" : "1:955553",
                        "subslug" : "1:955553:02",
                        "score" : "0.615",
                        "position_start" : "1",
                        "position_end" : 955553,
                        "name" : "AGRN",
                        "ref" : "A"
                },
                {
                        "slug" : "1:955553",
                        "subslug" : "1:955553:01",
                        "score" : "0.715",
                        "position_start" : 1,
                        "position_end" : 955553,
                        "name" : "AGRN",
                        "ref" : "A"
                }
        ],
        "slug" : "1:955553"
}