حذف و تغییر نام فیلدها + آشنایی با upsert در MongoDB

Introduction to upsert in MongoDB

20 اردیبهشت 1401
درسنامه درس 43 از سری دوره جامع آموزش MongoDB
MongoDB: حذف و تغییر نام فیلد ها + آشنایی با upsert (قسمت 45)

حذف و تغییر نام فیلدها

حالا که با تغییر مقادیر فیلدها آشنا شده ایم بهتر است به سراغ حذف فیلدها و تغییر نام آن ها نیز برویم. برای شروع می خواهم فیلد phone را برای تمام افرادی که isSporty هستند، حذف نماییم. توجه کنید که نمی خواهیم مقدارش را روی صفر یا null قرار بدهیم بلکه می خواهیم کل فیلد phone را حذف کنیم تا اصلا وجود نداشته باشد. اگر می خواهید یک مقدار را null کنید باید به شکل زیر عمل کنید:

db.users.updateMany({isSporty: true}, {$set: {phone: null}})

ما معمولا زمانی فیلدی را null می کنیم که مطمئن باشیم در آینده می خواهیم آن را به روز رسانی کنیم یا اینکه در قسمتی از برنامه استفاده خواهد شد. مثلا شماره تلفن کاربر را برای ثبت نام نمی خواهیم بنابراین در ابتدا null است اما برای انجام تراکنش های مالی باید شماره تلفن خود را وارد کند بنابراین نمی توانیم چنین فیلدی را نداشته باشیم. با اجرای کوئری بالا تمام افرادی که isSporty در آن ها true باشد، شماره تلفن هایشان null خواهد شد.

اما اگر بخواهیم یک فیلد را به طور کامل حذف کنیم، از اپراتور unset$ استفاده نماییم:

db.users.updateMany({isSporty: true}, {$unset: {phone: ""}})

برای استفاده از unset$ ابتدا آن را به عنوان آرگومان دوم پاس داده و سپس برای مقدارش، فیلد مورد نظر خود را مشخص می کنیم که در اینجا phone است. مسئله اینجاست که باید به phone مقداری بدهید. این مقدار می تواند هر چیزی باشد اما معمولا یک رشته ی خالی پاس داده می شود. احتمالا می گویید چرا برای حذف یک فیلد باید مقداری را به آن بدهیم؟ این مقدار برای فیلد حذف شده نیست! همانطور که می دانید MongoDB بسیار شبیه به اشیاء جاوا اسکریپتی است و ساختار مشخصی دارد و در اشیاء جاوا اسکریپتی نمی توان key را بدون value داشت بنابراین این یک محدودیت ساختاری است که باید از آن پیروی کنیم. با اجرای این کوئری تمام کاربرانی که isSporty باشند، فیلد phone را از دست خواهند داد.

حالا اگر بخواهیم فیلد age را تغییر نام بدهیم، باید چه کار کنیم؟

db.users.updateMany({}, {$rename: {age: "totalAge"}})

من آرگومان اول updateMany را خالی گذاشته ام که همان فیلتر ما بود. با این کار تمام اسناد درون کالکشن را هدف می گیریم. سپس از اپراتور rename استفاده کرده ام که کارش تغییر نام فیلدها است و فیلد age را به totalAge تغییر داده ام. با اجرای کوئری بالا نتیجه ی زیر را می گیریم:

{ "acknowledged" : true, "matchedCount" : 5, "modifiedCount" : 3 }

به نظر شما چرا از 5 سند پیدا شده (که کل اسناد کالکشن ما می باشد) فقط 3 سند ویرایش شده است؟ به دلیل اینکه کاربران Javad و Amir اصلا دارای فیلد age نبوده اند بنابراین اصلا age ای نداشته اند که حالا بخواهد ویرایش شود. اگر شما فیلد age را برای آن ها اضافه کرده باشید، این خروجی متفاوت خواهد بود.

متد upsert چیست؟

