MongoDB:Map Reduce:从另一个创建一个子文档

时间:2014-02-28 23:08:59

标签: javascript mongodb mapreduce mongodb-query aggregation-framework

我有一个mongodb集合,其中包含以下文档:

{
"_id" : ObjectId("safdsd435tdg54trgds"),
"startDate" : ISODate("2013-07-02T17:35:01.000Z"),
"endDate" : ISODate("2013-08-02T17:35:01.000Z"),
"active" : true,
"channels" : [ 
    1, 2, 3, 4
],

}

我想把它转换成这样的东西:

{
"_id" : ObjectId("safdsd435tdg54trgds"),
"startDate" : ISODate("2013-07-02T17:35:01.000Z"),
"endDate" : ISODate("2013-08-02T17:35:01.000Z"),
"active" : true,
"channels" : [ 
    1, 2, 3, 4
],
"tags" :[ 
            {
                "name": one
                "type": channel
            },
            {
                "name": two
                "type": channel
            },
            {
                "name": three
                "type": channel
            },
            {
                "name": four
                "type": channel
            }
        ]           
}

我已经有1,2,3,4表示的映射。为了简单起见,我把它们作为字母格式。值可能不同,但它们是静态映射。

1 个答案:

答案 0 :(得分:2)

你似乎试图在没有大量迭代的情况下进行此更新,所以你“可以”使用mapReduce做到这一点,虽然它采用非常“mapReduce方式”,因为它有自己的处理方式。

首先,您要定义一个封装当前文档的映射器

var mapFunction = function (){

    var key = this._id;

    var value = {
       startDate: this.startDate,
       endDate: this.endDate,
       active: this.active,
       channels: this.channels

    };

    emit( key, value );
};

现在 reducer 实际上不会被调用,因为 mapper 中的所有键都是唯一的,当然是来自_id的{​​{1}}值原始文档。但是为了让电话快乐:

var reduceFunction = function(){};

由于这是一对一,因此最终确定。它可以在映射器中,但为了清洁起见

var finalizeFunction = function (key, reducedValue) {

    var tags = [
        { name: "one", type: "channel" },
        { name: "two", type: "channel" },
        { name: "three", type: "channel" },
        { name: "four", type: "channel" }
    ];

    reducedValue.tags = [];

    reducedValue.channels.forEach(function(channel) {
        reducedValue.tags.push( tags[ channel -1 ] );
    });

    return reducedValue;

};

然后调用mapReduce:

 db.docs.mapReduce( 
     mapFunction,
     reduceFunction,
    { 
        out: { replace: "newdocs" },
        finalize: finalizeFunction 
    }
 )

这样会输出到新集合,但是就像mapReduce那样,你就有了这个:

{
    "_id" : ObjectId("53112b2d0ceb66905ae41259"),
    "value" : {
            "startDate" : ISODate("2013-07-02T17:35:01Z"),
            "endDate" : ISODate("2013-08-02T17:35:01Z"),
            "active" : true,
            "channels" : [ 1, 2, 3, 4 ],
            "tags" : [
                    {
                        "name" : "one",
                        "type" : "channel"
                    },
                    {
                        "name" : "two",
                        "type" : "channel"
                    },
                    {
                        "name" : "three",
                        "type" : "channel"
                    },
                    {
                        "name" : "four",
                        "type" : "channel"
                    }
            ]
    }
}

因此,_id以外的所有文档字段都位于value字段下,因此这不是您想要的文档。但这是如何 mapReduce的工作原理。

如果你真的需要从退出监狱并愿意稍等一下,那么即将发布的2.6版本已经添加了一个$out管道阶段。因此,您“可以”使用$project转换新集合中的文档,如下所示:

db.newdocs.aggregate([

    // Transform the document
    {"$project": { 
        "startDate": "$value.startDate",
        "endDate":   "$value.endDate",
        "active":    "$value.active",
        "channels":  "$value.channels",
        "tags":      "$value.tags"
    }},

    // Output to new collection
    {"$out": "fixeddocs" }

])

这样才对。但当然这不是你的原创系列。因此,要返回该状态,您将需要.drop()个集合并使用.renameCollection()

db.newdocs.drop();

db.docs.drop();

db.fixeddocs.renameCollection("docs");  

现在请仔细阅读阅读文档,有几个限制,当然你也必须重新创建索引。

所有这些,特别是最后一个阶段将导致磁盘抖动的很多,并且还要记住您在此处删除集合。几乎可以肯定的是,在执行此操作时离线访问您的数据库。

即便如此,危险在这里也足够真实,也许您可​​以使用任意JavaScript来运行迭代循环来更新文档。如果你真的必须这样做,你总是可以使用db.eval()来完成所有操作。但如果,那么请仔细阅读文档

但是为了完整性,即使我不提倡这个:

db.eval(function(){

    db.docs.find().forEach(function(document) {

        var tags = [
            { name: "one", type: "channel" },
            { name: "two", type: "channel" },
            { name: "three", type: "channel" },
            { name: "four", type: "channel" }
        ];

        document.tags = [];

        document.channels.forEach(function(channel) {
             document.tags.push( tags[ channel -1 ] );
        });

        var id = document._id;
        delete document._id;           

        db.docs.update({ "_id": id },document);

    });

})