我有一个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
数组,以便“展平”#34;根据转换函数将其转换为标量(我想获取距离中每个inner_distance
元素的绝对值,然后取所有这些值的最小值。)
例如,在上面的示例中,distance
数组包含:[{"inner_distance" : 2}, {"inner_distance" : 4}, {"inner_distance" : -1}]
,我需要弄清楚如何应用转换来制作distance: 1
(或者如果它更容易,新属性,例如distance_new: 1
。
我想内联执行此操作(是正确的术语吗?),以便我执行操作并以存储的记录结束:
{
"_id" : "123456789",
"distance" : 1,
"name" : [
{
"inner_name" : "MyName"
}
],
"entries" : [
{ ... },
{ ... },
],
"property1" : "myproperty1",
"property2" : "myproperty2",
"property3" : "myproperty3"
}
有没有人对这样的事情有任何经验?我一直在试图弄清楚如何创建一个map-reduce
命令来运行它但没有运气。
答案 0 :(得分:2)
你想要的东西可以在MongoDB 3.2中有效处理。
您需要使用$abs
运算符返回每个" inner_distance"的绝对值。以及$min
返回数组中的最小值。当然,$map
阶段中的$project
运算符会返回" 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);
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)