حالا که با روابط «یک به چند» و «چند به یک» آشنا شدیم تنها یک نوع رابطه دیگر باقی می ماند: روابط «چند به چند». تصور کنید که یک وب سایت فروشگاهی داریم؛ در این وب سایت محصولات مختلفی وجود دارد و کاربران می توانند آن ها را خریداری کنند. بنابراین هر کاربر می تواند چندین محصول را بخرد و هر محصول میتواند چندین و چند خریدار داشته باشد (یک مشتری محصولات متعددی را خریده و یک محصول مشتریان متعددی دارد). به چنین رابطه ای، رابطه چند به چند می گوییم. به نظر شما چنین حالتی را چگونه پیاده سازی کنیم؟ در همین ابتدا باید بگویم که در اکثر مواقع روابط چند به چند از reference ها استفاده می کنند چرا که روش بهینه تری برای ذخیره سازی این نوع داده ها است. برای شروع این جلسه پایگاههای داده قبلی خودم را پاک می کنم تا با یک پایگاه داده جدید شروع کنیم.
اول از همه پایگاه داده جدید خودم را تعریف می کنم:
use shop
سپس collection محصولات خودم را با نوشتن یک document جدید ثبت می کنم:
db.products.insertOne({title: "A Book", price: 12.99})
در مرحله بعد یک collection دیگر برای کاربران خود خواهیم داشت:
db.customers.insertOne({name: "Amir", age: 24})
و نهایتا یک collection برای سفارشات:
db.orders.insertOne({productId: ObjectId("5e882f7fdd477cc0371edb93"), customerId: ObjectId("5e882fcf7e199f9e5154bd52")})
در کد بالا id پاس داده شده برای productId همان id محصول ما است که قبلاً در collection خودش وارد کرده بودیم (محصول A Book) و id پاس داده شده به customerId نیز همان id مشتری ما است (مشتری Amir). این روش همان روش دنیای MySQL است. یعنی collection ای (سفارشات) را در میان داده ها داریم که دو collection دیگر (محصولات و مشتریان) را به هم متصل می کند. ما می توانیم در MongoDB به روش دیگری عمل کنیم که فقط با دو collection سر و کار داشته باشیم بنابراین من orders را حذف می کنم:
db.orders.drop()
حالا می توانیم مشتری را به شکل زیر ویرایش کنیم:
db.customers.updateOne({}, {$set: {orders: [{productId: ObjectId("5e882f7fdd477cc0371edb93"), quantity: 2}]}})
سپس با دستور زیر نتیجه را مشاهده می کنیم:
db.customers.findOne()
نتیجه:
"_id" : ObjectId("5e882fcf7e199f9e5154bd52"), "name" : "Amir", "age" : 24, "orders" : [ { "productId" : ObjectId("5e882f7fdd477cc0371edb93"), "quantity" : 2 } ] }
با این حساب id محصول (A Book) را درون آرایه orders در document مشتری قرار داده ایم. این روش، همان روش MongoDB است. البته شما می توانید بگویید که در این برنامه خاص می توانیم از روش embedded documents نیز استفاده کنیم. همانطور که گفتم در اکثر روابط چند به چند بهتر است از reference ها استفاده کنیم (بسته به برنامه شما) اما بله می توانیم در این برنامه که الان نوشته ایم از embedded document ها نیز استفاده کنیم.
برای انجام این کار فرض می کنیم که داده های یک کاربر و سفارشات او را در همان زمان ثبت سفارش گرفته و در یک document ذخیره می کنیم:
db.customers.updateOne({}, {$set: {orders: [{title: "A Book", price: 12.99, qunatity: 2}]}})
با اجرای این دستور، مقدار قبلی برای مشتری ما (Amir) حذف شده و این مقدار جدید در orders قرار می گیرد. همچنین می توانیم نتیجه را با دستور زیر ببینیم:
db.customers.findOne()
نتیجه برگردانده شده:
"_id" : ObjectId("5e882fcf7e199f9e5154bd52"), "name" : "Amir", "age" : 24, "orders" : [ { "title" : "A Book", "price" : 12.99, "qunatity" : 2 } ] }
با این حساب، محصولات خریداری شده توسط کاربر با خود کاربر یکجا ذخیره شده اند. به نظر شما عیب این روش چیست؟ مشکل این روش data duplication است، یعنی تکراری بودن داده ها. فرض کنید که یک کاربر کتاب «شاهنامه» را از فروشگاه ما بخرد، سپس کاربر دیگری نیز آن را بخرد، و همچنین کاربر دیگری و الی آخر. حالا ما هزار کاربر داریم که شاهنامه را خریده اند و هزار بار این داده را برایشان تکرار کرده ایم. من این موضوع را بدون کد و به صورت ساده برایتان نمایش می دهم:
"name": "Amir, "orders" : [ { "title" : "A Book", "price" : 12.99, "qunatity" : 2 } ] "name": "Nastaran, "orders" : [ { "title" : "A Book", "price" : 12.99, "qunatity" : 2 } ] "name": "Abolfazl, "orders" : [ { "title" : "A Book", "price" : 12.99, "qunatity" : 2 } ] "name": "Alireza, "orders" : [ { "title" : "A Book", "price" : 12.99, "qunatity" : 2 } ] "name": "Maryam, "orders" : [ { "title" : "A Book", "price" : 12.99, "qunatity" : 2 } ] "name": "Mohsen, "orders" : [ { "title" : "A Book", "price" : 12.99, "qunatity" : 2 } ]
آیا متوجه مشکل بالا می شوید؟ title و price مرتبا تکرار شده اند. همچنین اگر بخواهید قیمت کتاب (یا اطلاعات دیگر آن) را تغییر دهید باید قیمت را در تمام این داده ها نیز تغییر بدهید. درست است؟ بله و خیر!
در برنامه ای مانند برنامه ما که یک وب سایت فروشگاهی می باشد، نیازی به تغییر داده ها نیست. به طور مثال اگر قرار باشد قیمت کتاب بالاتر برود نیازی نیست که قیمت کتاب را در خرید های گذشته نیز بالاتر ببریم. مگر قرار است مشتریان قبلی قیمت بیشتری بپردازند؟!! بنابراین این مسئله به شما مربوط است که آیا می خواهید داده های شما در کل پایگاه داده به روز رسانی شود و همیشه آخرین نسخه از اطلاعات را داشته باشید یا این که برایتان مهم نیست که داده های قبلی قدیمی باشند؟ در برخی از برنامه ها حتماً لازم است آخرین نسخه داده ها را در سرتاسر پایگاه داده داشته باشیم بنابراین نمی توانیم از چنین روشی استفاده کنیم اما در وب سایت هایی مانند وب سایت های فروشگاهی می توانیم این کار را انجام بدهیم (البته اگر جزئیات ذکر شده برای شما مهم نیست). نکته مهم این است که نمی خواهم با دیدن یک رابطه چند به چند، فکر کنید که حتماً باید از reference ها استفاده کنید یا با دیدن روابط یک به یک تصور کنید که تنها راه درست document های تو در تو هستند. شما باید خودتان کارکرد برنامه را تحلیل کرده و آن را پیاده سازی کنید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.