تابع limit و استفاده از projection در MongoDB

Limit Function and Using Projection in MongoDB

20 اردیبهشت 1401
درسنامه درس 39 از سری دوره جامع آموزش MongoDB
MongoDB: تابع limit و استفاده از projection (قسمت 41)

تابع limit و کاربرد آن

در جلسه قبل با دستورات مختلفی از قبیل sort و skip آشنا شدیم. یکی دیگر از توابعی که باید با آن آشنا بشویم، تابع limit است. تابع limit (به معنی «محدودیت») مسئول محدود کردن تعداد سند هایی است که یک cursor دریافت می کند. قطعا زمانی که تعداد اسناد درون cursor محدود شود، مثلا فقط 20 مورد باشد، ما نیز به همان 20 مورد دسترسی خواهیم داشت و برای موارد بعدی باید دوباره کوئری بزنیم. به مثال زیر توجه کنید:

db.movies.find().sort({"rating.average": +1, runtime: -1}).skip(100).limit(10).pretty()

این کوئری 100 مورد اول را skip می کند (نادیده می گیرد) و سپس فقط 10 مورد بعدی را دریافت می کند. اگر کوئری بالا را بدون limit صدا بزنید، عبارت Type "it" for more را در انتهای نتایج دریافت می کنید چرا که Cursor شما تمام اطلاعات را دریافت کرده است بنابراین با نمایش 20 عدد از آن ها به انتهایش نرسیده ایم اما زمانی که کوئری بالا را (با limit) اجرا کنید دیگر Type "it" for more را نمی بینید. چرا؟ به دلیل اینکه کل cursor ما 10 سند را برمی گرداند و shell در هر کوئری Find، بیست نتیجه را نشان می دهد بنابراین 10 مورد مشخص شده نمایش داده می شوند و دیگر قرار نیست چیزی نمایش داده شود. آیا مثال pagination از جلسه قبل را به یاد دارید؟ من در آن جلسه گفتم که فرض کنید سایت شما pagination (صفحه بندی مطالب سایت - مثلا در نتایج جست و جو در گوگل که 10 نتیجه اول و سپس 10 نتیجه دوم و الی آخر را داریم) دارد و در هر صفحه 10 پست نمایش داده می شود. حالا اگر کاربر در صفحه دوم باشد، ما باید 10 پست اول را نادیده بگیریم. برای انجام این کار skip کردن 10 نتیجه اول کافی نیست. چرا؟ اگر فقط 10 نتیجه اول را Skip کنیم یعنی از نتیجه 11 تا آخرین نتیجه در کالکشن (حتی اگر نتیجه هزارم باشد) را دریافت می کنیم! بنابراین در مثال pagination حتما باید Skip و limit را با هم داشته باشیم.

سوال بعدی ما اینجاست که آیا صدا زدن skip یا limit یا sort دارای ترتیب خاصی است؟ مثلا به کوئری زیر نگاه کنید:

db.movies.find().skip(30).sort({"rating.average": +1, runtime: -1}).limit(10).pretty()

ما در این کوئری ابتدا 30 سند را نادیده گرفته ایم و سپس آن ها را Sort کرده ایم بنابراین نتایج ما با کوئری قبلی تفاوت دارد. درست است؟ خیر! احتمالا تصور کنید که اگر داده ها را sort کنیم (به ترتیب خاصی لیست کنیم) و سپس 30 نتیجه اول آن را نادیده بگیریم یک لیست خاص می گیریم که با کوئری دوم (ابتدا نادیده گرفتن 30 سند تصادفی و سپس مرتب کردن باقی مانده داده ها) کاملا متفاوت است. این تصور کاملا اشتباه است. شما اجازه دارید کوئری بالا را به هر شکل دلخواهی بنویسید؛ چه limit یا skip را در ابتدا بیاورید و چه در انتها، فرقی نخواهد کرد. چرا؟ به دلیل اینکه MongoDB خودش می داند که ابتدا باید Sort را انجام دهد، سپس skip و limit کند بنابراین نباید نگران ترتیب این دستورات باشید.

شکل دهی به داده ها با Projection

