【问题标题】:How can I transform an array in MongoDB to a single scalar value?如何将 MongoDB 中的数组转换为单个标量值?
【发布时间】:2015-12-09 15:36:39
【问题描述】:

我有一个 MongoDB 文档,看起来有一些数组属性:

{
    "_id" : "123456789",
    "distance" : [ 
        {
            "inner_distance" : 2
        },
        {
            "inner_distance" : 4
        },
        {
            "inner_distance" : -1
        }
    ],
    "name" : [ 
        {
            "inner_name" : "MyName"
        }
    ],
    "entries" : [ 
        { ... }, 
        { ... }, 
    ],
    "property1" : "myproperty1",
    "property2" : "myproperty2",
    "property3" : "myproperty3"
}

我正在试图弄清楚如何对distance 数组应用转换,以便根据转换函数将其“展平”为标量(我想获得 ` 中每个 inner_distance 元素的绝对值距离,然后取所有这些值中的最小值。)

例如在上面的示例中,distance 数组有:[{"inner_distance" : 2}, {"inner_distance" : 4}, {"inner_distance" : -1}],我需要弄清楚如何应用转换来生成 distance: 1(或者如果更简单,可以使用新属性,例如 @987654327 @。

我想内联执行此操作(这是正确的术语吗?),以便我执行操作并以存储的记录结束:

{
    "_id" : "123456789",
    "distance" : 1,
    "name" : [ 
        {
            "inner_name" : "MyName"
        }
    ],
    "entries" : [ 
        { ... }, 
        { ... }, 
    ],
    "property1" : "myproperty1",
    "property2" : "myproperty2",
    "property3" : "myproperty3"
}

有没有人有过类似的经验?我一直在试图弄清楚如何创建一个map-reduce 命令来运行它,但没有运气。

【问题讨论】:

  • 所以如果我是正确的,你的查询应该返回1 对吧?
  • 为了清楚起见,我在问题中添加了更多内容。我正在寻找这个“内联”,意思是更新记录以将distance 值“展平”为1。有意义吗?
  • 您要更新/修改集合中的文档还是只运行查询以获得此结果?
  • 或者...如果可以将查询输出到新集合,那也可以。

标签: mongodb mapreduce mongodb-query aggregation-framework


【解决方案1】:

嗯,你想要的可以在 MongoDB 3.2 中高效处理。

您需要使用$abs 运算符返回每个“inner_distance”的绝对值,并使用$min 返回数组中的最小值。当然,$project 阶段中​​的$map 运算符返回一个“inner_distance”数组。

然后您需要遍历聚合结果并使用.bulkWrite() 方法更新您的文档。

var operations = [];
db.collection.aggregate([
    { "$project": { 
        "distance": { 
            "$min": { 
                "$map": { 
                    "input": "$distance", 
                    "as": "d", 
                    "in": { "$abs": "$$d.inner_distance" }
                }
            }
        }
    }}
]).forEach(function(doc) {
    var operation = { 'updateOne': { 
        'filter': { '_id': doc._id }, 
        'update': { 
            '$set': { 'distance': doc.distance }
        }
    }};
    operations.push(operation); 
});
operations.push( {
    ordered: true,      
    writeConcern: { w: "majority", wtimeout: 5000 } 
});

db.collection.bulkWrite(operations);

mapReduce 解决方案

var map = function() { 
    var distance = this.distance.map(function(element) { 
        return Math.abs(element.inner_distance); 
    } ); 
    emit(this._id, Math.min(...distance)); 
};

var results =  db.collection.mapReduce(map, 
    function(key, values) { return;}, 
    { 'out': { 'inline': 1 } }
);

返回这个:

{
        "results" : [
                {
                        "_id" : "123456789",
                        "value" : 1
                },
                {
                        "_id" : "143456789",
                        "value" : 1
                }
        ],
        "timeMillis" : 31,
        "counts" : {
                "input" : 2,
                "emit" : 2,
                "reduce" : 0,
                "output" : 2
        },
        "ok" : 1
}

然后您可以使用"bulk" 操作来更新您的文档。

var bulk = db.collection.initializeOrderedBulkOp();
var count = 0;
results['results'].forEach(function(element) {
    bulk.find( { '_id': element._id } ).updateOne( {
        '$set': { 'distance': element.value }
    });
    count++;
    if (count % 200 === 0) {
        bulk.execute();
        bulk = db.collection.initializeOrderedBulkOp();
    }
})

if (count > 0 )  bulk.execute();

注意:

在 mapReduce 示例中,Math.min(...distance) 使用 ES6 中的新 spread operator,但您也可以使用 Math.min.apply(Math, distance)

【讨论】:

    猜你喜欢
    • 2016-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-23
    • 2011-08-12
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    相关资源
    最近更新 更多