2

ツリーを表すエンティティのコレクションがあります。各エンティティには、属性の配列を含むプロパティがあります。

例えば:

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

MapReduce を使用して、元のコレクションに似た別のコレクションを生成したいと考えていますが、コレクション内の各アイテムには、エンティティに直接関連付けられている属性だけでなく、その祖先の属性も含まれています。階層のルート。

したがって、次のエンティティが与えられます。

{
    "_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 ジョブの生成を管理しましたが、階層をどのように処理するかについて頭を悩ませることができません。データを保存する別の方法を受け入れていますが、階層全体を 1 つのドキュメントに保存したくありません。

MongoDB の MapReduce でこの種のシンは可能ですか、それとも問題を間違った方法で考えているだけですか?

4

1 に答える 1

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,
}
于 2012-07-06T16:51:41.423 に答える