2012-07-06 38 views
2

我有一个实体集合,它代表一棵树。每个实体都有一个包含属性数组的属性。MongoDB中的“父链接”树上的MapReduce

例如:

{ 
    "_id" : 1, 
    "parent_id" : null, 
    "attributes" : [ "A", "B", "C" ] 
} 

我想用MapReduce的生成另一个集合这类似于原来的集合,但集合中的每个项目,它不仅包含直接与实体相关的属性,还有它的祖先,一直到hiearchy的根源。

因此,考虑下列实体:

{ 
    "_id" : 1, 
    "parent_id" : null, 
    "attributes" : [ "A", "B", "C" ] 
} 

{ 
    "_id" : 2, 
    "parent_id" : 1, 
    "attributes" : [ "D", "E", "F" ] 
} 

{ 
    "_id" : 3, 
    "parent_id" : 2, 
    "attributes" : [ "G", "H", "I" ] 
} 

的MapReduce工作的结果将是以下几点:

{ 
    "_id" : 1, 
    "attributes" : [ "A", "B", "C" ] 
} 

{ 
    "_id" : 2, 
    "attributes" : [ "A", "B", "C", "D", "E", "F" ] 
} 

{ 
    "_id" : 3, 
    "attributes" : [ "A", "B", "C", "D", "E", "F", "G", "H", "I" ] 
} 

我已经成功生产MapReduce作业里面做简单的事情,像数的属性对于每个实体,但无法让我的头脑如何处理层次结构。我愿意选择存储数据的其他方式,但不希望将整个层次结构存储在单个文档中。

在MongoDB中使用MapReduce可能会出现这种情况吗?或者我只是以错误的方式思考问题?

+0

是可以将数据在您的应用程序相结合? Map Reduce依赖于单线程的Javascript。这里是关于树的模式设计的MongoDB文档:http://www.mongodb.org/display/DOCS/Trees+in+MongoDB – Jenna 2012-07-06 21:54:28

+0

这是可能的,但它很难保持实体同步,因为可能会有相当多的深层次。 – tjrobinson 2012-07-08 05:58:34

回答

5

好吧,所以我不认为这将是非常高性能/可伸缩的,因为您必须从子节点递归查找父ID。但是,它确实提供了您想要的输出。

var mapFunc = function(doc, id) { 
    // if this is being invoked by mapReduce, it won't pass any parameters 
    if(doc == null) { 
    doc = this; 
    id = this._id; 
    } else if (doc.parent_id != null) { 
    // if this is a recursive call, find the parent 
    doc = db.test.findOne({_id:doc.parent_id}); 
    } 
    // emit the id, which is always the id of the child node (starting point), and the attributes 
    emit(id, {attributes: doc.attributes}); 
    // if parent_id is not null, call mapFunc with the hidden parameters 
    if(doc.parent_id != null) { 
    // recursive mapFunc call 
    mapFunc(doc, id); 
    } 
} 
// since we're going to call this from within mapReduce recursively, we have to save it in the system JS 
db.system.js.save({ "_id" : "mapFunc", "value" : mapFunc}); 

var reduceFunc = function(key, values) { 
    var result = {attributes:[]}; 
    values.forEach(function(value) { 
    // concat the result to the new values (I don't think order is guaranteed here) 
    result.attributes = value.attributes.concat(result.attributes); 
    }); 
    return result; 
} 

// this just moves the attributes up a level 
var finalize = function(key, value) {return value.attributes}; 

// quick test... 
db.test.mapReduce(mapFunc, reduceFunc, {out: {inline: 1}, finalize: finalize}); 

提供:

"results" : [ 
    { 
     "_id" : 1, 
     "value" : [ 
      "A", 
      "B", 
      "C" 
     ] 
    }, 
    { 
     "_id" : 2, 
     "value" : [ 
      "A", 
      "B", 
      "C", 
      "D", 
      "E", 
      "F" 
     ] 
    }, 
    { 
     "_id" : 3, 
     "value" : [ 
      "A", 
      "B", 
      "C", 
      "D", 
      "E", 
      "F", 
      "G", 
      "H", 
      "I" 
     ] 
    } 
], 
"timeMillis" : 2, 
"counts" : { 
    "input" : 3, 
    "emit" : 6, 
    "reduce" : 2, 
    "output" : 3 
}, 
"ok" : 1, 
} 
+0

谢谢,我会尽力去做。我认为查询map函数中的数据库是个坏主意 - 如果你使用分片?我们还没有,但可能会有一天。 – tjrobinson 2012-07-08 06:00:41

+0

是的,就像我说的那样,这不是一个缩放解决方案 - 也许有更好的方法,但我想不出一个。麻烦的是,你没有办法将节点链接在一起,而无需对每个级别进行另一个查询。 – 2012-07-08 13:40:23

+0

这是不可能的,因为mongodb 2.4在地图函数中使用“db。*”。此解决方案不适用于mongo的最新版本... – k4st0r42 2015-10-02 08:43:13