ما تا این قسمت از سری آموزشی، با مفهوم 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 آشنا خواهیم شد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.