تا این قسمت از فصل UPDATE در مورد ویرایش فیلد های آرایه ها، چه به صورت دسته جمعی و چه به صورت تکی، مفصلا صحبت کرده ایم اما هیچ نکته ای راجع به حذف کردن عناصر از آرایه ها یا اضافه کردن عناصر به آرایه ها نگفته ایم. من از اضافه کردن عناصر شروع می کنم. به طور مثال ما می خواهیم کاربر Maria را پیدا کرده و یک مورد به hobbies او اضافه کنیم. به نظر شما کوئری زیر می تواند چنین کاری را انجام دهد؟
db.users.updateOne({name: "Maria"}, {$set: {hobbies: []}})
شما هر چیزی را که در کوئری بالا برای hobbies قرار بدهید، جایگزین مقادیر قبلی شده و مقادیر قبلی را حذف می کند اما من نمی خواهم چنین اتفاقی بیفتد. من می خواهم مقادیر قبلی را نگه دارم و یک مقدار جدید به آن اضافه کنم. مشکل ما در اینجا اپراتور set$ است بنابراین باید به جای آن از اپراتور دیگری به نام push$ استفاده کنیم که کارش اضافه کردن آیتم ها به آرایه است و چیزی را جایگزین مقادیر قبلی نمی کند.
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {title: "Gym", frequency: 2}}})
همانطور که می بینید برای اضافه کردن یک مقدار به یک آرایه ابتدا باید نام آن آرایه را به push$ بدهید و سپس عضو جدید را برای آن نام مشخص کنید. حالا می توانیم با دستور find نتیجه این تغییر را در Maria مشاهده کنیم:
"_id" : ObjectId("5eb65e8f817a4b9b2877881a"), "name" : "Maria", "age" : 29, "hobbies" : [ { "title" : "boxing", "frequency" : 3, "goodFrequency" : true }, { "title" : "Gym", "frequency" : 2 } ], "isSporty" : true
همانطور که می بینید علاوه بر Boxing، عضو Gym نیز به آن اضافه شده است. همچنین اگر بخواهید چندین سند را با هم اضافه یا همان push$ کنید باید از اپراتور each$ کمک بگیرید:
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {$each: [{title: "Cooking", frequency: 2}, {title: "Hiking", frequency: 1}]}}})
با اجرای کوئری بالا دو hobby جدید به نام های cooking و hiking به Maria اضافه می شود اما قبل از آنکه کوئری بالا را اجرا کنم باید نکته ای را برایتان توضیح بدهم. ما می توانیم از sort$ در کنار each$ استفاده نماییم تا مشخص کنیم که اعضای جدید به چه ترتیبی وارد hobbies شوند:
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {$each: [{title: "Cooking", frequency: 2}, {title: "Hiking", frequency: 1}], $sort: {frequency: -1}}}})
در اینجا گفته ام sort بر اساس Frequency و به صورت descending (نزولی - به خاطر 1-) باشد. شاید بگویید اصلا نیازی به Sort$ نیست چرا که ما خودمان اعضا را با ترتیب دلخواه خودمان تایپ می کنیم تا به همان ترتیب وارد hobbies شوند. مسئله اینجاست که داده ها در اکثر مواقع از سمت کاربر می آید. مثلا اگر در سایت شما خرید و فروش کالا انجام می شود و کاربر به ترتیب دلخواهش خرید می کند اما شما می خواهید رسید را به صورت الفبایی مرتب کنید، sort$ به دردتان خواهد خورد. البته هنوز کوئری بالا را اجرا نمی کنیم. ما می توانیم slice$ را هم به آن اضافه کنیم تا فقط چند عدد از کل عناصر اضافه شده را واقعا به hobbies اضافه کنیم:
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {$each: [{title: "Cooking", frequency: 2}, {title: "Hiking", frequency: 1}], $sort: {frequency: -1}, $slice: 1}}})
slice$ در کد بالا باعث می شود که پس از sort$ شدن، فقط اولین عنصر به آرایه hobbies اضافه شود. در چنین برنامه ای که خودمان داده ها را به صورت دستی نوشته ایم، slice اصلا منطقی نیست اما در برخی از برنامه ها شاید بخواهید گران ترین کالا ها را به صورت نزولی دسته بندی کرده و سپس 3 کالای اول را از بقیه جدا کنید. در چنین مواقعی slice$ نیز به کمک ما می آید. من در برنامه ام استفاده ای برای Slice$ ندارم بنابراین آن را حذف کرده و فقط کوئری زیر را اجرا می کنم:
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {$each: [{title: "Cooking", frequency: 2}, {title: "Hiking", frequency: 1}], $sort: {frequency: -1}}}})
با اجرای دستور بالا نتیجه زیر را می گیریم:
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
با یک دستور ساده find می توانیم ساختار جدید کاربر Maria را مشاهده کنیم:
"_id" : ObjectId("5eb65e8f817a4b9b2877881a"), "name" : "Maria", "age" : 29, "hobbies" : [ { "title" : "boxing", "frequency" : 3, "goodFrequency" : true }, { "title" : "Gym", "frequency" : 2 }, { "title" : "Cooking", "frequency" : 2 }, { "title" : "Hiking", "frequency" : 1 } ], "isSporty" : true
همانطور که می بینید Hiking کمتر از Cooking است (از نظر frequency) بنابراین در پایین تر قرار گفته است. اگر کوئری خود را به صورت زیر می نوشتیم، باز هم همین نتیجه را می گرفتیم:
db.users.updateOne({name: "Maria"}, {$push: {hobbies: {$each: [{title: "Hiking", frequency: 1}, {title: "Cooking", frequency: 2}], $sort: {frequency: -1}}}})
من این کوئری را چند بار و با sort های مختلف اجرا می کنم. به نظر شما چه اتفاقی می افتد؟ آیا داده ها جایگزین می شوند؟
"_id" : ObjectId("5eb65e8f817a4b9b2877881a"), "name" : "Maria", "age" : 29, "hobbies" : [ { "title" : "boxing", "frequency" : 3, "goodFrequency" : true }, { "title" : "Gym", "frequency" : 2 }, { "title" : "Cooking", "frequency" : 2 }, { "title" : "Hiking", "frequency" : 2 }, { "title" : "Cooking", "frequency" : 2 }, { "title" : "Cooking", "frequency" : 2 }, { "title" : "Hiking", "frequency" : 1 }, { "title" : "Hiking", "frequency" : 1 } ], "isSporty" : true
همانطور که می بینید داده های ما جایگزین نشده و به تعدادشان اضافه می شود. همچنین با تغییر مقدار، و تغییر sort توانسته ایم جای آن ها را نیز عوض کنیم. نکته جالب تر اینجاست که sort$ فقط نتایج ما را مرتب نمی کند بلکه کل آرایه hobbies را مرتب کرده است. سوال بعدی اینجاست که حالا چطور این داده های تکراری را حذف کنیم؟ در واقع سوال بهتر این است که چطور داده ها را از یک آرایه حذف کنیم؟
برخلاف push$ متدی به نام pull$ وجود دارد که کارش برعکس است و عناصر را از آرایه ها حذف می کند. به طور مثال برای حذف Hiking از Maria می توان گفت:
db.users.updateOne({name: "Maria"}, {$pull: {hobbies: {title: "Hiking"}}})
دقیقا مانند push ابتدا hobbies را مشخص کرده ایم و سپس گفته ایم هر جایی که title برابر Hiking بود، آن عضو را حذف کند. با یک دستور ساده find می توانیم نتیجه این کوئری را ببینیم:
"_id" : ObjectId("5eb65e8f817a4b9b2877881a"), "name" : "Maria", "age" : 29, "hobbies" : [ { "title" : "boxing", "frequency" : 3, "goodFrequency" : true }, { "title" : "Gym", "frequency" : 2 }, { "title" : "Cooking", "frequency" : 2 }, { "title" : "Cooking", "frequency" : 2 }, { "title" : "Cooking", "frequency" : 2 } ], "isSporty" : true
همانطور که می بینید دیگر هیچ Hiking برای Maria باقی نمانده است. حالا می توانیم همین کار را با Cooking نیز انجام بدهیم:
db.users.updateOne({name: "Maria"}, {$pull: {hobbies: {title: "Cooking"}}})
حالا Maria به شکل زیر در می آید:
"_id" : ObjectId("5eb65e8f817a4b9b2877881a"), "name" : "Maria", "age" : 29, "hobbies" : [ { "title" : "boxing", "frequency" : 3, "goodFrequency" : true }, { "title" : "Gym", "frequency" : 2 } ], "isSporty" : true
نکته بعدی اینجاست که اگر بخواهیم فقط آخرین عنصر یک آرایه را حذف کنیم (هر چه که باشد) باید از روش دیگری عمل کنیم. مثلا در حال حاضر کاربر Pooya در آخرین عضو آرایه hobbies خود hiking را دارد که frequency آن برابر صفر است بنابراین بهتر است آن را به شکل زیر حذف کنیم:
db.users.updateOne({name: "Pooya"}, {$pop: {hobbies: 1}})
عدد 1 که به hobbies داده ایم مشخص کننده عنصر آخر است. اگر بخواهیم عنصر اول را حذف کنیم باید از 1- استفاده کنیم:
db.users.updateOne({name: "Pooya"}, {$pop: {hobbies: -1}})
من می خواهم عنصر آخر را حذف کنم بنابراین همان 1 را گذاشته و کوئری را اجرا می کنم. حالا Pooya دیگر Hiking را ندارد:
"_id" : ObjectId("5eb3c250c91228985d228864"), "name" : "Pooya", "hobbies" : [ { "title" : "Sports", "frequency" : 4, "goodFrequency" : true }, { "title" : "Cooking", "frequency" : 2 } ], "phone" : 75834965320948, "totalAge" : 27.500000000000004
نکته: برای اضافه کردن اعضای غیر تکراری باید به جای push$ از addToSet$ استفاده کنید. برخلاف push$، هر چند بار که addToSet$ را تکرار کنید، اگر عضو مورد نظر قبلا در آرایه باشد، دیگر اضافه نمی شود.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.