اعمال projection در آرایه‌ها و محاسبه‌ی طول آن‌ها

Apply Projection on Arrays and Calculate Their Length

26 اردیبهشت 1401
درسنامه درس 73 از سری دوره جامع آموزش MongoDB
MongoDB: اعمال projection در آرایه ها و محاسبه ی طول آن ها (قسمت 75)

ما تا این قسمت از سری آموزشی، با مفهوم projection آشنا شده ایم و از آن هم در کوئری های ساده find و هم در فریم ورک aggregation استفاده کرده ایم اما این بار می خواهیم با projection در آرایه ها کار کنیم. قبل از هر کاری باید با ساختار داده های خودمان در کالکشن friends آشنا شویم. برای این کار یک کوئری find ساده کافی است:

"_id" : ObjectId("5ec48cfe35361420f36550a7"),
"name" : "Max",                              
"hobbies" : [                                
        "Sports",                            
        "Cooking"                            
],                                           
"age" : 29,                                  
"examScores" : [                             
        {                                    
                "difficulty" : 4,            
                "score" : 57.9               
        },                                   
        {                                    
                "difficulty" : 6,            
                "score" : 62.1               
        },                                   
        {                                    
                "difficulty" : 3,            
                "score" : 88.5               
        }                                    
]

در این جلسه می خواهیم examScores (نمرات امتحانات) را هدف قرار بدهیم و کاری کنیم که به جای نمایش تمام نمرات، فقط اولین سند داخل این آرایه نمایش داده شود. برای این کار اپراتوری به نام slice$ (به معنی «قطعه» یا «تکه») وجود دارد که در ساده ترین حالت خود دو آرگومان می گیرد:

  • نام آرایه ای که باید قطعه شود.
  • تعداد عناصری که باید از آن جدا شوند.

بنابراین می توانیم یک کوئری ساده را به شکل زیر بنویسیم:

db.friends.aggregate([
    { $project: { _id: 0, examScore: { $slice: ["$examScores", 1] } } }
]).pretty()

من نیازی به id_ ندارم بنابراین آن را 0 گذاشته ام تا نمایش داده نشود. سپس یک فیلد جدید به نام examScore (توجه کنید که آخرش s ندارد) تعریف کرده ام. این فیلد، اپراتور slice$ را گرفته است و همانطور که گفتم این اپراتور در ساده ترین حالت خود یک آرایه با دو آرگومان می گیرد. اولین آرگومان نام آرایه خودمان است که examScores (با s در انتها) می باشد و دومین آرگومان تعداد عناصری است که می خواهیم از آن جدا کنیم. محل شروع شمارش نیز از اول آرایه می باشد بنابراین عدد 1 را پاس داده ام که یعنی فقط اولین عنصر آن را به ما برگردان. با اجرای این کوئری، نتیجه زیر را می گیریم:

{ "examScore" : [ { "difficulty" : 4, "score" : 57.9 } ] }
{ "examScore" : [ { "difficulty" : 7, "score" : 52.1 } ] }
{ "examScore" : [ { "difficulty" : 3, "score" : 75.1 } ] }

همه چیز به درستی کار می کند و اینها دقیقا اولین اعضای آرایه examScores هستند اما اگر بخواهیم عضو آخر را بگیریم چطور؟ اگر بخواهیم دو عضو آخر را بگیریم چطور؟ من گفتم که محل شمارش از ابتدای آرایه است اما اگر آرگومان دوم را به صورت عددی منفی پاس بدهید، شمارش از انتهای آرایه آغاز می شود. بنابراین برای دریافت دو عنصر آخر examScores می توان کوئری زیر را نوشت:

"examScore" : [                         
    {                               
            "difficulty" : 6,       
            "score" : 62.1          
    },                              
    {                               
            "difficulty" : 3,       
            "score" : 88.5          
    }                               
]                                       
                                    
                                    
"examScore" : [                         
    {                               
            "difficulty" : 2,       
            "score" : 74.3          
    },                              
    {                               
            "difficulty" : 5,       
            "score" : 53.1          
    }                               
]                                       
                                    
                                    
"examScore" : [                         
    {                               
            "difficulty" : 8,       
            "score" : 44.2          
    },                              
    {                               
            "difficulty" : 6,       
            "score" : 61.5          
    }                               
]

سوال بعدی این است که اگر آرایه ما بزرگ بود چطور؟ مثلا یک آرایه 100 عضوی داریم و می خواهیم فقط عضو دوم را برگردانیم. آنگاه چکار کنیم؟ دستور slice حالت دیگری دارد که سه آرگومان قبول می کند:

  • اولین آرگومان، نام آرایه ای است که باید برش بخورد.
  • دومین آرگومان محل شروع شمارش است (بر اساس ایندکس صفر).
  • سومین آرگومان تعداد اعضای برگردانده شده است.

با این حساب می توان گفت:

db.friends.aggregate([
    { $project: { _id: 0, examScore: { $slice: ["$examScores", 2, 1] } } }
]).pretty()

یعنی از عضو سوم شروع کن (عدد 2 یعنی ایندکس 2) و سپس یک عنصر را به من بده (عدد 1). به زبان ساده تر یعنی عضو سوم را به من برگردان! با اجرای این کوئری نتیجه زیر را می گیریم:

{ "examScore" : [ { "difficulty" : 3, "score" : 88.5 } ] }
{ "examScore" : [ { "difficulty" : 5, "score" : 53.1 } ] }
{ "examScore" : [ { "difficulty" : 6, "score" : 61.5 } ] }

این ها دقیقا اعضای سوم آرایه های examScores هستند.

فرض بعدی ما این است که اصلا نمرات برایمان مهم نیست بلکه می خواهیم بدانیم افراد حاضر در کالکشن، چند امتحان داده اند. تعداد امتحانات برابر با طول آرایه examScores است بنابراین می توانیم از اپراتوری به نام size$ استفاده کنیم که طول آرایه را حساب می کند:

db.friends.aggregate([
    { $project: { _id: 0, numScores: { $size: "$examScores" } } }
]).pretty()

در اینجا فیلد جدیدی به نام numScores (مخفف number of scores - تعداد نمرات) ساخته ام که طول آرایه examScores را محاسبه می کند. مثل همیشه نیز id_ به صورت خودکار وارد نتایج می شود بنابراین باید آن را به صورت صریح و دستی 0 کنیم تا مزاحم کار ما نشود. حالا اگر این کوئری را اجرا کنیم، نتیجه زیر را می گیریم:

{ "numScores" : 3 }
{ "numScores" : 3 }
{ "numScores" : 3 }

یعنی سه سند داشته ایم و در هر سند فیلدی به نام numScores ساخته ایم که هر کدام سه عضو دارد. یعنی به سادگی می توان فهمید که هر فرد 3 بار امتحان داده است. ممکن است بگویید که نیازی به این اپراتور نداریم چرا که می توانیم به راحتی تعداد این امتحانات را بشماریم و از اول نیز معلوم بود که هر فرد سه بار امتحان داده است. حرفتان درست است! اما این یک مثال بسیار ساده بود. استفاده واقعی این اپراتور در پروژه های اصلی و بزرگی است که کاربران یا فیلد ها در آن قابل شمارش نیست. در جلسه بعدی با اپراتور مشهور دیگری به نام filter آشنا خواهیم شد.

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

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

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