آشنایی با اپراتور bucket$ در فریم‌ورک aggregation

Familiarity with Bucket Operation in Aggregation Framework

26 اردیبهشت 1401
درسنامه درس 76 از سری دوره جامع آموزش MongoDB
MongoDB: آشنایی با اپراتور bucket$ در فریم ورک aggregation (قسمت 78)

ما در جلسه قبل موفق شدیم با تغییر داده ها دقیقا نتیجه مورد نظر خود را از یک آرایه استخراج کنیم اما در این جلسه با اپراتور جدیدی به نام 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()

بگذارید اجزای این کوئری را برایتان توضیح بدهم:

  • اولین آرگومان bucket$ دستورِ groupBy است که مشخص می کند معیار دسته بندی داده ها در bucket های مختلف چیست؟ ما بر اساس کدام فیلد باید داده ها را جدا کنیم؟
  • آرگومان بعدی boundaries است که مشخص کننده جفت های عددی برای فیلد age است. یعنی هر دو عدد کنار هم در این آرگومان، یک جفت حساب می شوند که اولی حداقل و دومی حداکثر را مشخص می کند بنابراین در کد بالا گفته ایم که داده هایمان باید در 3 گروه سنی (به خاطر groupBy که روی Age است) دسته بندی شود: دسته اول افراد 0 تا 18 سال هستند و دسته دوم 30 تا 50 سال و دسته سوم 80 تا 120 سال. یادتان باشد که بازه های تعریف شده در این آرگومان همیشه باید صعودی باشند و نمی توانید نزولی کار کنید.
  • آرگومان بعدی output است. در حالت عادی و بدون output، کوئری بالا فقط تعداد افراد درون هر bucket را مشخص می کند اما اگر output را به آن بدهیم، نتیجه را کامل برمی گرداند. در output باید مشخص کنید که چه فیلد هایی باید برگردانده شوند که من 3 فیلد را مشخص کرده ام. اول تعداد افراد هر bucket که به sum$ محاسبه می شود و در مورد آن صحبت کرده ایم. دوم میانگین سنی افراد که با اپراتور avg$ محاسبه می شود. سوم نام های افراد در هر bucket که با اپراتور push$ قابل انجام است.

اگر کوئری بالا را اجرا کنیم، تعداد افراد بسیار زیادی (هزاران هزار نفر) برای ما برگردانده می شوند که اصلا قابل خواندن نیست و در بسیاری از ترمینال ها جای کافی برای نمایش آن ها وجود ندارد بنابراین ممکن است تصور کنید که دستور به درستی اجرا نشده است. من از عمد فیلد 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 مشخص شده است.

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

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

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