Asosiasi Banyak-ke-Banyak MongoDB

143

Bagaimana Anda melakukan asosiasi banyak-ke-banyak dengan MongoDB?

Sebagai contoh; katakanlah Anda memiliki tabel Pengguna dan tabel Peran. Pengguna memiliki banyak peran, dan peran memiliki banyak pengguna. Di tanah SQL Anda akan membuat tabel UserRoles.

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

Bagaimana hubungan yang sama ditangani dalam MongoDB?

Josh Close
sumber
Lihat juga jawaban untuk pertanyaan ini dan pertanyaan ini
Matius Murdoch

Jawaban:

96

Bergantung pada kebutuhan kueri Anda, Anda dapat memasukkan semuanya ke dalam dokumen pengguna:

{name:"Joe"
,roles:["Admin","User","Engineer"]
}

Untuk mendapatkan semua Insinyur, gunakan:

db.things.find( { roles : "Engineer" } );

Jika Anda ingin mempertahankan peran dalam dokumen terpisah maka Anda dapat memasukkan _id dokumen dalam array peran alih-alih nama:

{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}

dan mengatur peran seperti:

{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}
diederikh
sumber
7
Yang terakhir akan lebih baik karena saya perlu mendapatkan daftar semua peran yang tersedia. Satu-satunya bagian yang buruk adalah saya harus mengatur kedua ujung asosiasi itu. Saat melakukan cara SQL, menambahkan UserRole akan membuat Pengguna tahu tentang Peran dan Peran tahu tentang Pengguna. Dengan cara ini berarti saya harus mengatur Peran pada Pengguna, dan Pengguna pada Peran. Saya kira itu baik-baik saja.
Josh Tutup
46
Hanya karena database tidak mendukung sql tidak berarti bahwa referensi bukan alat yang berguna NoSQL! = NoReference lihat penjelasan ini: mongodb.org/display/DOCS/Schema+Design
Tom Gruner
8
Ini sepertinya bukan ide yang bagus. Jika Anda hanya memiliki enam peran, tentu saja, tetapi bagaimana jika Anda memiliki 20.000 objek yang dapat dikaitkan dengan 20.000 objek lebih banyak (dalam hubungan banyak-banyak)? Bahkan dokumen MongoDB mengisyaratkan bahwa Anda harus menghindari array referensi yang bisa berubah dan besar. docs.mongodb.org/manual/tutorial/…
CaptSaltyJack
Tentunya untuk hubungan banyak ke banyak dengan banyak objek yang ingin Anda gunakan solusi yang berbeda (seperti penerbit / contoh buku dalam dokumen). Dalam hal ini berfungsi dengan baik dan hanya akan menyulitkan jika Anda membuat dokumen peran pengguna yang terpisah.
diederikh
1
Ini berfungsi untuk sebagian besar sistem karena peran biasanya merupakan kumpulan kecil dan kami biasanya mengambil pengguna dan kemudian melihat perannya. Tetapi bagaimana jika perannya besar? atau bagaimana jika saya meminta Anda untuk memberi saya daftar pengguna yang memiliki peran == "Insinyur"? Sekarang Anda harus meminta seluruh koleksi pengguna Anda (mengunjungi semua pengguna yang juga tidak memiliki peran Engineer) hanya untuk mendapatkan 2 atau 3 pengguna yang mungkin memiliki peran ini di antara jutaan pengguna tersebut misalnya. Meja atau koleksi terpisah jauh lebih baik.
Pemrogram
32

Alih-alih mencoba memodelkan sesuai dengan pengalaman kami selama bertahun-tahun dengan RDBMS, saya merasa jauh lebih mudah untuk memodelkan solusi repositori dokumen menggunakan MongoDB, Redis, dan penyimpanan data NoSQL lainnya dengan mengoptimalkan untuk kasus penggunaan baca, sambil tetap mempertimbangkan atomiknya. operasi tulis yang perlu didukung oleh kasus penggunaan tulis.

