【问题标题】:MongoDB : Map Reduce : Create one sub-document from another oneMongoDB:Map Reduce:从另一个子文档创建一个子文档
【发布时间】:2014-02-28 23:08:58
【问题描述】:

我有一个 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 的含义进行了映射。只是为了简单起见,我将它们作为字母格式。值可能不同,但它们是静态映射。

【问题讨论】:

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


    【解决方案1】:

    您似乎正在尝试在不对您的集合进行大量迭代的情况下进行此更新,因此您“可以”使用 mapReduce 进行此操作,尽管采用非常“mapReduce 方式”,因为它有自己的做事方式。

    所以首先你要定义一个 ma​​pper 来封装你当前的文档:

    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,因为 ma​​pper 中的所有键都是唯一的,当然是 reducer 中的 _id 值em>原件文件。但是为了让通话愉快:

    var reduceFunction = function(){};
    

    因为这是一个一对一的事情,这将进入finalize。它可能在 ma​​pper 中,但为了清洁起见

    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);
    
        });
    
    })
    

    【讨论】:

    • 所有文档不一定都有channel的vvalue,那么如何进行null检查并且当它是null值时不进行迭代。
    • @PiHorse 也许最简单的做法是使用查询选择完全跳过它们:{"channel": {"$exists": true}}。根据您选择的方法,您可能还需要反向条件才能合并结果。
    猜你喜欢
    • 2017-06-13
    • 2015-10-31
    • 1970-01-01
    • 2012-10-05
    • 2016-12-14
    • 2015-08-19
    • 1970-01-01
    • 2016-06-28
    • 2018-07-29
    相关资源
    最近更新 更多