آشنایی با اپراتورهای منطقی یا logical در MongoDB

Logical Operators in MongoDB

19 اردیبهشت 1401
درسنامه درس 33 از سری دوره جامع آموزش MongoDB
MongoDB: آشنایی با اپراتور های منطقی یا logical (قسمت 35)

در این جلسه می خواهیم با برخی از اپراتورهای منطقی در MongoDB آشنا شویم. این اپراتورها and$ و not$ و nor$ و or$ هستند. اگر تا اینجا نیز به همراه من پیش آمده باشید فایل مربوط به فیلم ها را در پایگاه داده ی خود دارید. حالا فرض کنید ما می خواهیم تمام document هایی را پیدا کنیم که rating در آن ها کمتر از 5 یا بیشتر از 9.3 باشد. به عبارتی فیلم هایی را می خواهیم که بد یا بسیار عالی باشند. در حالت عادی و بدون استفاده از اپراتورهای منطق باید دو دستور جداگانه ی زیر را اجرا کنیم:

db.movies.find({"rating.average": {$lt: 5}}).pretty()

این دستور تمام سریال هایی با rating کمتر از 5 را به ما می دهد که دو سریال هستند. اگر می خواهید بدانید چند آیتم را در یک کوئری داریم می توانید به جای pretty از count استفاده کنید:

db.movies.find({"rating.average": {$lt: 5}}).count()

خروجی این دستور عدد 2 خواهد بود. این کوئری اول ما است و برای پیدا کردن سریال هایی با rating بیشتر از 9.3 نیز می گوییم:

db.movies.find({"rating.average": {$gt: 9.3}}).count()

اما اگر بخواهیم هر دو را با هم و در یک کوئری بنویسیم باید از اپراتور منطقی or$ استفاده کنیم. برای انجام این کار به جای شروع با فیلد مورد نظر (rating.average) باید مستقیما با اپراتور or$ شروع کرده و یک آرایه را به آن بدهیم:

db.movies.find({$or: []}).pretty()

این آرایه فیلترهای متعددی را گرفته و نتیجه را به شما برمی گرداند بنابراین می توانیم دو فیلتری را که بالاتر تعریف کرده بودیم به آن بدهیم:

db.movies.find({$or: [{"rating.average": {$lt: 5}}, {"rating.average": {$gt: 9.3}}]}).pretty()

این یعنی سریال هایی که rating (امتیاز منتقدین) آن ها کمتر از 5 یا بیشتر از 9.3 است. or به معنی «یا» است بنابراین از نظر منطقی باعث جمع شدن بین دو فیلتر می شود و مجموع نتایج را برمی گرداند. با اجرای کد بالا 4 سریال یا document برگردانده می شود که دو عدد از آن ها زیر 5 و دو عدد بالای 9.3 هستند. اپراتور بعدی ما nor$ است که دقیقا برخلاف or$ است. مثلا اگر کوئری بالا را به جای or با nor بنویسیم، می شود:

db.movies.find({$nor: [{"rating.average": {$lt: 5}}, {"rating.average": {$gt: 9.3}}]}).pretty()

این کوئری یعنی تمام document هایی را برگردان که موافق با هیچ کدام از شرط های ذکر شده (فیلترها) نباشند؛ یعنی تمام سریال هایی که rating آن ها کمتر از 5 نبوده و بیشتر از 9.3 نیز نباشد. کل document های ما 240 عدد بود و شرط قبلی (با or) چهار document را به ما برمی گرداند. بنابراین اگر nor$ باید 236 سند را به ما برگرداند و همینطور نیز می باشد:

db.movies.find({$nor: [{"rating.average": {$lt: 5}}, {"rating.average": {$gt: 9.3}}]}).count()

با اجرای این دستور، عدد 236 به ما نشان داده می شود.

اپراتور and$

استفاده از اپراتور and$ دقیقا شبیه به or$ است. به طور مثال اگر بخواهیم تمام سریال هایی را پیدا کنیم که امتیازشان بالای 9 و در ژانر درام باشند می توان گفت:

