2012-07-06 37 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