در این جلسه می خواهیم در مورد اجرای عملیات های UPDATE در آرایه ها صحبت کنیم. همانطور که می دانید آرایه ها داده هایی مستقیم نیستند بلکه درون خودشان داده های دیگری را دارند و این مسئله آن ها را از داده های دیگر مانند رشته ها و اعداد جدا می کند. برای این جسله ای همان پایگاه داده user استفاده می کنیم بنابراین بهتر است بدون مقدمه شروع کنیم. فرض کنید که هدف ما ویرایش تمام کاربرانی باشد که Gym را در hobbies خود دارند اما frequency آن 3 یا بیشتر است. بهتر است ابتدا روی نوشتن فیلتر آن تمرکز کنیم بنابراین من با find شروع می کنم:
db.users.find({$and: [{"hobbies.title": "Gym"}, {"hobbies.frequency": {$gte: 3}}]}).pretty()
ما دو شرط مختلف را برای یک فیلد داریم بنابراین باید از and$ استفاده کنیم. شروط ما وجود Gym و مساوی بودن یا بیشتر بودن frequency از 3 است. اجرای کوئری بالا کاربران Amir (با Frequency شش) و Javad (با frequency دو) را به ما می دهد. آیا یادتان است که چرا Javad برگردانده شده است در حالی که frequency او برابر 2 است؟ ما در فصل قبل در مورد این موضوع صحبت کردیم که and$ به دنبال هر frequency ای که بالاتر یا مساوی با 3 باشد می گردد و اینطور نیست که frequency حتما باید برای Gym باشد. برای تصحیح این مسئله باید از elemMatch$ استفاده کنیم:
db.users.find({hobbies: {$elemMatch: {title: "Gym", frequency: {$gte: 3}}}}).pretty()
با اجرای این کوئری فقط Amir برگردانده می شود بنابراین کار ما صحیح است و می توانیم از این کوئری برای فیلتر خودمان استفاده کنیم. سوال من این است که آیا می توانیم از set$ برای ایجاد این تغییر استفاده کنیم؟
db.users.updateMany({hobbies: {$elemMatch: {title: "Gym", frequency: {$gte: 3}}}}, {$set: {}})
در این کوئری هر چیزی که درون set$ نوشته شود، جایگزین کل فرد می شود نه فقط قسمتی از آرایه hobbies اما اگر کوئری را به شکل زیر تغییر بدهیم چطور؟
db.users.updateMany({hobbies: {$elemMatch: {title: "Gym", frequency: {$gte: 3}}}}, {$set: {hobbies: }})
در این کوئری هر چیزی که مقابل hobbies قرار بدهیم، کل اعضای آرایه hobbies را حذف کرده و جایگزین آن ها می شود. برای این کار می توانیم از علامت $ و مفهوم first match برای آن استفاده کنیم (این مورد را در فصل قبل بررسی کردیم):
db.users.updateMany({hobbies: {$elemMatch: {title: "Gym", frequency: {$gte: 3}}}}, {$set: {"hobbies.$": {title: "Gym", frequency: }}})
همانطور که می بینی من از علامت $ برای اشاره به مورد پیدا شده در قسمت فیلتر (آرگومان اول updateMany) استفاده کرده ام. من در کد بالا Frequency را خالی گذاشته ام چرا که باید نکته مهمی را برایتان توضیح بدهم. کوئری بالا با updateMany نوشته شده است. چرا؟ به دلیل اینکه ممکن است در کالکشن ما بیشتر از یک فرد پیدا شود (مثلا 3 نفر مختلف به Gym می روند و frequency هایشان بالای 3 است اما با هم فرق دارند، یکی 3 یکی 5 یکی 8 و الی آخر). گرچه که در پایگاه داده ما فعلا فقط Amir را با این شرط داریم اما ممکن است در برنامه های دیگر اینطور نباشد. در اینجا اگر یک عدد خاص را برای frequency بگذاریم، Frequency همه آن ها را یکی خواهیم کرد! برای حل این مسئله باید کوئری بالا را به شکل زیر ویرایش کنیم:
db.users.updateMany({hobbies: {$elemMatch: {title: "Gym", frequency: {$gte: 3}}}}, {$set: {"hobbies.$.highFrequency": true}})
حالا پس از علامت $ از یک فیلد جدید به نام highFrequency استفاده کرده و آن را روی true گذاشته ایم. از آنجایی که این فیلد وجود ندارد، به طور خودکار ساخته خواهد شد. نتیجه اجرای کوئری بالا در پایگاه داده ما به شکل زیر خواهد بود:
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
حالا کاربر Amir دارای این خصوصیت شده است:
"_id" : ObjectId("5eb295a448ff1d422538a355"), "name" : "Amir", "hobbies" : [ { "title" : "programming", "frequency" : 3 }, { "title" : "Gym", "frequency" : 6, "highFrequency" : true } ], "isSporty" : true
ما تا اینجا فقط یکی از اعضای آرایه را تغییر داده ایم اما می توانیم این کار را برای همه اعضای آن نیز انجام بدهیم. به کوئری زیر توجه کنید:
db.users.find({"hobbies.frequency": {$gt: 2}}).pretty()
این کوئری به دنبال کاربرانی می گردد که frequency ای بالاتر از 2 داشته باشند. در بین کاربران مختلفی که برگردانده شده است، کاربرانی مثل Pooya را می بینیم:
"_id" : ObjectId("5eb3c250c91228985d228864"), "name" : "Pooya", "hobbies" : [ { "title" : "Sports", "frequency" : 5 }, { "title" : "Cooking", "frequency" : 3 }, { "title" : "Hiking", "frequency" : 1 } ], "phone" : 75834965320948, "totalAge" : 27.500000000000004
همانطور که می بینید frequency در hiking برای Pooya روی 1 است بنابراین چرا برگردانده شده است؟ به دلیل اینکه نیازی نیست همه frequency ها بر اساس فیلتر ما صحیح باشند. فیلتر ما می گوید هر فردی که frequency بالاتر از 2 داشته باشد و Pooya در Sports برابر 5 است بنابراین شرط صحیح است. همان یک فیلد کافی است و سند را برمی گرداند. سوال اینجاست که چطور می توانیم به تک تک این اعضا دسترسی داشته باشیم؟ به طور مثال به کوئری زیر نگاه کنید:
db.users.updateMany({"hobbies.frequency" : {$gt: 2}}, {$set: {"hobbies.$.goodFrequency" : true}})
اگر این کوئری را اجرا نمایید تمام کاربرانی که frequency بالاتر از 2 دارند (همه افراد درون کالکشن ما شامل این شرط می شوند) ویرایش شده و goodFrequency به آن ها اضافه می شود اما نکته جالبی وجود دارد. من یکی از این کاربران را برای نمونه می آورم:
"_id" : ObjectId("5eb3c250c91228985d228864"), "name" : "Pooya", "hobbies" : [ { "title" : "Sports", "frequency" : 5, "goodFrequency" : true }, { "title" : "Cooking", "frequency" : 3 }, { "title" : "Hiking", "frequency" : 1 } ], "phone" : 75834965320948, "totalAge" : 27.500000000000004
همانطور که می بینید این خصوصیت فقط به اولین عضو آرایه hobbies اضافه شده است. چرا؟ ما در فصل قبل توضیح دادیم که $ دارای مفهومی به نام first match (اولین انطباق یا اولین نتیجه) است که یعنی فقط اولین نتیجه را هدف گرفته یا ویرایش می کند. اولین نتیجه چیست؟ شرط ما این بود که frequency بالاتر از 2 باشد بنابراین اولین عضو آرایه hobbies در هر فرد که این شرط برایش صادق باشد ویرایش خواهد شد. مثلا کاربر دیگر ما را ببینید:
"_id" : ObjectId("5eb3e69ac91228985d228865"), "name" : "Javad", "hobbies" : [ { "title" : "Gym", "frequency" : 2 }, { "title" : "Driving", "frequency" : 4, "goodFrequency" : true } ], "isSporty" : true
اولین عضو آرایه hobbies برای این کاربر Gym است و frequency آن نیز 2 می باشد بنابراین نتیجه ما نیست اما عضو بعدی frequency دارای 4 است بنابراین goodFrequency به آن اضافه شده است. این مفهوم first match است. در قسمت بعدی این مشکل را حل خواهیم کرد و به مسائل دیگر آرایه ها می پردازیم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.