آشنایی با ایندکس‌های چندکلیدی - multi-key indexes

Familiarity with Multi-Key Indexes

24 اردیبهشت 1401
درسنامه درس 57 از سری دوره جامع آموزش MongoDB
MongoDB: آشنایی با ایندکس های چند کلیدی - multi-key indexes (قسمت 59)

تا این قسمت با انواع مختلفی از ایندکس ها آشنا شده ایم اما هنوز انواع دیگری باقی مانده است. در این جلسه می خواهیم در رابطه با ایندکس های چند کلیدی یا multi-key indexes صحبت کنیم. برای شروع این جلسه مطمئن شوید که کوئری زیر را اجرا می کنید تا کالکشن contacts (کالکشن 5000 هزار سندی که import کرده بودیم) حذف شود:

db.contacts.drop()

با اجرای این کوئری، مقدار true به شما نمایش داده می شود. سپس این کالکشن را با مقادیر جدید می سازیم:

db.contacts.insertOne({name: "Amir", hobbies: ["Programming", "Gym"], addresses: [{street: "Main street"}, {street: "Second Street"}]})

با این کار کاربر جدید ما وارد کالکشن جدید می شود. برای مشاهده آن باید کوئری findOne را اجرا کنیم:

db.contacts.findOne()

با اجرای کوئری زیر کاربر ما نمایش داده می شود:

"_id" : ObjectId("5ebcf2e986cc54399e6692ef"),
 "name" : "Amir",                             
 "hobbies" : [                                
         "Programming",                       
         "Gym"                                
 ],                                           
 "addresses" : [                              
         {                                    
                 "street" : "Main street"     
         },                                   
         {                                    
                 "street" : "Second Street"   
         }                                    
 ]                                            

همانطور که می بینید این کاربر hobbies را به صورت یک آرایه ساده دارد که دو عضو رشته ای در آن است. سپس در قسمت Addresses که یک آرایه است دو embedded document دارد. من برای ساده شدن کار فقط مقدار رشته ای را به آن ها داده ام. حالا سوال جالبی از شما دارم: آیا می توانیم کل قسمت hobbies را index کنیم؟ ما می دانیم که hobbies یک آرایه است اما آیا این کار ممکن است؟ بیایید امتحان کنیم:

db.contacts.createIndex({hobbies: 1})

با اجرای این کد هیچ خطایی نمی گیریم. حتی می توانیم از این به بعد به شکل زیر از آن استفاده کنیم:

db.contacts.find({hobbies: "Gym"}).pretty()

با اجرای کوئری بالا Amir برایمان برگردانده می شود و چیز عجیبی نداریم اما اگر آن را به explain اجرا کنیم، در گزارش خود نکته جالبی را می بینیم:

db.contacts.explain("executionStats").find({hobbies: "Gym"})

در گزارش برگردانده شده قسمت زیر مشخص است:

"isMultiKey" : true,

یعنی ایندکس ما از نوع multi-key است. چرا؟ به دلیل اینکه ایندکس خود را روی آرایه ای از مقادیر مختلف قرار داده ایم. در اصل ایندکس های multi-key نیز مانند ایندکس های معمولی اجرا می شوند اما از نظر نحوه ذخیره شدن، کمی متفاوت هستند. در مثال خودمان MongoDB تمام عناصر داخل آرایه hobbies را برداشته و به صورت جداگانه ذخیره می کند. بنابراین باید بدانید که در اکثر مواقع ایندکس های multi-key از ایندکس های single field بزرگ تر هستند. مثلا اگر هر آرایه hobbies دقیقا 4 عنصر مختلف داشته باشد و 1000 نفر را در کالکشن خود داشته باشیم، لیست ایندکس ها شامل 4 هزار عنصر خواهد بود.

حالا باید یک ایندکس دیگر را برای addresses تعریف کنیم:

db.contacts.createIndex({addresses: 1})

نتیجه مثل همیشه و بدون مشکل برایمان نمایش داده می شود:

"createdCollectionAutomatically" : false,
 "numIndexesBefore" : 2,                  
 "numIndexesAfter" : 3,                   
 "ok" : 1                                 

