ما در جلسه قبل موفق شدیم با تغییر داده ها دقیقا نتیجه مورد نظر خود را از یک آرایه استخراج کنیم اما در این جلسه با اپراتور جدیدی به نام bucket$ آشنا خواهیم شد. من در این جلسه می خواهم از کالکشن persons استفاده کنم. اگر یادتان باشد این کالکشن در پایگاه داده analytics قرار داشت بنابراین برای استفاده از آن می گوییم:
use analytics
سپس در تمامی دستورات بعدی نام db.persons را می آوریم. جهت یادآوری باید بگویم که ساختار هر سند در این کالکشن به صورت زیر بود:
"_id" : ObjectId("5ec23bdcdeafd616fdb3afb9"), "gender" : "male", "name" : { "title" : "mr", "first" : "zachary", "last" : "lo" }, "location" : { "street" : "3193 king st", "city" : "chipman", "state" : "yukon", "postcode" : "H8N 1Q8", "coordinates" : { "latitude" : "76.4507", "longitude" : "-70.2264" }, "timezone" : { "offset" : "+11:00", "description" : "Magadan, Solomon Islands, New Caledonia" } }, "email" : "zachary.lo@example.com", "login" : { "uuid" : "76970c67-4801-4926-80f0-4872fe0aee42", "username" : "lazyrabbit189", "password" : "pass1", "salt" : "BVMLMPwZ", "md5" : "a6ff61f912af9958587e0fc0c8dc920b", "sha1" : "bd37d1c699fb5a17031924c37e5d90ba4403e598", "sha256" : "0305e3ebf6f4502790d804cff5989a6a928f466af6e36bd808ad9ed24e51fee7" }, "dob" : { "date" : "1988-10-17T03:45:04Z", "age" : 29 }, "registered" : { "date" : "2011-09-29T20:54:32Z", "age" : 6 }, "phone" : "273-427-0510", "cell" : "309-911-7770", "id" : { "name" : "", "value" : null }, "picture" : { "large" : "https://randomuser.me/api/portraits/men/9.jpg", "medium" : "https://randomuser.me/api/portraits/med/men/9.jpg", "thumbnail" : "https://randomuser.me/api/portraits/thumb/men/9.jpg" }, "nat" : "CA"
bucket$ به شما اجازه می دهد که بیشتر با نحوه توزیع داده هایتان آشنا شوید. در واقع این اپراتور داده های شما را در گروه های خاصی قرار می دهد که به آن ها bucket (به معنی «سطل») می گوییم و سپس شما می توانید هر کاری خواستید روی این bucket ها انجام بدهید. بهتر است اپراتور bucket$ و کارایی آن را در عمل به شما نشان بدهم:
db.persons.aggregate([ { $bucket: { groupBy: "$dob.age", boundaries: [0, 18, 30, 50, 80, 120], output: { numPersons: { $sum: 1 }, averageAge: { $avg: "$dob.age" }, names: { $push: "$name.first" } } } } ]).pretty()
بگذارید اجزای این کوئری را برایتان توضیح بدهم:
اگر کوئری بالا را اجرا کنیم، تعداد افراد بسیار زیادی (هزاران هزار نفر) برای ما برگردانده می شوند که اصلا قابل خواندن نیست و در بسیاری از ترمینال ها جای کافی برای نمایش آن ها وجود ندارد بنابراین ممکن است تصور کنید که دستور به درستی اجرا نشده است. من از عمد فیلد names را برایتان قرار دادم تا ببینید با یک اشتباه کوچک، چقدر داده هایتان بهم می ریزد. برای خواناتر شدن نتایج من فیلد names را حذف می کنم:
db.persons.aggregate([ { $bucket: { groupBy: "$dob.age", boundaries: [0, 18, 30, 50, 80, 120], output: { numPersons: { $sum: 1 }, averageAge: { $avg: "$dob.age" } } } } ]).pretty()
با اجرای کوئری بالا نتیجه زیر را می گیریم:
{ "_id" : 18, "numPersons" : 868, "averageAge" : 25.101382488479263 } { "_id" : 30, "numPersons" : 1828, "averageAge" : 39.4917943107221 } { "_id" : 50, "numPersons" : 2304, "averageAge" : 61.46440972222222 }
همانطور که در قسمت boundaries گفته بودیم، سه بازه سنی را مشخص کرده ایم بنابراین سه دسته یا bucket برایمان برگردانده می شود. فیلد id به صورت پیش فرض و همیشه وجود خواهد داشت اما numPersons نشان می دهد که تعداد افراد درون هر bucket چقدر است و averageAge نیز میانگین سنی آن ها را نمایش می دهد. نکته جالب این است که اگر numPersons را در هر سه دسته با هم جمع بزنید به همان 5 هزار سند خودمان می رسیم.
با نگاه به میانگین های سنی بالا احتمال می رود که تعداد افراد زیر 18 سال و بالای 80 سال بسیار کم باشد بنابراین بیایید این مسئله را تست کنیم:
db.persons.find({"dob.age": {$lt: 18}}) db.persons.find({"dob.age": {$gt: 80}})
کوئری اول می گوید افراد زیر 18 سال را پیدا کن و کوئری دوم می گوید افراد بالای 80 سال را پیدا کن. با اجرای هر دو کوئری هیچ سندی برایمان برگردانده نمی شود بنابراین اصلا چنین افرادی را نداریم. با این حساب بهتر است قسمت boundaries خودمان را کمی بهتر بنویسیم:
db.persons.aggregate([ { $bucket: { groupBy: "$dob.age", boundaries: [18, 30, 40, 50, 60, 80, 120], output: { numPersons: { $sum: 1 }, averageAge: { $avg: "$dob.age" } } } } ]).pretty()
با پاس دادن مقادیر ریز تر به boundaries تعداد bucket های بیشتری را دریافت خواهیم کرد. همچنین من 120 را در انتهای آرایه باقی گذاشته ام تا کسی از داده ها جا نماند (نمی دانم پیر ترین فرد کالکشن چند سال دارد که بخواهم حداکثر تعیین کنم). با اجرای این کوئری نتیجه زیر را می گیریم:
{ "_id" : 18, "numPersons" : 868, "averageAge" : 25.101382488479263 } { "_id" : 30, "numPersons" : 910, "averageAge" : 34.51758241758242 } { "_id" : 40, "numPersons" : 918, "averageAge" : 44.42265795206972 } { "_id" : 50, "numPersons" : 976, "averageAge" : 54.533811475409834 } { "_id" : 60, "numPersons" : 1328, "averageAge" : 66.55798192771084 }
بنابراین کنترل کامل و ریز boundaries به دست خود شما است.
نکته: اگر می خواهید کار دسته بندی به صورت خودکار انجام شود و شما بازه های boundaries را تعریف نکنید باید از اپراتور bucketAuto$ استفاده کنید:
db.persons.aggregate([ { $bucketAuto: { groupBy: "$dob.age", buckets: 5, output: { numPersons: { $sum: 1 }, averageAge: { $avg: "$dob.age" } } } } ]).pretty()
buckets مشخص می کند که چند bucket داشته باشید. نتیجه اجرای کوئری بالا به شکل زیر است:
"_id" : { "min" : 21, "max" : 32 }, "numPersons" : 1042, "averageAge" : 25.99616122840691 "_id" : { "min" : 32, "max" : 43 }, "numPersons" : 1010, "averageAge" : 36.97722772277228 "_id" : { "min" : 43, "max" : 54 }, "numPersons" : 1033, "averageAge" : 47.98838334946757 "_id" : { "min" : 54, "max" : 65 }, "numPersons" : 1064, "averageAge" : 58.99342105263158 "_id" : { "min" : 65, "max" : 74 }, "numPersons" : 851, "averageAge" : 69.11515863689776
بازه سنی افراد (جوان ترین و پیر ترین) نیز در قسمت id مشخص شده است.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.