Misalnya, penggunaan domain "Pengguna dalam Peran" ikuti:

  1. Peran - Buat, Baca, Perbarui, Hapus, Daftar Pengguna, Tambah Pengguna, Hapus Pengguna, Hapus Semua Pengguna, Indeks Pengguna atau yang serupa untuk mendukung "Is User In Role" (operasi seperti wadah + metadata sendiri).
  2. Pengguna - Buat, Baca, Perbarui, Hapus (Operasi CRUD seperti entitas yang berdiri sendiri)

Ini dapat dimodelkan sebagai templat dokumen berikut:

User: { _id: UniqueId, name: string, roles: string[] }
    Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
    Indexes: unique: [ name ]

Untuk mendukung penggunaan frekuensi tinggi, seperti fitur terkait Peran dari entitas Pengguna, User.Roles sengaja didenormalkan, disimpan pada Pengguna serta Peran.Pengguna memiliki penyimpanan duplikat.

Jika tidak mudah terlihat dalam teks, tetapi ini adalah jenis pemikiran yang dianjurkan saat menggunakan repositori dokumen.

Saya berharap bahwa ini membantu menjembatani kesenjangan sehubungan dengan sisi baca operasi.

Untuk sisi tulis, yang dianjurkan adalah memodelkan menurut penulisan atom. Misalnya, jika struktur dokumen mengharuskan untuk mendapatkan kunci, memperbarui satu dokumen, lalu yang lain, dan mungkin lebih banyak dokumen, kemudian melepaskan kunci, kemungkinan model telah gagal. Hanya karena kita dapat membuat kunci terdistribusi bukan berarti kita harus menggunakannya.

Untuk kasus model Pengguna dalam Peran, operasi yang meregangkan atom kita, menghindari penguncian adalah menambah atau menghapus Pengguna dari Peran. Dalam kedua kasus tersebut, operasi yang berhasil menghasilkan Pengguna tunggal dan dokumen Peran tunggal yang diperbarui. Jika ada yang gagal, mudah untuk melakukan pembersihan. Ini adalah salah satu alasan pola Unit Kerja muncul cukup banyak di mana repositori dokumen digunakan.

Operasi yang benar-benar meregangkan penulisan atom kami menghindari kunci adalah membersihkan Peran, yang akan menghasilkan banyak pembaruan Pengguna untuk menghapus nama Peran dari array User.roles. Operasi clear ini umumnya tidak disarankan, tetapi jika diperlukan dapat diimplementasikan dengan memesan operasi:

  1. Dapatkan daftar nama pengguna dari Role.users.
  2. Iterasi nama pengguna dari langkah 1, hapus nama peran dari User.roles.
  3. Bersihkan pengguna Peran.

Dalam kasus masalah, yang paling mungkin terjadi dalam langkah 2, kemunduran mudah karena set nama pengguna yang sama dari langkah 1 dapat digunakan untuk memulihkan atau melanjutkan.

paegun
sumber
15

Saya baru saja menemukan pertanyaan ini dan, meskipun sudah lama, saya pikir akan bermanfaat untuk menambahkan beberapa kemungkinan yang tidak disebutkan dalam jawaban yang diberikan. Selain itu, beberapa hal telah bergerak sedikit dalam beberapa tahun terakhir, jadi perlu ditekankan bahwa SQL dan NoSQL bergerak lebih dekat satu sama lain.

Salah satu komentator mengemukakan sikap berhati-hati yang bijaksana bahwa "jika data bersifat relasional, gunakan relasional". Namun, komentar itu hanya masuk akal di dunia relasional, di mana skema selalu datang sebelum aplikasi.

DUNIA HUBUNGAN: Data struktur> Tulis aplikasi untuk mendapatkannya
NOSQL DUNIA: Desain aplikasi> Data struktur sesuai

