آشنایی با Project در فریم‌ورک aggregation

Introduction to Project in Aggregation Framework

26 اردیبهشت 1401
درسنامه درس 68 از سری دوره جامع آموزش MongoDB
MongoDB: آشنایی با Project در فریم ورک aggregation (قسمت 70)

stage بعدی در فریم ورک aggregation، همان project$ نام دارد و به جای اینکه داده ها در یک گروه قرار دهد، تغییرات را روی تک تک داده ها اعمال می کند. ما با مفهوم projection در کوئری های find آشنا شده ایم اما projection در فریم ورک aggregation بسیار قدرتمند تر عمل می کند. بگذارید به شکل ساده شروع کنیم و بگوییم که نمی خواهیم هیچ فیلتر کردنی انجام بدهیم، بلکه می خواهیم تمام داده های موجود را تغییر بدهیم تا فقط gender را داشته باشند:

db.persons.aggregate([
    { $project: { _id: 0, gender: 1 } }
])
    .pretty()

با اجرای این کوئری نتیجه زیر را می بینید (فقط بخشی از نتایج را می آورم):

{ "gender" : "female" }
{ "gender" : "male" }
{ "gender" : "female" }
{ "gender" : "female" }
{ "gender" : "female" }
{ "gender" : "male" }
{ "gender" : "female" }
{ "gender" : "female" }

البته همانطور که از مبحث projection به یاد داریم، این تغییرات واقعا روی داده های اصلی اعمال نمی شوند بلکه داده های ارسال شده به ما تغییر می کنند. حالا می خواهیم چند فیلد مثل email و location را نیز بگیریم اما می خواهیم آن ها را تغییر بدهیم. مثلا name فقط یک فیلد باشد نه اینکه یک embedded document باشد که نام و نام خانوادگی را به صورت جداگانه دارد. برای این کار می گوییم:

db.persons.aggregate([
    { $project: { _id: 0, gender: 1, fullName: { $concat: ["$name.first", " ", "$name.last"] } } }
])
    .pretty()

من خصوصیتی به نام fullName تعریف کرده ام که اپراتور concat$ را صدا می زند. این اپراتور دو یا چند رشته را در هم ادغام کرده و به هم می چسباند. این رشته ها باید به صورت یک آرایه به آن پاس داده شوند بنابراین از فیلد name خصوصیت first و last را داده ایم و بین آن ها یک رشته با یک فاصله (اسپیس) گذاشته ایم تا نام و نام خانوادگی به هم نچسبند. در ضمن یادتان نرود که رشته های داخل آرایه را با علامت $ بنویسید تا MongoDB بفهمد که منظور ما یک فیلد خاص است، در غیر این صورت رشته name.first و name.last را برای نام تمام افراد قرار می دهد. با اجرای کوئری بالا، نتیجه زیر را می گیریم:

{ "gender" : "male", "fullName" : "zachary lo" }               
{ "gender" : "male", "fullName" : "harvey chambers" }          
{ "gender" : "male", "fullName" : "gideon van drongelen" }     
{ "gender" : "male", "fullName" : "victor pedersen" }          
{ "gender" : "female", "fullName" : "پریا پارسا" }             
{ "gender" : "female", "fullName" : "maeva wilson" }           
{ "gender" : "female", "fullName" : "olav oehme" }             
{ "gender" : "male", "fullName" : "carl jacobs" }              
{ "gender" : "male", "fullName" : "elijah lewis" }             
{ "gender" : "female", "fullName" : "madeleine till" }         
{ "gender" : "male", "fullName" : "isolino viana" }            
{ "gender" : "female", "fullName" : "mestan kaplangı" }        
{ "gender" : "female", "fullName" : "katie welch" }            
{ "gender" : "female", "fullName" : "sandra lorenzo" }         
{ "gender" : "male", "fullName" : "بنیامین سالاری" }           
{ "gender" : "female", "fullName" : "andreia arnaud" }         
{ "gender" : "female", "fullName" : "anne ruiz" }              
{ "gender" : "female", "fullName" : "delia durand" }           
{ "gender" : "female", "fullName" : "anaëlle adam" }           
{ "gender" : "female", "fullName" : "louise graham" }          
Type "it" for more                                             

همانطور که می بینید داده های دریافتی کاملا متفاوت از داده های ذخیره شده در کالکشن ما هستند. بیایید یک قدم جلوتر برویم. ما می خواهیم نام های برگردانده شده حتما با حروف بزرگ شروع شوند (در زبان هایی که حروف بزرگ و کوچک داریم). برای انجام این کار می گوییم:

