假设我有以下三条规则:
[{
selector: {_id: "1"},
value: {name: "apple"}
},
{
selector: {name: "apple"},
value: {type: "fruit"}
},
{
selector: {type: "fruit"},
value: {class: "food"}
}]
我想最终得到这样的结果:
{
_id: 1,
value: {
name: "apple",
type: "fruit",
class: "food"
}
}
我可以用mapreduce做这个吗?
下面是我对这个问题的看法(使用mongodb驱动程序的node.js)。我很确定它可以优化很多。
var logicize = function(db, callback) {
var reduce = function(key, values){
// If values is an array of objects, it is merged into a single object
if(values.length) {
while(values.length>1) {
var current = values.pop();
for (var property in current) {
values[0][property] = current[property];
}
}
return values[0];
}
return values;
};
db.collection("rules", function(err, rules) {
db.collection("results", function(err, results) {
rules.mapReduce("function() {emit(this.selector._id, this.value);}", reduce, {out: {replace:"results"}, query:{"selector._id":{$exists:true}}}, function() {
rules.find({"selector._id":{$exists:false}}, function(err, cursor) {
cursor.nextObject(function(err, item) {
// Recursive because I don't want to start a new mapreduce
// before the previous one has finished. The following one
// might depend on the results of the previous
(function recurse(item) {
if(item==null) // Done
callback();
else {
var map = new Function('emit(this._id, '+JSON.stringify(item.value)+');');
var conditions = {};
for(var condition in item.selector) {
conditions['value.'+condition] = item.selector[condition];
}
results.mapReduce(map, reduce, {out:{reduce:"results"},query: conditions}, function() {
// Previous mapreduce has finished so we can start the next one
cursor.nextObject(function(err, item) {
recurse(item);
});
});
}
})(item);
});
});
});
});
});
}
规则在"规则"集合中,结果进入"结果"。我首先使用具有_id的规则执行初始mapreduce。之后,我将为每个其他规则运行一个单独的mapreduce。
您几乎可以使用mapreduce执行任何您想要的操作,因为您可以在所有对象上执行任意javascript。但您应该意识到,mapreduce目前速度不是很快,建议不要在map/reduce函数中执行"db"操作(与分片不一致,可能会产生锁定问题)。
理想情况下,第一步是尝试对数据进行建模,使其预先填充,即使这意味着复制一些数据。如果这是不可能的,你应该比较mapreduce和在客户端工作的速度。