تا اینجا می دانیم که فیلد addresses که یک آرایه حاوی چندین embedded document است، دارای ایندکس شده است اما نمی دانیم که چطور کار می کند بنابراین برای پاسخ به این سوال باید متد explain را باری دیگر اجرا کنیم. یادتان باشد که برای دریافت جزئیات حتما executionStats را به عنوان آرگومان به explain پاس بدهید:

db.contacts.explain("executionStats").find({"addresses.street": "Main Street"})

ما در این کوئری به دنبال آدرس هایی هستیم که در آن ها street برابر Main Street باشد. نکته عجیب اینجاست که در گزارش ارسال شده برای ما winningPlan از نوع COLLSCAN است:

"winningPlan" : {
"stage" : "COLLSCAN",
// بقیه کد ها //

به نظر شما چرا از ایندکس ما استفاده نشده است؟ مگر ما برایش ایندکس را تعریف نکرده بودیم؟ مسئله اینجاست که ما ایندکس را روی addresses تعریف کرده ایم بنابراین ایندکس ما کل document را در خود دارد نه فیلد های آن را! بنابراین اینطور نیست که MongoDB اعضای یک آرایه را استخراج کرده و سپس تک تک key ها و value های آن را نیز استخراج کند. ایندکس ما فقط زمانی مورد استفاده قرار می گیرد که به دنبال کل عضو بگردیم. مثلا:

db.contacts.explain("executionStats").find({addresses: {street: "Main Street"}})

حالا در گزارش دریافتی می بینیم که از index scan استفاده شده است:

"stage" : "IXSCAN"

احتمالا با خودتان می گویید اگر هدف اصلی ما یکی از فیلد های داخل این document ها باشد چطور؟ آیا راهی نداریم که به این موارد درون embedded document دسترسی داشته باشیم؟ یک راه ساده وجود دارد! ما می توانیم ایندکس را مستقیما برای فیلد street درون addresses تعریف کنیم. به طور مثال:

db.contacts.createIndex({"addresses.street": 1})

این مقدار نیز یک ایندکس multi-key است و تفاوتی با بقیه ایندکس های multi-key ندارد اما این بار به جای تعریف کردن آن برای کل سند، آن را برای یک فیلد خاص تعریف کرده ایم. حالا بیایید دوباره کوئری قبلی خود را اجرا کنیم تا نتیجه را مشاهده کنیم:

db.contacts.explain("executionStats").find({addresses: {street: "Main Street"}})

در گزارش برگردانده شده هم مشخص می شود که ما این بار از index scan استفاده کرده ایم و این اسکن از نوع multi-key بوده است:

"stage" : "IXSCAN"
......
"isMultiKey" : true,

البته توجه داشته باشید که استفاده از ایندکس های multi-key معمولا باعث کاهش سرعت دستورات write می شود چرا که به ازای هر سند اضافه شده (write شده) تمام این ایندکس های multi-key باید به روز رسانی شوند. مثلا اگر یک سند با 10 مقدار مختلف برای آرایه addresses اضافه کنید، تمام این 10 مقدار باید وارد لیست ایندکس ها شوند. بنابراین با احتیاط کامل از آن ها استفاده کنید.

نکته دیگری را باید به یاد داشته باشید. ما می توانیم یک multi-key index را به عنوان بخشی از یک compound index تعریف کنیم:

db.contacts.createIndex({name: 1, hobbies: 1})

اما نمی توانید compound index خود را مخلوطی از 2 یا چند multi-key index تعریف کنید. مثلا نمی توان گفت:

db.contacts.createIndex({addresses: 1, hobbies: 1})

با اجرای این کوئری خطای زیر را می گیرید:

"ok" : 0,                                                       
 "errmsg" : "cannot index parallel arrays [hobbies] [addresses]",
 "code" : 171,                                                   
 "codeName" : "CannotIndexParallelArrays"                        

یعنی نمی توانید آرایه های موازی را با هم ایندکس کنید. بنابراین محدودیت multi-key indexes در compound indexes فقط یک عدد است.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش MongoDB توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مقالات مرتبط
آخرین سوالات کاربران
5451218 در 3 سال قبل پرسیده:
ما را دنبال کنید
اینستاگرام روکسو تلگرام روکسو ایمیل و خبرنامه روکسو