2016-11-10 19 views
10

MongoDBのツリー構造を持つ文書のリストがあります。Model Tree Structures with Parent Referencesパターンが使用されています。私は 'name'プロパティを与えられた祖先リスト(ルートまで)を返す単一の集約クエリが必要です。MongoDBのコレクションの再帰的検索

構造:

{ 
    '_id': '1', 
    'name': 'A', 
    'parent': '', 
}, 
{ 
    '_id': '2', 
    'name': 'B', 
    'parent': 'A', 
}, 
{ 
    '_id': '3', 
    'name': 'C', 
    'parent': 'B', 
}, 
{ 
    '_id': '4', 
    'name': 'D', 
    'parent': 'C', 
} 

集計結果:(考えると、名前= 'D')

{ 
    '_id': '4', 
    'name': 'D', 
    'ancestors': [{name:'C'}, {name:'B'}, {name:'A'}] 
} 

Note:私は今、文書構造を変更することはできません。それは多くの問題を引き起こすでしょう。私はModel Tree Structures with an Array of Ancestorsを使用することを提案する多くのソリューションを見た。しかし、私は今それを使用することはできません。単一集約クエリを使用して上記のパターンでそれを達成する方法はありますか?ありがとうございます

+0

なぜ「_id」文字列ですか? – styvane

+0

@Styvaneこれは単なる例です。実際の教師がObjectIdを持っています – RaR

+0

@RaR Styvaneの答えが恩恵を促してくれるのですか? – JohnnyHK

答えて

11

MongoDB 3.4からは、これをAggregation Frameworkで行うことができます。

パイプラインで最初に最も重要なステージは$graphLookupステージです。 $graphLookupは、「親」フィールドと「名前」フィールドに再帰的に一致させることができます。結果として、我々は各 "名前"の先祖を得る。

パイプラインの次のステージは、我々は単に「名前」我々が興味を持っているを選択$match段階である。

最終段階では、我々は「先祖」に式を適用する$addFields$project段階であります配列演算子$mapを使用して配列を返します。

もちろん、$reverseArray演算子を使用すると、期待結果を得るためにreverse our arrayとなります。

db.collection.aggregate(
    [ 
     { "$graphLookup": { 
      "from": "collection", 
      "startWith": "$parent", 
      "connectFromField": "parent", 
      "connectToField": "name", 
      "as": "ancestors" 
     }}, 
     { "$match": { "name": "D" } }, 
     { "$addFields": { 
      "ancestors": { 
       "$reverseArray": { 
        "$map": { 
         "input": "$ancestors", 
         "as": "t", 
         "in": { "name": "$$t.name" } 
        } 
       } 
      } 
     }} 
    ] 
) 
1

あなたは、あなたがこれを達成するためのmongoシェル上で再帰を使用することができますjavascriptのクライアント側を使用して開いている場合:

var pushAncesstors = function (name, doc) { 
    if(doc.parent) { 
    db.collection.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}}); 
    pushAncesstors(name, db.collection.findOne({name : doc.parent})) 
    } 
} 

db.collection.find().forEach(function (doc){ 
    pushAncesstors(doc.name, doc); 
}) 

これはあなたのすべての製品の完全なhirearchyを与えるだろう。サンプル出力:

{ "_id" : "1", "name" : "A", "parent" : "" } 
{ "_id" : "2", "name" : "B", "parent" : "A", "ancesstors" : [ { "name" : "A" } ] } 
{ "_id" : "3", "name" : "C", "parent" : "B", "ancesstors" : [ { "name" : "B" }, { "name" : "A" } ] } 
{ "_id" : "4", "name" : "D", "parent" : "C", "ancesstors" : [ { "name" : "C" }, { "name" : "B" }, { "name" : "A" } ] } 

正しいコレクションを更新しないようにするには、データを別のコレクションに挿入して更新します。 pushAncesstors機能は次のように変更されます:

var pushAncesstors = function (name, doc) { 
    if(doc.parent) { 
    db.outputColl.save(doc) 
    db.outputColl.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}}); 
    pushAncesstors(name, db.collection.findOne({name : doc.parent})) 
    } 
} 
+0

答えをありがとう。はい、私はクライアントサイドのJavaScriptを使用することにオープンしています。しかし、上記のものは既存の文書を更新するでしょうか?階層を取得し、文書を更新しないことが必要です。 – RaR

+0

答えを更新して、現在のコレクションを変更しないままにしました。 – ares

関連する問題