در قسمت قبل با روابط یک به یک آشنا شدیم و به شما گفتیم که تقریبا همیشه در این نوع روابط از embedded document ها استفاده خواهیم کرد اما می خواهم به شما بگویم که مجبور به انجام این کار نیستید (MongoDB محدودیتی برایتان نمی گذارد).
من در ابتدای این جلسه، این موضوع را توضیح می دهم و سپس وارد مبحث روابط یک به چند می شویم. فرض کنید ما بخواهیم داده های مربوط به افراد و ماشین هایشان را ذخیره کنیم. برای ساده شدن کار، فرض می کنیم که هر فرد فقط می تواند یک ماشین داشته باشد تا رابطه ی یک به یک برقرار شود. من پایگاه داده ی قبلی را مثل همیشه حذف می کنم تا با یک پایگاه داده ی جدید شروع کنیم.
اول از همه باید یک پایگاه داده ی جدید را ایجاد کنیم:
use carData
حالا یک collection و document خواهیم داشت:
db.persons.insertOne({name: "Amir", car: {model: "BMW", price: 40000}})
این روش همان روش جلسه ی قبل است که به طور کل روش بهتری برای روابط یک به یک محسوب می شود اما مسئله ای باقی می ماند.
حالا فرض کنید برنامه ای که ما می خواهیم آن را بنویسیم از نوع برنامه های تحلیل آمار است. در این نوع برنامه ها معمولاً به میانگین تعداد ماشین ها، به میانگین قیمت آن ها و به اطلاعات اینچنینی نیاز خواهیم داشت اما به ندرت پیش می آید که بخواهیم هر فرد و ماشین او را در یک جا نمایش بدهیم. تمرکز ما روی دریافت ماشین ها و انجام تحلیل های آماری روی آن هاست بنابراین نیازی به نوشتن کوئری برای دریافت فرد و ماشین نیست مگر در موارد خاص. در چنین حالتی استفاده از reference ها گزینه ی مناسبی است. چرا؟ به دلیل اینکه اگر از داکیومنت های تو در تو استفاده کنیم برای تحلیل آمار مجبور به دریافت کل document هستیم یعنی اطلاعات هر فرد را به همراه ماشین و اطلاعات دیگر دریافت خواهیم کرد در حالی که فقط به اطلاعات آن ماشین نیاز داریم. در واقع در چنین برنامهای اگر از متد document های تو در تو استفاده کنیم باری اضافی به برنامه ی خود تحمیل کرده ایم چرا که اولاً بسیاری از داده های ناخواسته را از پایگاه داده دریافت کرده ایم و دوما باید این داده های ناخواسته را فیلتر کنیم تا داده های مورد نظر از آن ها استخراج شود.
با این حساب من document قبلی خود را پاک می کنم:
db.persons.deleteMany({})
سپس یک document جدید را می نویسم:
db.persons.insertOne({name: "Amir", age: 24, salary: 3000})
سپس برای ماشین می گوییم:
db.cars.insertOne({model: "BMW", price: 40000, owner: ObjectId("5e881f45633797823c711718")})
id ای که برای این قسمت وارد کرده ام همان id ای است که از دستور قبلی گرفتم (یعنی آیدی مربوط به نام Amir). به این صورت اگر بخواهیم تحلیل های آماری را روی افراد یا ماشین ها انجام بدهیم، نیازی نیست که همه را با هم دریافت کرده و سپس داده های مورد نظر خود را از آن ها استخراج کنیم. ما می توانیم تحلیل های آماری را روی ماشین ها انجام دهیم و اگر قصد پیدا کردن صاحب ماشین را داشتیم با یک کوئری جداگانه صاحبان ماشین را نیز پیدا کنیم.
یکی از مثال های ساده ی روابط یک به چند، انجمن های آنلاین هستند. به طور مثال در قسمت روکسوکیو (مرجع پرسش و پاسخ برنامه نویسان و گرافیست ها) که مربوط به پرسش و پاسخ برنامه نویسی و گرافیک است هر فرد یک سوال می پرسد و ممکن است چندین جواب بگیرد. بنابراین هر سوال بیشتر از یک جواب دارد که خود یک رابطه ی یک به چند را تشکیل می دهد. برای شروع پایگاه های داده ی قبلی را حذف کنید تا از صفر شروع کنیم. ابتدا پایگاه داده ی جدید را می سازم:
use support
سپس یک ردیف جدید را می سازیم:
db.questionThreads.insertOne({creator: "Amir", question: "How does that all work?", answers: ["q1a1", "q1a2"]})
در اینجا creator یعنی کسی که سوال را پرسیده است، question هم عنوان سوال است. البته من می خواهم در اینجا از reference ها استفاده کنم به همین دلیل در قسمت answers (پاسخ ها) از آیدی پاسخ ها استفاده کرده ام و خود پاسخ را درون این document نگذاشته ام.
حالا با دستور زیر می توانیم نتیجه را مشاهده کنیم:
db.questionThreads.findOne()
نتیجه:
"_id" : ObjectId("5e8821d6158b40c7efee0cea"), "creator" : "Amir", "question" : "How does that all work?", "answers" : [ "q1a1", "q1a2" ] }
با این حساب باید یک collection دیگر به نام Answers داشته باشیم که پاسخ ها را داشته باشند. ما در قسمت answers دو id داریم بنابراین نیاز به دو پاسخ داریم:
db.answers.insertMany([{_id: "q1a1", text: "It works like that."},{_id: "q1a2", text: "Thanks!"}])
با این کار دو پاسخ را یک جا وارد کرده ایم (یادتان باشد که در insertMany حتما باید document ها را درون آرایه پاس بدهید). برای اینکه پاسخ ها را ببینیم از دستور زیر استفاده می کنیم:
{ "_id" : "q1a1", "text" : "It works like that." } { "_id" : "q1a2", "text" : "Thanks!" }
قرار است ما در این برنامه برای هر سوال چندین جواب دریافت کنیم. با این حساب هنوز هم بیشتر از یک درخواست یا کوئری لازم داریم تا این کار را انجام بدهیم بنابراین در چنین حالتی باز هم بهتر است از document های تو در تو استفاده کنیم. برای شروع دوباره اول از همه questionThreads را خالی می کنم:
db.questionThreads.deleteMany({})
سپس ردیف جدید را با روش embedded documents می نویسیم:
db.questionThreads.insertOne({creator: "Amir", question: "How does that work?", answers: [{text: "Like that."}, {text: "Thanks!"}]})
این روش، روش عاقلانه تری نسبت به reference ها است. توجه کنید که روابط داده ها (یک به یک یا یک به چند) تعیین کننده ی نوع پایگاه داده ی شما نیستند بلکه کلیتی از کار پایگاه داده را در اختیارتان قرار می دهند تا بتوانید بر اساس نیاز برنامه، خودتان طرح آن را بریزید. در برنامه ی ما که یک انجمن پرسش و پاسخ است معمولاً همیشه پاسخ ها به همراه سوال دریافت می شوند (هیچ انجمنی نیست که پاسخ ها و سوالات را در صفحات جداگانه قرار بدهد) همچنین از آنجایی که معمولاً هر سوال بیشتر از ۱۰ یا ۲۰ یا 30 جواب ندارد (چه برسد به اینکه یک سوال ده ها هزار پاسخ بگیرد) نیازی به نگرانی در مورد پاسخ های دریافتی نیست و هیچ وقت به محدودیت ۱۶ مگابایتی نمی رسند.
البته در مواقعی استفاده از reference ها بهتر است و همانطور که گفتم تمام این مسائل به نوع کارکرد برنامه ی شما مربوط می شود. به طور مثال پایگاه داده ی کشوری را در نظر بگیریم که قرار است تمام افراد را بر اساس شهری که در آن هستند به همراه نام، نام خانوادگی و اطلاعاتی دیگر ذخیره کند (مثل ثبت احوال). در چنین پایگاه داده ای اسامی شهرها یک طرف و افرادی که در آن زندگی می کنند و در طرف دیگر قرار می گیرد اما استفاده از document های تو در تو کار اشتباهی است. اولاً در بسیاری از اوقات می خواهیم عملیات تحلیل آمار را فقط روی افراد انجام دهیم یا در برخی اوقات می خواهیم اطلاعاتی را از شهرها دریافت کنیم (مثلاً تعداد شهرهای یک استان). در چنین حالتی اگر از document های تو در تو استفاده کرده باشیم باید تمام داده ها را دریافت کرده و سپس از بین تمام این داده ها موارد مورد نظر خود را استخراج کنیم. دوما از آنجایی که تعداد افراد موجود در یک کشور بسیار زیاد است ممکن است به محدودیت ۱۶ مگابایتی برسیم.
بنابراین نهایتا باید به نحوه ی کار برنامه ی خود توجه کنید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.