کنترل دستی cursor و مرتب کردن داده‌ها

Manual Cursor Control and Data Sorting

20 اردیبهشت 1401
درسنامه درس 38 از سری دوره جامع آموزش MongoDB
MongoDB: کنترل دستی cursor و مرتب کردن داده ها (قسمت 40)

کنترل دستی cursor

همانطور که در انتهای جلسه قبل توضیح دادم Cursor ها حاوی اطلاعاتی از داده ها هستند اما خود داده ها را ندارند. ما می خواهیم در این جلسه به صورت عملی و جزئی تری با آن ها کار کنیم. قبلا هم گفته بودم که Driver ها به جای برگرداندن 20 سند از یک کالکشن، شیء cursor را برمی گردانند که باید با آن کار خاصی را انجام بدهیم. مثلا:

use movieData
db.movies.find().next()

تابع next به ما اجازه می داد که به داده بعدی در cursor برویم. با اجرای دستور بالا، برای من سریال arrow با id برابر 4 برگردانده می شود:

"id" : 4

حالا اگر دوباره دستور next را اجرا کنم، باز هم همان سریال و همان id به من برگردانده می شود. به نظر شما چرا این اتفاق می افتد؟ به دلیل اینکه کوئری ما در shell از ابتدا و بدون توجه به موارد قبلی اجرا می شود و مانند driver ها نیست. برای اینکه عملیات driver ها را در Shell شبیه سازی کنیم باید کوئری را در یک ثابت ذخیره نماییم:

const dataCursor = db.movies.find()

با اجرای این دستور درون shell چیزی نمی بینید اما cursor شما درون dataCursor ذخیره شده است و حالا می توانیم next را روی آن صدا بزنیم:

dataCursor.next()

در بار اولی که این کد را اجرا می کنم، سریال arrow با آیدی 4 را می گیرم اما در دفعه بعدی True Detective را با آیدی 5 دریافت می کنم. اگر همینطور به صدا زدن next ادامه بدهیم، آیدی های بعدی را دریافت خواهیم کرد. حالا اگر واقعا بخواهیم 20 سند اول را دریافت کنیم، باید چه کار کنیم؟ این مسئله بستگی به driver شما دارد اما به طور خلاصه باید از یک حلقه استفاده کنید که روی cursor گردش کند. ما فعلا در Shell هستیم که بر پایه جاوا اسکریپت است بنابراین می توانیم از حلقه forEach استفاده نماییم:

dataCursor.forEach(doc => {printjson(doc)})

یادتان باشد که forEach در کد بالا بین تمام سند هایی گردش می کند که درون cursor ما (dataCursor) باشند. من دو بار next را روی cursor صدا زده ام و سریال های arrow و True Detective را دریافت کرده ام بنابراین از 240 سریال، 2 عدد را کم می کنیم (حلقه فقط روی موارد باقی مانده در Cursor گردش می کند نه اینکه از صفر شروع کند) که می شود 238. یعنی با اجرای کد بالا 238 سریال درون shell برایتان چاپ می شود. حالا اگر دوباره next را صدا بزنیم چه می شود؟

dataCursor.next()

از آنجایی که دیگر سندی در cursor ما باقی نمانده است خطای زیر را می گیریم:

[js] Error: error hasNext: false

خصوصیت hasNext روی false است یعنی دیگر سندی در cursor باقی نمانده است. شما می توانید این موضوع را به صورت دستی نیز چک کنید:

dataCursor.hasNext()

این تابع یکی از مقادیر false یا True را برمی گرداند.

ترتیب داده های دریافتی

مسئله بعدی در مورد مرتب کردن داده های دریافتی از Cursor است. زمانی که ما روی cursor گردش کرده و داده ها را می گیریم، این داده ها ترتیب خاصی ندارند اما می توانیم با استفاده از متد sort آن ها را بر اساس ترتیب خاصی مشخص کنیم. مثلا اگر بخواهیم سریال ها را بر اساس rating (امتیاز منتقدان) مرتب کنیم، می گوییم:

db.movies.find().sort({"rating.average": -1}).pretty()

همانطور که می بینید Sort یک شیء را به عنوان آرگومان خود می گیرد که فیلد تعیین کننده برای ترتیب دهی اسناد را مشخص می کند. من rating.average را انتخاب کرده ام و به آن مقدار 1- را داده ام. در این مورد باید توجه کنید که 1- یعنی نزولی (descending - از زیاد به کم نزول می کنیم) و 1+ یعنی صعودی (ascending - از کم به زیاد صعود می کنیم). شما باید یکی از این دو عدد را مشخص کنید.

همچنین برخی اوقات چند سریال دقیقا یک rating را دارند. در چنین مواقعی می توانیم یک معیار دوم برای مرتب سازی داده ها بدهیم. مثلا من در کوئری زیر می گویم اگر rating ها یکی بود، آن سریالی را اول بیاور که در آن runtime (زمان پخش هر قسمت) بیشتر باشد:

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

این کوئری ابتدا سریال های دارای rating پایین تر را می آورد (1+ یعنی صعودی - از کم به زیاد) و اگر دو rating با هم برابر بودند، سریالی ابتدا می آید که runtime بیشتری داشته باشد (1- یعنی نزولی - از زیاد به کم). بنابراین اجازه دارید هر تعدادی از این معیار ها که می خواهید، از هر فیلدی که باشد، تعریف کنید.

همچنین شما می توانید برخی از عناصر را skip کنید (یعنی از آن ها رد شوید و نادیده بگیرید). احتمالا می گویید چرا کسی باید داده ها را نادیده بگیرد؟ فرض کنید سایت شما pagination (صفحه بندی مطالب سایت - مثلا در نتایج جست و جو در گوگل که 10 نتیجه اول و سپس 10 نتیجه دوم و الی آخر را داریم) دارد و در هر صفحه 10 پست نمایش داده می شود. حالا اگر کاربر در صفحه دوم باشد، ما می خواهیم 10 پست اول را نادیده بگیریم. برای این کار به راحتی می گوییم:

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

با اجرای این دستور، نتایج ما در ابتدا بر اساس rating و runtime مرتب شده و سپس 10 مورد اول نادیده گرفته می شود (نتایج از مورد 11 به بعد شروع می شود).

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

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

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