در حال حاضر با هر درخواست ما برای هر سریال، تمام داده های مربوط به آن سریال دریافت می شود اما در اکثر اوقات در برنامه های واقعی نیاز به این تعداد داده نداریم. ما در اکثر اوقات داده هایی بسیار محدود تر را می خواهیم تا فقط کلیات مربوط به یک سریال یا یک محصول را در سایت خود نمایش بدهیم. مثلا فرض کنید که من می خواهم در سایت خودم فقط داده های زیر را برای هر سریال دریافت کرده و نمایش بدهم:

  • نام سریال (فیلد name)
  • ژانر سریال (فیلد genres)
  • مدت زمان هر قسمت (فیلد runtime)
  • امتیاز منتقدین (فیلد rating)

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

db.movies.find({}, {name: 1, genres: 1, runtime: 1, rating: 1}).pretty()

همانطور که می دانید projection با ذکر نام فیلد های مورد نظر و دادن عدد 1 برقرار می شود. کوئری بالا می گوید داده های دریافتی من فقط باید شامل name و genres و runtime و rating باشند (اگر بخواهیم همه داده ها را به جز داده های خاصی داشته باشیم، باید نام فیلد های ناخواسته را بیاوریم و عدد 0 را به آن ها بدهیم اما در اینجا چنین چیزی را نمی خواهیم). با اجرای دستور بالا نتایجی می گیریم که ساختار زیر را دارند:

        "_id" : ObjectId("5e8d82a8fed9c9b1e19c6518"), 
        "name" : "True Blood",                        
        "genres" : [                                  
                "Drama",                              
                "Romance",                            
                "Supernatural"                        
        ],                                            
        "runtime" : 60,                               
        "rating" : {                                  
                "average" : 8                         
        }                                             
}                                                     
                                                      
        "_id" : ObjectId("5e8d82a8fed9c9b1e19c6519"), 
        "name" : "The Last Ship",                     
        "genres" : [                                  
                "Drama",                              
                "Action",                             
                "Thriller"                            
        ],                                            
        "runtime" : 60,                               
        "rating" : {                                  
                "average" : 7.8                       
        }                                             
}                                                     
                                                      
        "_id" : ObjectId("5e8d82a8fed9c9b1e19c651a"), 
        "name" : "The 100",                           
        "genres" : [                                  
                "Action",                             
                "Adventure",                          
                "Science-Fiction"                     
        ],                                            
        "runtime" : 60,                               
        "rating" : {                                  
                "average" : 7.9                       
        }                                             
}                                                     

بنابراین کد ما کار می کند. البته توجه داشته باشید که id استثناء است و همیشه در نتایج برگردانده شده خواهد بود مگر آنکه به صورت دستی آن را غیر فعال کنید. مثلا:

db.movies.find({}, {name: 1, genres: 1, runtime: 1, rating: 1, _id:0}).pretty()

با این کار id از داده هایمان حذف می شود:

        "name" : "The 100",
        "genres" : [
                "Action",
                "Adventure",
                "Science-Fiction"
        ],
        "runtime" : 60,
        "rating" : {
                "average" : 7.9
        }
}

نکته: projection به داده های اصلی در پایگاه داده دست نزده و آن ها را تغییر نمی دهد بلکه فقط قسمت های مورد نظر شما از آن داده ها را گرفته و به شما برمی گرداند بنابراین چیزی از داده های اصلی حذف نمی شود.

البته projection می تواند برای embedded document ها نیز انجام شود. مثلا می توان گفت برای rating فقط مقدار فیلد average را می خواهیم (rating ما فقط یک فیلد دارد که آن هم average است بنابراین در عمل چیزی را از آن حذف نکرده ایم و این مثال برای یادگیری شما است):

db.movies.find({}, {name: 1, genres: 1, runtime: 1, "rating.average": 1, _id:0}).pretty()

همانطور که گفتم با این کار تغییری در داده های برگشتی نمی بینیم چرا که rating فقط یک فیلد داشت و آن هم average بود اما اگر فیلد های دیگری درون rating داشتیم، فقط average را دریافت می کردیم.

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

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

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