Apa itu operator $ unwind di MongoDB?

103

Ini adalah hari pertama saya dengan MongoDB, jadi silakan bergabung dengan saya :)

Saya tidak mengerti $unwindoperatornya, mungkin karena bahasa Inggris bukan bahasa ibu saya.

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

Operator proyek adalah sesuatu yang saya bisa mengerti, saya kira (seperti SELECT, bukan?). Tapi kemudian, $unwind(mengutip) mengembalikan satu dokumen untuk setiap anggota array yang tidak digulung dalam setiap dokumen sumber .

Apakah ini seperti JOIN? Jika ya, bagaimana hasil $project(dengan _id, author, titledan tagsbidang) dapat dibandingkan dengan tagsarray yang?

CATATAN : Saya telah mengambil contoh dari situs web MongoDB, saya tidak tahu struktur tagsarray. Saya pikir itu adalah deretan nama tag sederhana.

gremo
sumber

Jawaban:

236

Pertama, selamat datang di MongoDB!

Hal yang perlu diingat adalah bahwa MongoDB menggunakan pendekatan "NoSQL" untuk penyimpanan data, jadi hilangkan pikiran memilih, bergabung, dll. Dari pikiran Anda. Cara penyimpanan data Anda dalam bentuk dokumen dan koleksi, yang memungkinkan cara dinamis untuk menambahkan dan memperoleh data dari lokasi penyimpanan Anda.

Karena itu, untuk memahami konsep di balik parameter $ unwind, pertama-tama Anda harus memahami kasus penggunaan yang coba Anda kutip. Contoh dokumen dari mongodb.org adalah sebagai berikut:

{
 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 }
}

Perhatikan bagaimana tag sebenarnya adalah larik 3 item, dalam hal ini menjadi "menyenangkan", "baik", dan "menyenangkan".

Apa yang dilakukan $ unwind adalah memungkinkan Anda mengupas dokumen untuk setiap elemen dan mengembalikan dokumen yang dihasilkan. Untuk memikirkan ini dalam pendekatan klasik, itu akan menjadi ekuivalen dengan "untuk setiap item dalam larik tag, kembalikan dokumen dengan hanya item itu".

Jadi, hasil dari menjalankan sebagai berikut:

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

akan mengembalikan dokumen-dokumen berikut:

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

Perhatikan bahwa satu-satunya hal yang berubah dalam larik hasil adalah apa yang dikembalikan dalam nilai tag. Jika Anda memerlukan referensi tambahan tentang cara kerjanya, saya telah menyertakan tautan di sini . Semoga ini membantu, dan semoga berhasil dengan Anda terjun ke salah satu sistem NoSQL terbaik yang pernah saya temui sejauh ini.

Lab HGS
sumber
44

$unwind menduplikasi setiap dokumen dalam pipeline, sekali per elemen array.

Jadi, jika pipeline masukan Anda berisi satu dokumen artikel dengan dua elemen di dalamnya tags, {$unwind: '$tags'}akan mengubah pipeline menjadi dua dokumen artikel yang sama kecuali tagsbidangnya. Di dokumen pertama, tagsakan berisi elemen pertama dari array dokumen asli, dan di dokumen kedua, tagsakan berisi elemen kedua.

JohnnyHK
sumber
22

Mari kita pahami dengan sebuah contoh

Seperti inilah tampilan dokumen perusahaan :

dokumen asli

The $unwindmemungkinkan kita untuk mengambil dokumen sebagai masukan yang memiliki medan dihargai array dan menghasilkan dokumen output, sehingga ada satu dokumen output untuk setiap elemen dalam array. sumber

Tahap $ bersantai

Jadi mari kita kembali ke contoh perusahaan kami, dan lihat penggunaan tahapan melepas lelah. Kueri ini:


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"
    } }
])

menghasilkan dokumen yang memiliki array untuk jumlah dan tahun.

keluaran proyek

Karena kami mengakses jumlah yang dikumpulkan dan tahun pendanaan untuk setiap elemen dalam rangkaian putaran pendanaan. Untuk memperbaikinya, kita dapat menyertakan tahap pelepasan sebelum tahap proyek kita dalam pipa agregasi ini, dan membuat parameter ini dengan mengatakan bahwa kita ingin ke unwindarray putaran pendanaan:


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"
    } }
])

melepas memiliki efek keluaran ke tahap berikutnya lebih banyak dokumen daripada yang diterima sebagai masukan

Jika kita melihat pada funding_roundsarray, kita tahu bahwa untuk masing-masing funding_rounds, ada raised_amountdan funded_yearbidang. Jadi, unwinduntuk setiap dokumen yang merupakan elemen dari funding_roundsarray akan menghasilkan dokumen keluaran. Sekarang, dalam contoh ini, nilai kita adalah strings. Namun, terlepas dari jenis nilai untuk elemen dalam larik, unwindakan menghasilkan dokumen keluaran untuk masing-masing nilai ini, sehingga bidang yang dimaksud hanya akan memiliki elemen itu. Dalam kasus funding_rounds, elemen itu akan menjadi salah satu dokumen ini sebagai nilai funding_roundsuntuk setiap dokumen yang diteruskan ke projecttahap kita . Hasilnya, setelah menjalankan ini, sekarang kita mendapatkan amountdan a year. Satu untuk setiap putaran pendanaan untuk setiap perusahaandalam koleksi kami. Artinya, pertandingan kami menghasilkan banyak dokumen perusahaan dan setiap dokumen perusahaan tersebut menghasilkan banyak dokumen. Satu untuk setiap putaran pendanaan dalam setiap dokumen perusahaan. unwindmelakukan operasi ini menggunakan dokumen yang diserahkan kepadanya dari matchpanggung. Dan semua dokumen ini untuk setiap perusahaan kemudian diteruskan ke projectpanggung.

