تبدیل یک محل به geoJSON در فریم‌ورک aggregation

Convert a Location to geoJSON in the Aggregation Framework

26 اردیبهشت 1401
درسنامه درس 69 از سری دوره جامع آموزش MongoDB
MongoDB: تبدیل یک محل به geoJSON در فریم ورک aggregation (قسمت 71)

ما در قسمت قبل کاری کردیم که فقط جنسیت (gender) و نام کامل کاربر نمایش داده شود بنابراین باید یک قدم جلوتر رفته و محل کاربر را نیز به صورت داده های geoJSON نمایش بدهیم. در حال حاضر ساختار کاربر ما در پایگاه داده به شکل زیر است:

        "_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"
                }
        },
// بقیه داده ها //

اگر دقت کنید هر کاربر دو نقطه مختصاتی دارد latitude و longitude بنابراین می توانیم این دو نقطه را گرفته و یک داده geoJSON از آن در بیاوریم. البته مشکل اینجاست که این نقاط به صورت رشته هستند اما ما باید آن ها را به صورت عددی در بیاوریم. البته ایمیل و dob را هم نمایش خواهیم داد بنابراین بهتر است با location شروع کنیم. همانطور که می دانید ما می توانیم از یک اپراتور چند بار استفاده کنیم و تکراری بودن آن ها در هر Stage اصلا مهم نیست چرا که هر Stage داده های stage قبلی را دریافت می کند. بر همین اساس من یک project$ دیگر را می نویسم تا داده های location را در آن تغییر بدهم. چرا؟ project$ فعلی ما بسیار پیچیده است (نام را در آن تغییر داده ایم) و نوشتن کد های دیگر در آن، کار خواندن کد ها را بسیار سخت می کند بنابراین بهتر است یک stage دیگر را برایش تعریف کنیم:

db.persons.aggregate([
    { $project: { _id: 0, name: 1, email: 1 } },
    {
        $project: {
            gender: 1,
            email: 1,
            location: 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.last"
                        }, 1]
                    }]
                }]
            }
        }
    }]).pretty()

در کد بالا مشاهده می کنید که این stage اضافه شده، مقادیر id را حذف کرده اما مقادیر name و email را می گیرد و آن ها را به stage دوم پاس می دهد. توجه داشته باشید که داده ها از stage اول به Stage دوم می روند بنابراین دیگر نیازی به حذف کردن id_ در stage دوم نبود و من کد آن را حذف کردم (آن را در همان stage اول حذف کرده ایم). در نهایت location را نیز به Stage دوم پاس داده ایم. اگر این کار را نکنیم، location از stage اول به دوم منتقل نمی شود بنابراین در نهایت اصلا داده های location را نخواهیم داشت. حالا نوبت ویرایش کردن location در stage اول است بنابراین شروع می کنیم:

db.persons.aggregate([
    {
        $project: {
            _id: 0, name: 1, email: 1, location: {
                type: "point", coordinates: [
                    "$location.coordinates.longitude",
                    "$location.coordinates.latitude"
                ]
            }
        }
    },
    {
        $project: {
            gender: 1,
            email: 1,
            location: 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.last"
                        }, 1]
                    }]
                }]
            }
        }
    }]).pretty()

من در این کد گفته ام که location باید یک شیء بگیرد که در آن type برابر point باشد (ما می توانیم مقادیر را به شکل بالا و دستی وارد کنیم و حتما نیازی نیست که از فیلد ها استفاده کنیم). سپس برای coordinates نیز باید آرایه ای داشته باشیم که به ترتیب longitude و latitude را دارد. با اجرای کوئری بالا کاربران به شکل زیر به ما برگردانده می شوند (من فقط یک نفر را می آورم):

"location" : {                         
        "type" : "point",              
        "coordinates" : [              
                "148.0944",            
                "35.5726"              
        ]                              
},                                     
"email" : "louise.graham@example.com", 
"fullName" : "Louise Graham"           

بنابراین برای هر فرد هر دارای fullname و email و هم دارای location است و کد های ما به خوبی اجرا شده اند. آیا مشکلی داریم؟ بله! اگر از فصل داده های geospatial یادتان باشد، مقادیر longitude و latitude باید به صورت عدد باشند اما در نتیجه بالا، هر دو رشته هستند! ما باید راهی پیدا کنیم که این داده ها را از رشته به عدد تبدیل کنیم. خوشبختانه MongoDB برای حل این مشکل یک اپراتور به نام convert$ دارد که وظیفه اش تبدیل کردن انواع داده ها به هم است:

db.persons.aggregate([
    {
        $project: {
            _id: 0, name: 1, email: 1, location: {
                type: "point", coordinates: [
                    { $convert: { input: "$location.coordinates.longitude", to: "double", onError: 0.0, onNull: 0.0 } },
                    { $convert: { input: "$location.coordinates.latitude", to: "double", onError: 0.0, onNull: 0.0 } }
                ]
            }
        }
    },
    {
        $project: {
            gender: 1,
            email: 1,
            location: 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.last"
                        }, 1]
                    }]
                }]
            }
        }
    }]).pretty()

همانطور که در کد بالا مشخص است اپراتور convert$ 4 آرگومان می گیرد که 2 تای اول اجباری هستند:

  • input: داده ای که باید تبدیل شود.
  • to: فرمتی که input باید به آن تبدیل شود.
  • onError: در صورت بروز خطا، input به چه چیزی تبدیل شود؟
  • onNull: در صورت null شدن داده ها، input به چه چیزی تبدیل شود؟

همانطور که می بینید من داده های خودمان را به آن پاس داده ام و برای to مقدار double (عدد اعشاری) را گذاشته ام تا از رشته به اعشار تبدیل شوند. برای دو آرگومان آخر نیز 0.0 را در نظر گرفته ام تا اعداد صفر شوند. با اجرای کوئری بالا، نتیجه زیر را خواهیم داشت:

"location" : {                        
         "type" : "point",             
         "coordinates" : [             
                 148.0944,             
                 35.5726               
         ]                             
 },                                    
 "email" : "louise.graham@example.com",
 "fullName" : "Louise Graham"          

همانطور که می بینید اعداد ما دیگر رشته نیستند و می توانیم به راحتی از آن ها استفاده کنیم.

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

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

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