db.movies.find({$and: [{"rating.average": {$gt: 9}}, {genres: "Drama"}]}).pretty()

کد بالا سه document مختلف را برمی گرداند. بنابراین تفاوت and و or در اینجاست که:

  • سریال های درام و امتیاز 9 به بالا (هر دو شرط باید صحیح باشد)
  • سریال های درام یا امتیاز 9 به بالا (صحیح بودن یکی از شرط ها کافی است اما هر دو شرط هم می تواند صحیح باشد).

البته روش خلاصه تری برای استفاده از and$ وجود دارد که به شکل زیر است:

db.movies.find({"rating.average": {$gt: 9}, genres: "Drama"}).count()

کد بالا همان 3 عدد document را برای ما برمی گرداند. یعنی برای استفاده از اپراتور and$ می توانید فیلترها را در کنار هم بنویسید (با ویرگول از هم جدا می شوند). با این حساب چرا اپراتور and$ وجود دارد؟ به دلیل اینکه ممکن است چندین شرط را از یک فیلد واحد بخواهیم. مثلا اگر بگوییم:

 db.movies.find({genres: "Drama", genres: "Horror"}).count()

کد بالا نتیجه ی 23 را به ما می دهد که یعنی هفده document را با این شرط داریم. همچنین می بینید که هر دو شرط ما از یک فیلد خاص (Genres) هستند؛ سریال هایی که هم در ژانر درام و هم در ژانر وحشت هستند. این کد در shell به خوبی کار می کند بنابراین ما فعلا با آن مشکلی نداریم اما در بسیاری از driver ها خطا خواهد داد. چرا؟ به دلیل اینکه در جاوا اسکریپت داشتن نام های تکراری به عنوان key های یک شیء (genres) مجاز نیست. به همین خاطر است که در تعدادی از driver ها با خطا روبرو می شویم و باید از حالت غیر خلاصه استفاده نماییم. بنابراین کد بالا به شکل زیر نوشته می شود:

db.movies.find({$and: [{genres: "Drama"}, {genres: "Horror"}]}).count()

کد بالا عدد 17 را به ما می دهد! اگر این کد دقیقا معادل کد بالا است، چطور کد بالا عدد 23 و این کد عدد 17 را به ما می دهند؟ جواب این است که این دو کوئری یکی نیستند! مگر ما نگفته بودیم که Shell نیز بر پایه ی جاوا اسکریپت است؟ بنابراین نمی توانید کد زیر را بنویسید:

db.movies.find({genres: "Drama", genres: "Horror"}).count()

در اسناد JSON و جاوا اسکریپت، key های یک شیء نباید تکراری باشند اما کد بالا دو بار genres را دارد بنابراین genres دوم که روی horror تنظیم شده است، جایگزین genres اول می شود! بنابراین کد بالا دقیقا معادل کد زیر است:

db.movies.find({genres: "Horror"}).count()

بنابراین شاید در روی کاغذ اپراتور and$ یک حالت خلاصه داشته باشد اما اگر داده هایتان در یک فیلد تکراری باشد، اینطور نیست و در اکثر درایور ها به مشکل برمی خوریم. همیشه از حالت کامل and$ استفاده نمایید مگر آنکه مطمئن هستید داده هایتان در فیلد های کاملا مختلفی قرار دارند. آخرین اپراتور منطقی not$ است و فکر می کنم نحوه ی کار آن برای همه ی شما مشخص است. not$ دقیقا خلاف فیلتر شما را هدف می گیرد. به کد زیر نگاه کنید:

db.movies.find({"runtime": {$not: {$eq: 60}}}).count()

کد بالا می گوید تمام سریال هایی را پیدا کن که runtime آن ها برابر با 60 نباشد (مهم نیست بیشتر باشد یا کمتر باشد - فقط 60 نباشد). البته از این اپراتور زیاد استفاده نمی شود چرا که کد بالا دقیقا معادل کد زیر است:

db.movies.find({"runtime": {$ne: 60}}).count()

حتما اپراتور ne (مخفف not equal to) را از جلسات قبل به خاطر دارید.

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

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

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