upsert ترکیبی از دو کلمه ی update (به روز رسانی) و insert (وارد کردن و ثبت داده) است. upsert به ما کمک می کند که یک سند را به روز رسانی کنیم اما اگر آن سند وجود نداشت، خودش آن را برای ما می سازد. بگذارید برایتان مثالی بزنم. من در کوئری زیر می گویم به دنبال کاربری به نام Maria بگرد (چنین کاربری در پایگاه داده ی ما وجود ندارد) و اطلاعات او را به شکل زیر تغییر بده:

db.users.updateOne({name: "Maria"}, {$set: {age: 29, hobbies: [{title: "boxing", frequency: 3}], isSporty: true}})

از آنجایی که کاربری به نام Maria وجود ندارد، نتیجه ی اجرای کوئری بالا به شکل زیر خواهد بود:

{ "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }

یعنی هیچ سندی تغییر نکرده و هیچ اتفاقی نیفتاده است. مسئله اینجاست که هر دو دستور updateOne و updateMany یک آرگومان سوم را می گیرند که همان upsert بوده و به صورت پیش فرض روی false تنظیم شده است. اگر ما آن را روی true قرار بدهیم، در صورتی که سند مورد نظر وجود نداشته باشد، به صورت خودکار ساخته خواهد شد:

db.users.updateOne({name: "Maria"}, {$set: {age: 29, hobbies: [{title: "boxing", frequency: 3}], isSporty: true}}, {upsert: true})

همانطور که می بینید در کوئری بالا upsert روی true تنظیم شده است بنابراین با اجرای این کوئری، نتیجه ی زیر را می گیرید:

"acknowledged" : true,
"matchedCount" : 0,
"modifiedCount" : 0,
"upsertedId" : ObjectId("5eb65e8f817a4b9b2877881a")

نتیجه ی بالا می گوید هیچ چیزی پیدا نشد (matchedCount) چرا که Maria در کاربران ما وجود ندارد و همینطور هیچ چیزی ویرایش نشد (modifiedCount) چرا که Maria وجود ندارد که حالا بخواهد ویرایش شود اما MongoDB به صورت خودکار این کاربر را برای ما ساخته است (upsertedId). مقدار upsertedId همان مقدار کاربر جدیدی است که تازه ساخته شده است. حالا دستور زیر را اجرا می کنیم تا کاربران خود را ببینیم:

db.users.find().pretty()

با این کار Maria را در لیست کاربران خود خواهیم دید:

"_id" : ObjectId("5eb65e8f817a4b9b2877881a"), 
 "name" : "Maria",                             
 "age" : 29,                                   
 "hobbies" : [                                 
         {                                     
                 "title" : "boxing",           
                 "frequency" : 3               
         }                                     
 ],                                            
 "isSporty" : true                             

نکته ی جالبی در این کوئری وجود دارد که باید آن را مشخص کنم:

db.users.updateOne({name: "Maria"}, {$set: {age: 29, hobbies: [{title: "boxing", frequency: 3}], isSporty: true}}, {upsert: true})

ما نام Maria را در قسمت set$ ننوشته بودیم اما سندی که ایجاد شده است یک name با مقدار Maria دارد! چرا؟ به دلیل اینکه ما در قسمت فیلتر در کوئری بالا از حالت تساوی استفاده کرده ایم (name برابر Maria باشد) و از اپراتور هایی مانند gt یا lt استفاده نکرده ایم. زمانی که از حالت تساوی استفاده می کنیم و به دنبال سندی می گردیم، حتما آن سند باید مقدار درون تساوی را داشته باشد. مثلا نمی شود که ما به دنبال فردی با نام Maria باشیم اما نام Maria در Document ما وجود نداشته باشد! هیچ فرد عاقلی چنین جست و جویی را انجام نمی دهد بنابراین MongoDB خودش می فهمد که اگر ادمین سیستم، به دنبال فردی با نام Maria بوده است بنابراین حتما این سند جدید باید این نام را در خودش داشته باشد.

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

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

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