2016-11-10 12 views
10

Ağaç yapısına sahip, Model Tree Structures with Parent References deseninin kullanıldığı MongoDB'de belge listesi var. 'Name' özelliği göz önüne alındığında, ata listesi (köküne kadar) döndüren tek bir toplama sorgusu istiyorum.MongoDB'deki bir koleksiyondaki özyinelikli arama

Yapısı:

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

Toplama sonucu: (göz önüne alındığında, name = 'D')

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

Note: Şimdi belge yapısını değiştiremez. Birçok soruna neden olur. Model Tree Structures with an Array of Ancestors'u kullanmayı öneren birçok çözüm gördüm. Ama şimdi kullanamam. Tek bir toplama sorgusu kullanarak yukarıdaki kalıpla elde etmenin herhangi bir yolu var mı? Teşekkürler

+0

Neden '_id' dizesidir: pushAncesstors işlevi değişecektir? – styvane

+0

@Styvane Sadece bir örnek. Asıl doküman ObjectId – RaR

+0

@RaR'a sahip olacak. Styvane'nin sizin için işe yaramayan bir cevabı var mı? – JohnnyHK

cevap

11

MongoDB 3.4'ten başlayarak, bunu Toplama Çerçevesi ile yapabiliriz.

Boru hattımızda ilk ve en önemli aşama, $graphLookup aşamasıdır. $graphLookup, "ebeveyn" ve "ad" alanlarında yinelemeli olarak eşleşmemize olanak tanır. Sonuç olarak, her "isim" nin atalarını alırız.

boru hattı sonraki aşama biz sadece biz ilgilenen "adı" seçeneğini $match aşamadır.

son aşamada biz "atalarının" için bir ifade uygulamak $addFields veya $project aşamadır $map dizi operatörünü kullanarak dizi.

Tabi ki,operatörüyle beklenen sonucu elde etmek için reverse our array numaralı telefonu kullanıyoruz. istemci tarafı javascript kullanmak açıksa

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

, bunu başarmak için Mongo kabuk özyinelemeyi kullanabilirsiniz:

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

Bu, tüm ürünler için size tam hirearchy verecektir. Örnek çıktı:

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

senin gereksinimi doğru koleksiyonu güncelleştirmek bir diffferent koleksiyonunda veri eklemek ve orada güncellemek için değilse.

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

Teşekkür çalışan bir yolunu arıyordu MongoDB v3.4 dan çalışacaktır. Evet, istemci tarafında javascript kullanıyorum. Ancak, yukarıdaki belge mevcut belgeyi güncelleyecektir, değil mi? İhtiyaç hiyerarşiyi elde etmek, ancak belgeyi güncellemek değil. – RaR

+0

Mevcut koleksiyonu değiştirilmemiş halde bırakmak için yanıtı güncellendi. – ares

İlgili konular