【问题标题】:Reshape documents by splitting a field value通过拆分字段值来重塑文档
【发布时间】:2016-08-30 09:34:36
【问题描述】:

假设我们有一组原始数据:

{ "person": "David, age 102"}
{ "person": "Max, age 8" }

我们想将该集合转换为:

{ "age": 102 }
{ "age": 8 }

仅使用 mongo(d) 引擎。 (如果所有人名或年龄的长度相等, $substr 可以完成这项工作,)有可能吗?

假设正则表达式是微不足道的 /\d+/

【问题讨论】:

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


    【解决方案1】:

    MongoDB 3.4 版本中的最佳方式。

    这个版本的mongod 提供了$split 运算符,它当然可以拆分字符串,如here 所示。

    然后,我们使用$let 变量运算符将新计算的值分配给一个变量。然后可以在 in 表达式中使用新值返回“name”和“age”值,使用 $arrayElemAt 运算符返回指定索引处的元素; 0 用于第一个元素,-1 用于最后一个元素。

    请注意,在 in 表达式中,我们需要拆分最后一个元素才能返回整数字符串。

    最后我们需要迭代Cursor对象并使用NumberparseInt将整数字符串转换为数字并使用批量操作和bulkWrite()方法将$set这些字段的值以实现最高效率。

    let requests = [];
    db.coll.aggregate(
        [
            { "$project": {  
                "person": { 
                    "$let": { 
                        "vars": { 
                            "infos":  { "$split": [ "$person", "," ] } 
                        }, 
                        "in": { 
                            "name": { "$arrayElemAt": [ "$$infos", 0 ] }, 
                            "age": { 
                                "$arrayElemAt": [ 
                                    { "$split": [ 
                                        { "$arrayElemAt": [ "$$infos", -1 ] }, 
                                        " " 
                                    ]}, 
                                    -1 
                                ] 
                            } 
                        } 
                    } 
                }  
            }}
        ] 
    ).forEach(document => { 
        requests.push({ 
            "updateOne": { 
                "filter": { "_id": document._id }, 
                "update": { 
                    "$set": { 
                        "name": document.person.name, 
                        "age": Number(document.person.age) 
                    },
                    "$unset": { "person": " " }
                } 
            } 
        }); 
        if ( requests.length === 500 ) { 
            // Execute per 500 ops and re-init
            db.coll.bulkWrite(requests); 
            requests = []; 
        }} 
    );
    
     // Clean up queues
    if(requests.length > 0) {
        db.coll.bulkWrite(requests);
    }
    

    MongoDB 3.2 或更新版本。

    MongoDB 3.2 弃用了旧的 Bulk() API 及其关联的 methods 并提供了 bulkWrite() 方法,但它不提供 $split 运算符,因此我们这里唯一的选择是使用 mapReduce()方法来转换我们的数据,然后使用批量操作更新集合。

    var mapFunction = function() { 
        var person = {}, 
        infos = this.person.split(/[,\s]+/); 
        person["name"] = infos[0]; 
        person["age"] = infos[2]; 
        emit(this._id, person); 
    };
    
    var results = db.coll.mapReduce(
        mapFunction, 
        function(key, val) {}, 
        { "out": { "inline": 1 } }
    )["results"];
    
    results.forEach(document => { 
        requests.push({ 
            "updateOne": { 
                "filter": { "_id": document._id }, 
                "update": { 
                    "$set": { 
                        "name": document.value.name, 
                        "age": Number(document.value.age) 
                    }, 
                    "$unset": { "person": " " }
                } 
            } 
        }); 
        if ( requests.length === 500 ) { 
            // Execute per 500 operations and re-init
            db.coll.bulkWrite(requests); 
            requests = []; 
        }} 
    );
    
    // Clean up queues
    if(requests.length > 0) {
        db.coll.bulkWrite(requests);
    }
    

    MongoDB 版本 2.6 或 3.0。

    我们需要使用现已弃用的Bulk API

    var bulkOp = db.coll.initializeUnorderedBulkOp();
    var count = 0;
    
    results.forEach(function(document) { 
        bulkOp.find({ "_id": document._id}).updateOne(
            { 
                "$set": { 
                    "name": document.value.name, 
                    "age": Number(document.value.age)
                },
                "$unset": { "person": " " }
            }
        );
        count++;
        if (count === 500 ) {
            // Execute per 500 operations and re-init
            bulkOp.execute();
            bulkOp = db.coll.initializeUnorderedBulkOp();
        }
    });
    
    // clean up queues
    if (count > 0 ) {
        bulkOp.execute();
    }
    

    【讨论】:

      猜你喜欢
      • 2017-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-23
      • 2013-08-28
      • 2020-02-04
      相关资源
      最近更新 更多