اگر یادتان باشد در قسمت قبلی بحث بر سر ویرایش تک تک اعضای یک آرایه بود تا آنجا که به کوئری زیر رسیدیم:
db.users.updateMany({"hobbies.frequency" : {$gt: 2}}, {$set: {"hobbies.$.goodFrequency" : true}})
و متوجه شدیم که مفهوم first match چیست. سوال اصلی اینجاست که چطور باید تمام اعضای یک آرایه را به روز رسانی کنیم؟ بیایید اول روی قسمت فیلتر کار کنیم. برای تمرین کردن فیلتر همیشه بهتر است که از یک دستور find استفاده کنید تا نتیجه را قبل از ویرایش مشاهده نمایید:
db.users.find({totalAge: {$gt: 20}}).pretty()
یعنی افرادی که totalAge آن ها بیشتر از 20 باشد. با اجرای کوئری بالا دو کاربر Pooya و Nastaran را پیدا می کنیم که به ترتیب 27.500000000000004 و 31 سال داشتند. فرض ما این است که می خواهیم به تک تک اعضای آرایه hobbies در این دو کاربر، یک فیلد جدید اضافه کنیم. ما می دانیم که نمی توانیم کدی را به شکل زیر بنویسیم:
db.users.updateMany({totalAge: {$gt: 20}{, {$inc: {"hobbies.frequency": -1}})
این کوئری به ما خطا می دهد. چرا؟ به دلیل اینکه استفاده از double quotes برای دسترسی به فیلد های یک آرایه مجاز است ("hobbies.frequency") اما نمی توانید از آن (بدین شکل) برای به روز رسانی آرایه ها استفاده کنید. hobbies.frequency غلط است چرا که درون hobbies یک داده (که در مثال ما یک شیء جاوا اسکریپتی است) دیگر داریم و هر کدام از آن داده ها یک frequency دارند بنابراین باید چیزی بین hobbies و frequency باشد. از طرفی توضیح دادیم که نمی توانیم از $ نیز استفاده کنیم بنابراین تنها راه حل ما استفاده از یک placeholder دیگر به نام []$ می باشد که به جای اولین نتیجه، تمام نتایج را شامل می شود:
db.users.updateMany({totalAge: {$gt: 20}}, {$inc: {"hobbies.$[].frequency": -1}})
حالا اگر به کاربران خود نگاهی بیندازیم، می بینیم که تک تک frequency های آن، یک واحد کاهش پیدا کرده اند.
مسئله بعدی این است که چطور یک فیلد خاص یا چند فیلد خاص را ویرایش کنیم. به کوئری زیر نگاه کنید:
db.users.find({"hobbies.frequency": {$gt: 2}}).pretty()
این کوئری تمام افرادی را پیدا می کند که حداقل یک frequency بیشتر از 2 داشته باشند اما همانطور که قبلا هم توضیح دادم، فقط یک Frequency بالاتر از 2 کافی است تا کل آن سند برگردانده شود. مثلا:
"_id" : ObjectId("5eb3e69ac91228985d228865"), "name" : "Javad", "hobbies" : [ { "title" : "Gym", "frequency" : 2 }, { "title" : "Driving", "frequency" : 4, "goodFrequency" : true } ], "isSporty" : true
Javad برای Gym دارای frequency بیشتر از 2 نیست. سوال من این است که چگونه می توانیم فقط فیلد مخصوص driving را تغییر دهیم بدون اینکه به فیلد Gym دست بزنیم؟ کوئری زیر این کار را برای ما انجام می دهد:
db.users.updateMany({"hobbies.frequency": {$gt: 2}}, {$set: {"hobbies.$[el].goodFrequency": true}}, {arrayFilters: [{"el.frequency": {$gt: 2}}]})
من باید این کوئری را برایتان توضیح بدهم. در ابتدا فیلتر خود را نوشته ایم که معنی اش را می دانید. سپس از set استفاده کرده ام و گفته ام goodFrequency را در تک تک فیلد ها (استفاده از []$) برابر true قرار بده اما به []$ یک el داده ام. این el مخفف element است و مانند identifier ای است که در حلقه های جاوا اسکریپتی دیده ایم. یعنی el می شود تک تک عناصر آرایه hobbies! حالا به عنوان آرگومان سوم updateMany باید این el را دریافت کرده و شرط خاصی را بنویسیم. این شرط تعیین می کند که کدام فیلد ها ویرایش شوند. اگر یادتان باشد در فصل قبل در آرگومان سوم updateMany از upsert استفاده کرده بودیم بنابراین حتما متوجه شده اید که آرگومان سوم در update و updateMany برای چنین کار هایی می باشد.
شرطی که من به عنوان آرگومان سوم updateMany در کوئری بالا نوشته ام باید حتما با arrayFilters شروع شود و این یک کلیدواژه مهم است که قبل تغییر نیست. arrayFilters یک آرایه است که شرط های مورد نظر شما را می گیرد. بله! شما می توانید چندین شرط یا فیلتر در در این قسمت مشخص کنید اما من فقط به یک شرط نیاز دارم: el.frequency (یعنی frequency در هر عضو آرایه) بیشتر از 2 باشد. توجه کنید که در این کوئری فیلتر آرایه ما (آرگومان سوم) با فیلتر اصلی (آرگومان اول) یکی شده است اما الزامی در این مورد وجود ندارد. مثلا ما می توانیم در فیلتر اصلی (آرگومان اول) به دنبال age بیشتر از 20 سال باشیم و در فیلتر آرایه یک چیز دیگر را تغییر بدهیم. بنابراین کل کوئری به صورت پویا در اختیار شما است و می توانید هرکاری خواستید با آن انجام بدهید. با اجرای کوئری بالا نتیجه زیر را می گیریم:
{ "acknowledged" : true, "matchedCount" : 6, "modifiedCount" : 3 }
6 نفر بر اساس فیلتر ما پیدا شده اند اما فقط 3 نفر ویرایش شده اند چرا که arrayFilter جلوی ویرایش شدن تمام افراد را گرفته است. حالا اگر با find از بین تمام اسناد خود، آن هایی که frequency بالاتر از 2 داشته اند ویرایش شده اند. به طور مثال Amir در هر دو عضو hobbies ویرایش شده است:
"_id" : ObjectId("5eb295a448ff1d422538a355"), "name" : "Amir", "hobbies" : [ { "title" : "programming", "frequency" : 3, "goodFrequency" : true }, { "title" : "Gym", "frequency" : 6, "highFrequency" : true, "goodFrequency" : true } ], "isSporty" : true
در حالی که Pooya در فیلد های Cooking و Hiking به روز رسانی نشده است:
"_id" : ObjectId("5eb3c250c91228985d228864"), "name" : "Pooya", "hobbies" : [ { "title" : "Sports", "frequency" : 4, "goodFrequency" : true }, { "title" : "Cooking", "frequency" : 2 }, { "title" : "Hiking", "frequency" : 0 } ], "phone" : 75834965320948, "totalAge" : 27.500000000000004
بنابراین همه چیز به خوبی کار می کند.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.