MongoDB'deki $ çözme operatörü nedir?


103

Bu MongoDB ile ilk günüm, bu yüzden lütfen benimle sakin olun :)

$unwindOperatörü anlayamıyorum , belki de İngilizce benim anadilim olmadığı için.

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

Proje operatörü anlayabildiğim bir şey sanırım (sanırım öyle SELECTdeğil mi?). Ancak daha sonra $unwind(alıntı) , her kaynak belgedeki çözülmemiş dizinin her üyesi için bir belge döndürür .

Bu bir gibi JOINmi? Evet ise, nasıl bir sonucudur $project(ile _id, author, titleve tagsalanlar) ile karşılaştırılabilir tagsdiziden?

NOT : Örneği MongoDB web sitesinden aldım, tagsdizinin yapısını bilmiyorum . Bunun basit bir etiket adları dizisi olduğunu düşünüyorum.

Yanıtlar:


240

Öncelikle MongoDB'ye hoş geldiniz!

Hatırlanması gereken şey, MongoDB'nin veri depolamak için bir "NoSQL" yaklaşımı kullandığı, bu yüzden seçimlerin, birleşmelerin vb. Düşüncelerini zihninizden yok edin. Verilerinizi saklama biçimi belgeler ve koleksiyonlar biçimindedir, bu da verileri depolama konumlarınızdan dinamik bir şekilde ekleyip elde etmenize olanak tanır.

Bununla birlikte, $ çözme parametresinin arkasındaki kavramı anlamak için, önce alıntı yapmaya çalıştığınız kullanım durumunun ne dediğini anlamalısınız. Mongodb.org'dan alınan örnek belge aşağıdaki gibidir:

{
 title : "this is my title" ,
 author : "bob" ,
 posted : new Date () ,
 pageViews : 5 ,
 tags : [ "fun" , "good" , "fun" ] ,
 comments : [
             { author :"joe" , text : "this is cool" } ,
             { author :"sam" , text : "this is bad" }
 ],
 other : { foo : 5 }
}

Etiketlerin aslında 3 öğeden oluşan bir dizi olduğuna dikkat edin, bu durumda "eğlenceli", "iyi" ve "eğlenceli".

$ Çözülmenin yaptığı şey, her öğe için bir belgeyi soymanıza ve elde edilen bu belgeyi döndürmenize izin vermektir. Bunu klasik bir yaklaşımla düşünmek için, "etiketler dizisindeki her öğe için, yalnızca o öğeye sahip bir belge döndür" ifadesinin eşdeğeri olacaktır.

Böylece, aşağıdakileri çalıştırmanın sonucu:

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

aşağıdaki belgeleri döndürür:

{
     "result" : [
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "fun"
             },
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "good"
             },
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "fun"
             }
     ],
     "OK" : 1
}

Sonuç dizisinde değişen tek şeyin etiketler değerinde döndürülen şey olduğuna dikkat edin. Bunun nasıl çalıştığına dair ek bir referansa ihtiyacınız varsa, buraya bir bağlantı ekledim . Umarım bu yardımcı olur ve şimdiye kadar karşılaştığım en iyi NoSQL sistemlerinden birine girmenizde iyi şanslar.


44

$unwind ardışık düzen içindeki her belgeyi dizi öğesi başına bir kez çoğaltır.

Senin girdi boru hattı iki elemanlı bir makale doc içeriyorsa Yani tags, {$unwind: '$tags'}haricinde aynı olan iki makale belgeler olması boru hattı dönüştürecektir tagsalanında. İlk dokümanda, tagsorijinal dokümanın dizisindeki ilk öğeyi içerir ve ikinci dokümanda, tagsikinci öğeyi içerir.


22

Bir örnekle anlayalım

Bu nasıl şirket belge gibi görünür:

orijinal belge

Bu $unwind, dizi değerli bir alana sahip belgeleri girdi olarak almamızı ve dizideki her öğe için bir çıktı belgesi olacak şekilde çıktı belgeleri oluşturmamızı sağlar. kaynak

$ Çözme aşaması

Öyleyse şirket örneklerimize geri dönelim ve gevşeme aşamalarının kullanımına bir göz atalım. Bu sorgu:


