2016-11-10 8 views
10

Ich habe eine Liste von Dokumenten in MongoDB mit Baumstruktur, wobei Model Tree Structures with Parent References Muster verwendet. Ich möchte eine einzelne Aggregation Abfrage, die Vorfahrenliste (bis zum Stamm) zurückgegeben, angesichts der Eigenschaft "Name".Rekursive Suche auf einer Sammlung in MongoDB

Struktur:

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

Aggregationser: (gegeben, name = 'D')

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

Note: Ich kann nicht die Dokumentstruktur jetzt ändern. Es wird viele Probleme verursachen. Ich sah viele Lösungen, die vorschlagen, Model Tree Structures with an Array of Ancestors zu verwenden. Aber ich kann es jetzt nicht benutzen. Gibt es eine Möglichkeit, dies mit dem obigen Muster mithilfe einer einzigen Aggregationsabfrage zu erreichen? Danke

+0

Warum ist '_id' Zeichenfolge? – styvane

+0

@Styvane Es ist nur ein Beispiel. Aktuelles Dokument wird ObjectId haben – RaR

+0

@RaR Gibt es etwas über Styvane's Antwort, die nicht für dich funktioniert, die das Kopfgeld veranlasst? – JohnnyHK

Antwort

11

Ab MongoDB 3.4 können wir dies mit dem Aggregation Framework tun. Die erste und wichtigste Stufe in unserer Pipeline ist die $graphLookup Stufe. $graphLookup ermöglicht es uns, rekursiv auf das Feld "Parent" und "Name" abzustimmen. Als Ergebnis erhalten wir die Vorfahren jedes "Namens".

Die nächste Stufe in der Pipeline ist die $match Stadium, in dem wir den „Namen“ wählen Sie einfach wir daran interessiert sind.

Die letzte Stufe der Stufe $addFields oder $project ist, wo wir einen Ausdruck für die „Vorfahren“ gelten Array mit dem Array-Operator $map.

Natürlich mit dem $reverseArray Operator wir reverse our array, um das erwartete Ergebnis zu erhalten.

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

Wenn Sie offen sind Client-Seite Javascript verwenden, können Sie die Rekursion auf dem Mongo Shell verwenden, um dies zu erreichen:

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); 
}) 

Dies gibt Ihnen die vollständige hirearchy für alle Produkte. Beispielausgabe:

{ "_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" } ] } 

Wenn Ihre Anforderung nicht die korrekte Sammlung zu aktualisieren, um die Daten in einer diffferent Sammlung einfügen und dort aktualisieren. Die pushAncesstors Funktion ändert sich in:

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

funktioniert Danke für die Antwort. Ja, ich bin offen für die Verwendung von clientseitigem JavaScript. Aber das obige wird das bestehende Dokument aktualisieren, oder? Es besteht die Notwendigkeit, die Hierarchie zu erhalten, das Dokument jedoch nicht zu aktualisieren. – RaR

+0

Die Antwort wurde aktualisiert, um die aktuelle Sammlung unverändert zu lassen. – ares