فیلترها و ایندکس‌های partial

Partial Filters and Indexes

24 اردیبهشت 1401
درسنامه درس 53 از سری دوره جامع آموزش MongoDB
MongoDB: فیلتر ها و ایندکس های partial (قسمت 55)

یکی از روش های نوشتن ایندکس ها، نوشتن ایندکس های partial (به معنی «قسمتی» یا «جزئی») است. مثلا در پایگاه داده ما افراد 20 ساله و 30 ساله و پیر تر وجود دارند. حالا فرض کنید که برنامه ای داریم و کار این برنامه مشخص کردن سن افراد در زمان بازنشستگی است. در چنین حالتی معمولا به دنبال افراد بالای 60 سال می گردیم. به همین خاطر تنظیم index روی فیلد dob.age کار عاقلانه ای است چرا که ما فقط قسمت کوچکی از پایگاه داده را می خواهیم اما مشکل اینجاست که با این کار داده های زیادی را روی index خود خواهیم داشت که اصلا کوئری نمی خورند (به دنبالشان نمی گردیم). اگر فیلد age را index کنیم، تمام سن ها index می شوند (از 20 سال تا 60 سال و بالاتر) اما ما در برنامه فرضی خودمان فقط به دنبال افراد بالای 60 سال خواهیم بود و تمام داده های دیگر در لیست ایندکس بلا استفاده باقی خواهند ماند.

این مسئله از نظر سرعت برنامه ما را درگیر نمی کند و index شما و کوئری هایتان پرسرعت و بهینه خواهند بود اما از نظر فضا، لیست ایندکس بزرگ تر از حد نیاز خواهد بود و فضای سرور را اشغال می کند. همچنین برخی دیگر از کوئری ها مانند insert باید با هر بار وارد کردن داده جدید، لیست index را نیز مرتب کنند بنابراین هر چه لیست ایندکس بزرگ تر باشد، ممکن است کوئری های دیگر کُند تر شوند. بنابراین زمانی که می دانید از قسمتی از داده های یک فیلد آنچنان استفاده ای نخواهید داشت و اگر هم استفاده ای باشد با collection scan مشکلی ندارید، می توانید از قابلیت partial indexes یا ایندکس های جزئی استفاده کنید. در این نوع از ایندکس ها به جای اینکه کل یک فیلد را درون لیست ایندکس ها بگذاریم، قسمتی از آن را index می کنیم. بنابراین می توانیم مثالی که گفتم را پیاده سازی کنیم. در قدم اول نگاهی به index های contacts می اندازیم:

use contactData
db.contacts.getIndexes()

نتیجه اجرای کوئری بالا، به شکل زیر است:

{                                         
         "v" : 2,                          
         "key" : {                         
                 "_id" : 1                 
         },                                
         "name" : "_id_",                  
         "ns" : "contactData.contacts"     
 },                                        
 {                                         
         "v" : 2,                          
         "key" : {                         
                 "dob.age" : 1,            
                 "gender" : 1              
         },                                
         "name" : "dob.age_1_gender_1",    
         "ns" : "contactData.contacts"     
 }                                         

بنابراین بهتر است کل ایندکس ها را حذف کنیم تا دوباره ایندکس جزئی خودمان را تعریف کنیم. برای این کار می گوییم:

db.contacts.dropIndexes()

این دستور بدین شکل (بدون آرگومان) تمام ایندکس های یک کالکشن را حذف می کند. البته ایندکس پیش فرض MongoDB که برای id_ می باشد حذف شدنی نیست بنابراین با اجرای دستور بالا پیام زیر را می گیریم:

"nIndexesWas" : 2,                               
"msg" : "non-_id indexes dropped for collection",
"ok" : 1                                         

یعنی تمام ایندکس ها به جز id_ حذف شدند. حالا برای تعریف یک partial index به شکل زیر عمل می کنیم:

db.contacts.createIndex({"dob.age": 1,}, {partialFilterExpression: {gender: "male"}})

در آرگومان اول که برابر Dob.age است مشخص کرده ایم که ایندکس ما باید برای فیلد age تعریف شود اما در آرگومان دوم (partialFilterExpression) یک شرط برای آن گذاشته ایم و گفته ایم که فقط برای افرادی که مرد (male) باشند. توجه کنید که ایندکس فقط برای فیلد age است و برای gender نخواهد بود. gender یک شرط است تا به جای اینکه تمام فیلد های age را در لیست ایندکس داشته باشیم، فقط آن هایی را داشته باشیم که مربوط به مرد ها هستند.

برای مثال، در برنامه ما که به دنبال افراد بالای 60 سال هستیم، به شکل زیر عمل می کنیم (این کوئری را اجرا نکنید):

db.contacts.createIndex({"dob.age": 1,}, {partialFilterExpression: {"dob.age": {$gt: 60}}})

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

db.contacts.find({"dob.age": {$gt: 60}}).pretty()

با اجرای این دستور نکته جالبی را خواهیم دید. این دستور می گوید افراد بالای 60 سال را پیدا کن و اصلا با جنسیت افراد کاری ندارد بنابراین در نتایج خود یک یا چند female را خواهیم دید. تا اینجا مشکلی نیست اما سوالی به وجود می آید: این مسئله از نظر index ما چطور خواهد بود؟ برای درک این موضوع باید کوئری را Explain کنیم:

db.contacts.explain().find({"dob.age": {$gt: 60}})

اگر به گزارش برگردانده شده نگاه کنیم، می بینیم که یک collection scan اتفاق افتاده است و از index ما خبری نیست:

"winningPlan" : {
    "stage" : "COLLSCAN",

چرا؟ اولویت اصلی MongoDB پیدا کردن داده ها است و می داند که index شما فقط برای یک gender خاص تعریف شده است (male ها) بنابراین اگر از آن استفاده کند، female ها از نتایج حذف می شوند و داده هایی که واقعا باید برگردانده شوند (کوئری ما کاری به جنسیت نداشت) برگردانده نخواهند شد. اگر بخواهید از آن index استفاده کنید باید gender را نیز فیلتر کنید:

db.contacts.explain().find({"dob.age": {$gt: 60}, gender: "male"})

حالا گزارش این کوئری به ما می گوید که IXSCAN (همان index scan) اتفاق افتاده است بنابراین از ایندکس هایمان استفاده کرده ایم

سوال: با این حساب تفاوت partial index و compound index چیست؟

پاسخ: در partial index ها سایز لیست ایندکس کوچک تر است و فقط age هایی که برای افراد مذکر است در این لیست ذخیره شده اند. بدین ترتیب فضای روی سرور ذخیره شده و همچنین کوئری های write مانند insert سرعت بهتری خواهند داشت (مثلا اگر یک کاربر Female اضافه کنید، اصلا نیازی نیست به ایندکس اضافه شود بنابراین سرعت بالاتری خواهیم داشت).

بنابراین استفاده از partial index ها زمانی به درد می خورد که کوئری های خاصی (مثلا افراد بالای 60 سال که مذکر هستند) را مرتبا اجرا کنیم (اقتضای برنامه ما این باشد). قانون کلی این است که اگر MongoDB احساس کند که داده های برگردانده شده از داده های درون index بیشتر است، از index شما استفاده نخواهد کرد.

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

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

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