db.companies.aggregate([
    { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } },
    { $project: {
        _id: 0,
        name: 1,
        amount: "$funding_rounds.raised_amount",
        year: "$funding_rounds.funded_year"
    } }
])

hem miktar hem de yıl için dizileri olan belgeler üretir.

proje çıktısı

Çünkü fonlama turları dizisindeki her unsur için toplanan miktara ve finanse edilen yıla erişiyoruz. Bunu düzeltmek için, bu toplama işlem hattına proje aşamamızdan önce bir çözülme aşaması ekleyebilir ve bunu unwind, finansman turları dizisini istediğimizi söyleyerek parametreleştirebiliriz :


db.companies.aggregate([
    { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } },
    { $unwind: "$funding_rounds" },
    { $project: {
        _id: 0,
        name: 1,
        amount: "$funding_rounds.raised_amount",
        year: "$funding_rounds.funded_year"
    } }
])

çözülme, bir sonraki aşamaya girdi olarak aldığından daha fazla belgenin çıktısını alma etkisine sahiptir

funding_roundsDiziye bakarsak, her biri funding_roundsiçin bir raised_amountve bir funded_yearalan olduğunu biliyoruz . Dolayısıyla, dizinin unwindöğeleri olan belgelerin her biri funding_roundsiçin bir çıktı belgesi oluşturacaktır. Şimdi, bu örnekte, değerlerimiz strings. Ancak, bir dizideki öğelerin değerinin türüne bakılmaksızın, unwindbu değerlerin her biri için bir çıktı belgesi üretecektir, öyle ki söz konusu alan yalnızca o öğeye sahip olacaktır. Durumunda, sahnemize aktarılan her belgenin funding_roundsdeğeri olarak bu unsur bu belgelerden biri olacaktır . Bunu çalıştırmanın sonucu, şimdi bir ve a almamızdır . Her şirket için her finansman turu için bir tanefunding_roundsprojectamountyearkoleksiyonumuzda. Bunun anlamı, eşleşmemizin birçok şirket belgesi ürettiği ve bu şirket belgelerinin her birinin birçok belgeyle sonuçlandığı. Her şirket belgesinde her finansman turu için bir tane. sahneden unwindkendisine verilen belgeleri kullanarak bu işlemi gerçekleştirir match. Ve her firma için tüm bu belgeler projectsahneye aktarılır .

çıktıyı çöz

Dolayısıyla, fon verenin Greylock olduğu tüm belgeler (sorgu örneğinde olduğu gibi), filtreyle eşleşen her şirket için finansman turlarının sayısına eşit sayıda belgeye bölünecektir $match: {"funding_rounds.investments.financial_org.permalink": "greylock" }. Ve ortaya çıkan belgelerin her biri daha sonra bize iletilecek project. Şimdi, unwindgirdi olarak aldığı belgelerin her biri için tam bir kopya oluşturur. Tüm alanlar, bir istisna dışında aynı anahtara ve değere sahiptir ve bu, funding_roundsalanın bir funding_roundsbelge dizisi olmaktan çok , tek bir belge olan ve bireysel bir finansman turu olan bir değere sahip olmasıdır. Yani, olan bir şirket 4 finansman mermi sonuçlanacaktır unwindoluşturma 4belgeler. Her alanın tam bir kopya olduğu yerlerde, funding_roundsalan haricinde bu kopyaların her biri için bir dizi olmak yerine , şu anda işlenen funding_roundsşirket belgesindeki diziden ayrı bir öğe olacaktır unwind. Dolayısıyla, bir unwindsonraki aşamaya girdi olarak aldığından daha fazla belgenin çıktısını alma etkisine sahiptir. Bunun anlamı, projectsahnemizin artık bir funding_roundsalan almasıdır, bir dizi değil, bunun yerine bir raised_amountve bir funded_yearalanı olan iç içe bir belge . Böylece, filtreyi kullanan projecther şirket için birden fazla belge alacak matchve bu nedenle belgelerin her birini ayrı ayrı işleyebilir ve her şirket için her bir finansman turu için ayrı bir miktar ve yıl belirleyebilir..


2
aynı belgeyi kullanmak daha iyi olacaktır.
Jeb50