keluaran santai

Jadi, semua dokumen di mana pemberi dana adalah Greylock (seperti dalam contoh kueri) akan dipecah menjadi beberapa dokumen, sama dengan jumlah putaran pendanaan untuk setiap perusahaan yang cocok dengan filter $match: {"funding_rounds.investments.financial_org.permalink": "greylock" }. Dan masing-masing dokumen yang dihasilkan kemudian akan diteruskan ke kami project. Sekarang, unwindbuat salinan persis untuk setiap dokumen yang diterimanya sebagai input. Semua bidang memiliki kunci dan nilai yang sama, dengan satu pengecualian, dan itu adalah bahwa funding_roundsbidang tersebut daripada berupa kumpulan funding_roundsdokumen, melainkan memiliki nilai berupa satu dokumen, yang merupakan putaran pendanaan individu. Jadi, sebuah perusahaan yang memiliki 4 putaran pendanaan akan menghasilkan unwindmenciptakan 4dokumen. Di mana setiap bidang adalah salinan persis, kecuali funding_roundsbidang, yang akan menjadi larik untuk setiap salinan tersebut, melainkan akan menjadi elemen individual dari funding_roundslarik dari dokumen perusahaan yang unwindsedang diproses. Jadi, unwindmemiliki efek keluaran ke tahap berikutnya lebih banyak dokumen daripada yang diterima sebagai masukan. Artinya adalah bahwa projecttahapan kita sekarang mendapatkan funding_roundsbidang yang lagi-lagi bukan array, melainkan dokumen bersarang yang memiliki raised_amountdan funded_yearbidang. Jadi, projectakan menerima banyak dokumen untuk setiap perusahaan yang matchmenggunakan filter dan oleh karena itu dapat memproses setiap dokumen secara individual dan mengidentifikasi jumlah dan tahun individu untuk setiap putaran pendanaan untuk setiap perusahaan.

Zameer Ansari
sumber
2
menggunakan dokumen yang sama akan lebih baik.
Jeb50
1
Sebagai kasus penggunaan pertama untuk $ unwind, saya memiliki set bersarang yang cukup rumit. Beralih di antara dokumen mongo dan stackowerflow, jawaban Anda akhirnya membantu saya memahami $ project dan $ bersantai dengan lebih baik. Terima kasih @Zameer!
tujuh
3

Sesuai dokumentasi resmi mongodb:

$ unwind Mendekonstruksi bidang array dari dokumen masukan ke keluaran dokumen untuk setiap elemen. Setiap dokumen keluaran adalah dokumen masukan dengan nilai bidang larik diganti dengan elemen.

Penjelasan melalui contoh dasar:

Inventaris koleksi memiliki dokumen-dokumen berikut:

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

Operasi $ unwind berikut ini setara dan mengembalikan dokumen untuk setiap elemen di bidang ukuran . Jika bidang ukuran tidak menyelesaikan array tetapi tidak hilang, null, atau array kosong, $ unwind memperlakukan operan non-array sebagai array elemen tunggal.

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

atau

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

Di atas keluaran kueri:

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

Mengapa ini dibutuhkan?

$ unwind sangat berguna saat melakukan agregasi. itu memecah dokumen kompleks / bersarang menjadi dokumen sederhana sebelum melakukan berbagai operasi seperti penyortiran, searcing, dll.

Untuk mengetahui lebih banyak tentang $ bersantai:

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

Untuk mengetahui lebih banyak tentang agregasi:

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

Amitesh Bharti
sumber
2

pertimbangkan contoh di bawah ini untuk memahami Data ini dalam koleksi

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

Kueri - db.test1.aggregate ([{$ unwind: "$ size"}]);

keluaran

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

Izinkan saya menjelaskan dengan cara yang berkaitan dengan cara RDBMS. Ini pernyataannya:

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

untuk diterapkan ke dokumen / catatan :

{
 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 }
}

The $ Proyek / Pilih hanya mengembalikan ini bidang / kolom sebagai

PILIH penulis, judul, tag DARI artikel

Berikutnya adalah bagian menyenangkan dari Mongo, anggap larik ini tags : [ "fun" , "good" , "fun" ]sebagai tabel terkait lainnya (tidak bisa menjadi tabel pencarian / referensi karena nilai memiliki beberapa duplikasi) bernama "tag". Ingat SELECT umumnya menghasilkan hal-hal vertikal, jadi melepaskan "tag" adalah dengan membagi () vertikal menjadi "tag" tabel.

Hasil akhir dari $ project + $ unwind: masukkan deskripsi gambar di sini

Terjemahkan keluaran ke JSON:

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

Karena kami tidak memberi tahu Mongo untuk menghilangkan kolom "_id", jadi kolom itu otomatis ditambahkan.

Kuncinya adalah membuatnya seperti tabel untuk melakukan agregasi.

Jeb50
sumber
Atau cara lain untuk memikirkannya adalah UNION ALL
Jeb50