Sekalipun data bersifat relasional, NoSQL masih merupakan opsi. Misalnya, hubungan satu-ke-banyak tidak ada masalah sama sekali dan secara luas dibahas dalam dokumen MongoDB

SOLUSI 2015 UNTUK MASALAH 2010

Sejak pertanyaan ini diposting, ada upaya serius untuk membawa noSQL lebih dekat ke SQL. Tim yang dipimpin oleh Yannis Papakonstantinou di University of California (San Diego) telah mengerjakan FORWARD , sebuah implementasi dari SQL ++ yang dapat segera menjadi solusi untuk masalah yang terus-menerus seperti yang diposting di sini.

Pada tingkat yang lebih praktis, rilis Couchbase 4.0 berarti bahwa, untuk pertama kalinya, Anda dapat melakukan JOIN asli di NoSQL. Mereka menggunakan N1QL mereka sendiri. Ini adalah contoh dari JOINdari tutorial mereka :

SELECT usr.personal_details, orders 
        FROM users_with_orders usr 
            USE KEYS "Elinor_33313792" 
                JOIN orders_with_users orders 
                    ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

N1QL memungkinkan untuk sebagian besar jika tidak semua operasi SQL termasuk penggabungan, penyaringan, dll.

SOLUSI HYBRID BUKAN-SANGAT BARU

Jika MongoDB masih merupakan satu-satunya pilihan, maka saya ingin kembali ke poin saya bahwa aplikasi harus didahulukan dari struktur data. Tidak ada jawaban yang menyebutkan hybrid embedding, di mana sebagian besar data yang dipertanyakan tertanam dalam dokumen / objek, dan referensi disimpan untuk sebagian kecil kasus.

Contoh: dapatkah informasi (selain nama peran) menunggu? dapatkah bootstrap aplikasi lebih cepat dengan tidak meminta apa pun yang belum dibutuhkan pengguna?

Ini bisa menjadi kasus jika pengguna login dan dia perlu melihat semua opsi untuk semua perannya. Namun, pengguna adalah "Insinyur" dan opsi untuk peran ini jarang digunakan. Ini berarti aplikasi hanya perlu menunjukkan opsi untuk seorang insinyur jika dia ingin mengkliknya.

Ini dapat dicapai dengan dokumen yang memberi tahu aplikasi pada awal (1) peran apa yang menjadi milik pengguna dan (2) di mana mendapatkan informasi tentang suatu peristiwa yang terkait dengan peran tertentu.

   {_id: ObjectID(),
    roles: [[“Engineer”, ObjectId()”],
            [“Administrator”, ObjectId()”]]
   }

Atau, bahkan lebih baik, indeks bidang role.name di kumpulan peran, dan Anda mungkin tidak perlu menanamkan ObjectID () juga.

Contoh lain: apakah informasi tentang SEMUA peran yang diminta SEMUA waktu?

Ini juga bisa menjadi kasus bahwa pengguna masuk ke dasbor dan 90% dari waktu melakukan tugas yang terkait dengan peran "Insinyur". Embedding hibrida dapat dilakukan untuk peran tertentu secara penuh dan menyimpan referensi untuk sisanya saja.

{_id: ObjectID(),
  roles: [{name: Engineer”, 
           property1: value1,
           property2: value2
          },   
          [“Administrator”, ObjectId()”]
         ]
}

Menjadi schemaless bukan hanya karakteristik NoSQL, itu bisa menjadi keuntungan dalam hal ini. Ini benar-benar valid untuk menyarangkan berbagai jenis objek di properti "Peran" objek pengguna.

cortopy
sumber
5

dalam hal ketika karyawan dan perusahaan adalah entitas-objek coba gunakan skema berikut:

employee{
   //put your contract to employee
   contracts:{ item1, item2, item3,...}
}

company{
   //and duplicate it in company
   contracts:{ item1, item2, item3,...}
}
Andrei Andrushkevich
sumber