1
$ Çözme için ilk kullanım durumu olarak, oldukça karmaşık iç içe geçmiş kümelerim vardı. Mongo docs ve stackowerflow arasında gidip gelirken, cevabınız sonunda $ project ve $ çözülmeyi daha iyi anlamama yardımcı oldu. Teşekkürler @Zameer!
yedi

3

Mongodb resmi belgelerine göre:

$ çözme Her öğe için bir belge çıktısı sağlamak üzere girdi belgelerinden bir dizi alanını yeniden yapılandırır. Her çıktı belgesi, dizi alanı değerinin öğe ile değiştirildiği girdi belgesidir.

Temel örnek üzerinden açıklama:

Bir koleksiyon envanterinde aşağıdaki belgeler bulunur:

{ "_id" : 1, "item" : "ABC", "sizes": [ "S", "M", "L"] }
{ "_id" : 2, "item" : "EFG", "sizes" : [ ] }
{ "_id" : 3, "item" : "IJK", "sizes": "M" }
{ "_id" : 4, "item" : "LMN" }
{ "_id" : 5, "item" : "XYZ", "sizes" : null }

Aşağıdaki $ çözme işlemleri eşdeğerdir ve boyutlar alanındaki her öğe için bir belge döndürür . Boyutlar alanı bir diziye çözümlenmez, ancak eksik, null veya boş bir dizi değilse, $ çözülme, dizi olmayan işlenenleri tek bir öğe dizisi olarak değerlendirir.

db.inventory.aggregate( [ { $unwind: "$sizes" } ] )

veya

db.inventory.aggregate( [ { $unwind: { path: "$sizes" } } ] 

Sorgu çıktısının üstünde:

{ "_id" : 1, "item" : "ABC", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "sizes" : "L" }
{ "_id" : 3, "item" : "IJK", "sizes" : "M" }

Neden gerekli?

$ çözme, toplama yapılırken çok kullanışlıdır. sıralama, ayırma vb. gibi çeşitli işlemleri gerçekleştirmeden önce karmaşık / iç içe geçmiş belgeyi basit bir belgeye böler.

$ Gevşeme hakkında daha fazla bilgi edinmek için:

https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/

Toplama hakkında daha fazla bilgi edinmek için:

https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/


2

Bir koleksiyondaki bu Verileri anlamak için aşağıdaki örneği düşünün

{
        "_id" : 1,
        "shirt" : "Half Sleeve",
        "sizes" : [
                "medium",
                "XL",
                "free"
        ]
}

Sorgu - db.test1.aggregate ([{$ çözme: "$ boyutlar"}]);

çıktı

{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "medium" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "XL" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "free" }

1

RDBMS ile ilişkili bir şekilde açıklamama izin verin. Bu ifade:

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

belgeye / kayda başvurmak için :

{
 title : "this is my title" ,
 author : "bob" ,
 posted : new Date () ,
 pageViews : 5 ,
 tags : [ "fun" , "good" , "fun" ] ,
 comments : [
             { author :"joe" , text : "this is cool" } ,
             { author :"sam" , text : "this is bad" }
 ],
 other : { foo : 5 }
}

$ Projesi / Seç sadece bu alan / sütunları olarak döner

SEÇ yazar, başlık, etiket GELEN makalesinde

Sırada, Mongo'nun eğlenceli kısmı, bu diziyi tags : [ "fun" , "good" , "fun" ]"etiketler" olarak adlandırılan başka bir ilgili tablo olarak düşünün (değerlerin bazı kopyaları olduğu için bir arama / referans tablosu olamaz). SELECT'in genellikle dikey şeyler ürettiğini unutmayın, bu nedenle "etiketleri", dikey olarak () tablo "etiketlerine" ayırmaktır.

$ Project + $ çözülmesinin sonucu: görüntü açıklamasını buraya girin

Çıkışı JSON'a çevirin:

{ "author": "bob", "title": "this is my title", "tags": "fun"},
{ "author": "bob", "title": "this is my title", "tags": "good"},
{ "author": "bob", "title": "this is my title", "tags": "fun"}

Mongo'ya "_id" alanını atlamasını söylemediğimiz için otomatik olarak eklendi.

Anahtar, toplama işlemini tablo gibi yapmaktır.


Ya da bunu düşünmenin başka bir yolu UNION ALL
Jeb50
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.