db.persons.aggregate([
    { $project: { _id: 0, gender: 1, fullName: { $concat: [{ $toUpper: "$name.first" }, " ", { $toUpper: "$name.last" }] } } }
])
    .pretty()

همانطور که می بینید شما می توانید به جای پاس دادن یک رشته ساده، از یک شیء استفاده کنید اما به شرطی که این متد استفاده شده در آن در نهایت یک رشته برگرداند. متد toUpper$ یک رشته را برمی گرداند بنابراین مشکلی نخواهیم داشت. من کوئری بالا را اجرا کرده و قسمتی از نتایج را برایتان قرار می دهم:

{ "gender" : "male", "fullName" : "بنیامین سالاری" }
{ "gender" : "female", "fullName" : "ANDREIA ARNAUD" }
{ "gender" : "female", "fullName" : "ANNE RUIZ" }
{ "gender" : "female", "fullName" : "DELIA DURAND" }
{ "gender" : "female", "fullName" : "ANAëLLE ADAM" }
{ "gender" : "female", "fullName" : "LOUISE GRAHAM" }

این نتایج دو نکته را به ما نشان می دهند:

  1. زبان هایی که حروف بزرگ و کوچک ندارند (مانند فارسی) مشکلی با متد هایی مثل toUpper نداشته و خطا تولید نمی کنند. به زبان ساده هیچ اتفاقی برایشان نمی افتد.
  2. متد toUpper$ برای بزرگ کردن تمام حروف است نه فقط برخی از آن ها!

ما می خواهیم فقط حروف اول نام ها بزرگ باشند بنابراین باید فقط حرف اول را پاس بدهیم. یعنی به طور خلاصه:

  1. ابتدا حرف اول اسم هر فرد را از نام او جدا می کنیم.
  2. سپس این حرف را به toUpper می دهیم.
  3. سپس بقیه حروف اسم هر فرد را به نتیجه خارج شده از toUpper می چسبانیم.

این کار را عینا برای فیلد last (نام خانوادگی) نیز تکرار می کنیم.

db.persons.aggregate([{
    $project: {
        _id: 0,
        gender: 1,
        fullName: {
            $concat: [{
                $toUpper: {
                    $substrCP: ["$name.first", 0, 1]
                }
            }, {
                $substrCP: ["$name.first", 1, {
                    $subtract: [{
                        $strLenCP: "$name.first"
                    }, 1]
                }]
            },
                " ",
            {
                $toUpper: {
                    $substrCP: ["$name.last", 0, 1]
                }
            }, {
                $substrCP: ["$name.last", 1, {
                    $subtract: [{
                        $strLenCP: "$name.first"
                    }, 1]
                }]
            }]
        }
    }
}]).pretty()

اپراتور substrCP$ یک یا چند حرف را از یک رشته جدا می کند و سه آرگومان را در قالب یک آرایه می گیرد. اولین آرگومان رشته مورد نظر است که من name.first را داده ام. دومین آرگومان محل شروع برای بریدن رشته است که من 0 را داده ام، یعنی از اولین حرف شروع کن (رشته ها از ایندکس صفر شروع می شوند). آرگومان سوم تعداد کاراکتر هایی که باید بریده شوند را مشخص می کنند. من عدد 1 را داده ام که یعنی از محل شروع (ایندکس صفر) یک کاراکتر را ببُر. با این کار اولین حرف از رشته last.first جدا می شود.

سپس باید همه  حروف رشته را به جز حرف اول به آن بچسبانیم تا رشته نام دوباره کامل شود. برای این کار باز هم از substrCP$ استفاده کرده ام اما این بار باید تعداد کل کاراکتر ها را حساب کنیم تا به عنوان آرگومان آخر به آن بدهیم. من برای انجام این کار از اپراتور subtract$ استفاده کرده ام که کارش تفریق دو مقدار از هم است. سپس با اپراتور strLenCP$ تعداد کل کاراکتر ها را محاسبه کرده ام و با کمک subtract$ یک واحد از آن کم کرده ام. همین کار را عینا برای name.last نیز انجام می دهیم.

نتیجه اجرای کوئری بالا به شکل زیر است (من فقط قسمتی از آن را می آورم):

{ "gender" : "female", "fullName" : "Sandra Lorenzo" }
{ "gender" : "male", "fullName" : "بنیامین سالاری" }
{ "gender" : "female", "fullName" : "Andreia Arnaud" }
{ "gender" : "female", "fullName" : "Anne Ruiz" }
{ "gender" : "female", "fullName" : "Delia Durand" }
{ "gender" : "female", "fullName" : "Anaëlle Adam" }
{ "gender" : "female", "fullName" : "Louise Graham" }

همانطور که می بینید حرف اول نام و نام خانوادگی بزرگ شده اند.

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

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

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