درک بهتری از find و شیء cursor

Better Understanding of Find and Cursor Object

MongoDB: درک بهتری از find و شیء cursor (قسمت 09)

برای شروع این جلسه می خواهم یک collection جدید به نام passengers را به پایگاه داده خودم اضافه کنم. من یک آرایه از اشیاء JSON را برایتان آماده کرده ام تا با insertMany وارد collection کنید:

[
  {
    "name": "Max Schwarzmueller",
    "age": 29
  },
  {
    "name": "Manu Lorenz",
    "age": 30
  },
  {
    "name": "Chris Hayton",
    "age": 35
  },
  {
    "name": "Sandeep Kumar",
    "age": 28
  },
  {
    "name": "Maria Jones",
    "age": 30
  },
  {
    "name": "Alexandra Maier",
    "age": 27
  },
  {
    "name": "Dr. Phil Evans",
    "age": 47
  },
  {
    "name": "Sandra Brugge",
    "age": 33
  },
  {
    "name": "Elisabeth Mayr",
    "age": 29
  },
  {
    "name": "Frank Cube",
    "age": 41
  },
  {
    "name": "Karandeep Alun",
    "age": 48
  },
  {
    "name": "Michaela Drayer",
    "age": 39
  },
  {
    "name": "Bernd Hoftstadt",
    "age": 22
  },
  {
    "name": "Scott Tolib",
    "age": 44
  },
  {
    "name": "Freddy Melver",
    "age": 41
  },
  {
    "name": "Alexis Bohed",
    "age": 35
  },
  {
    "name": "Melanie Palace",
    "age": 27
  },
  {
    "name": "Armin Glutch",
    "age": 35
  },
  {
    "name": "Klaus Arber",
    "age": 53
  },
  {
    "name": "Albert Twostone",
    "age": 68
  },
  {
    "name": "Gordon Black",
    "age": 38
  }
]

حالا این مسافران را با insertMany وارد collection خودمان می کنیم:

db.passengers.insertMany([{"name":"Max Schwarzmueller","age":29},{"name":"Manu Lorenz","age":30},{"name":"Chris Hayton","age":35},{"name":"Sandeep Kumar","age":28},{"name":"Maria Jones","age":30},{"name":"Alexandra Maier","age":27},{"name":"Dr. Phil Evans","age":47},{"name":"Sandra Brugge","age":33},{"name":"Elisabeth Mayr","age":29},{"name":"Frank Cube","age":41},{"name":"Karandeep Alun","age":48},{"name":"Michaela Drayer","age":39},{"name":"Bernd Hoftstadt","age":22},{"name":"Scott Tolib","age":44},{"name":"Freddy Melver","age":41},{"name":"Alexis Bohed","age":35},{"name":"Melanie Palace","age":27},{"name":"Armin Glutch","age":35},{"name":"Klaus Arber","age":53},{"name":"Albert Twostone","age":68},{"name":"Gordon Black","age":38}])

توجه داشته باشید، از آنجایی که passengers وجود ندارد، به صورت خودکار به عنوان یک collection ساخته می شود (در جلسات قبل در این مورد توضیح داده بودم). با اجرای کد بالا نتیجه زیر را دریافت می کنیم:

        "acknowledged" : true,
        "insertedIds" : [
                ObjectId("5e8423be6d27a360fec5c8cb"),
                ObjectId("5e8423be6d27a360fec5c8cc"),
                ObjectId("5e8423be6d27a360fec5c8cd"),
                ObjectId("5e8423be6d27a360fec5c8ce"),
                ObjectId("5e8423be6d27a360fec5c8cf"),
                ObjectId("5e8423be6d27a360fec5c8d0"),
                ObjectId("5e8423be6d27a360fec5c8d1"),
                ObjectId("5e8423be6d27a360fec5c8d2"),
                ObjectId("5e8423be6d27a360fec5c8d3"),
                ObjectId("5e8423be6d27a360fec5c8d4"),
                ObjectId("5e8423be6d27a360fec5c8d5"),
                ObjectId("5e8423be6d27a360fec5c8d6"),
                ObjectId("5e8423be6d27a360fec5c8d7"),
                ObjectId("5e8423be6d27a360fec5c8d8"),
                ObjectId("5e8423be6d27a360fec5c8d9"),
                ObjectId("5e8423be6d27a360fec5c8da"),
                ObjectId("5e8423be6d27a360fec5c8db"),
                ObjectId("5e8423be6d27a360fec5c8dc"),
                ObjectId("5e8423be6d27a360fec5c8dd"),
                ObjectId("5e8423be6d27a360fec5c8de"),
                ObjectId("5e8423be6d27a360fec5c8df")
        ]

نکته: همیشه لازم نیست دستورات را کامل تایپ کنید. مثلا اگر بگویید db.passe و سپس کلید tab را بزنید، خود ترمینال آن را تبدیل به db.passengers می کند و کارتان راحت تر می شود (قابلیت autocompletion).

در حال حاضر اگر دستور ()db.passengers.find().pretty را اجرا کنیم، نفر آخر (آقای Gordon Black) مشاهده نمی شود اما در انتهای لیست پیامی به شکل زیر نمایش داده شده است:

Type "it" for more

بنابراین اگر دستور it را تایپ کنیم (به صورت خالی و بدون هیچ پسوند و پیشوندی) آقای Gordon Black را می بینیم. چرا چنین اتفاقی می افتد؟ چرا فقط 20 نتیجه اول برگردانده شده اند (20 نتیجه محدودیت تعیین شده توسط MongoDB Shell است)؟ دستور find در اصل داده ها را به ما برنمی گرداند، بلکه یک شیء cursor به ما می دهد. به تصویر زیر نگاه کنید:

نحوه ی عملکرد cursor object
نحوه عملکرد cursor object

این کار عاقلانه است. اگر Find قرار بود داده ها را به ما برگرداند، در پایگاه های داده بزرگ دچار مشکل می شدیم (مثلا اگر 10 میلیون document داشتیم، سیستم شما بهم میریخت!). بنابراین به جای آرایه ای از داده ها یک cursor object برمی گرداند. یعنی شیء ای که metadata های داده ها را دارد. به عبارت دیگر به جای خود داده ها، اطلاعاتی در مورد داده ها را به ما می دهد و به ما اجازه می دهد بین نتایج گردش کنیم. دستور it دقیقا همین کار را کرد و به ما اجازه داد بین document های بیشتری گردش کرده و آن ها را نمایش بدهیم. آن 20 نتیجه اول نیز توسط خود Shell به صورت خودکار از شیء cursor گرفته شده و نمایش داده می شود بنابراین تصور نکنید که find می تواند 20 نتیجه اول را برگرداند.

برای برگرداندن خود داده ها می توانیم از روش های مختلفی استفاده کنیم. مثلا:

db.passengers.find().toArray()

با این کار تمام داده ها در قالب یک آرایه برمی گردند و آقای Gordon Black نیز نفر آخر لیست است. در واقع دستور toArray در شیء cursor گردش می کند و تمام داده ها را از آن می گیرد.

متد دیگر ما forEach است که به شما اجازه می دهد روی تمام عناصر پیدا شده گردش کرده و عملیاتی را روی آن ها پیاده کنید. نحوه استفاده از آن به driver شما مربوط است بنابراین اگر از زبان PHP استفاده می کنید باید به documentation آن در سایت mongoDB بروید (در جلسات قبل به شما نمایش داده شد). MongoDB Shell بر اساس جاوا اسکریپت کار می کند بنابراین نحوه استفاده از forEach در آن با پاس دادن یک arrow function است:

db.passengers.find().forEach((passengerData) => {printjson(passengerData)})

از آنجایی که هیچ شرطی را در find نگذاشته ام، دستور forEach روی تمام document های من در passengers اجرا می شود. در واقع find همان شیء cursor را برمی گرداند تا forEach روی آن گردش کند. forEach در Shell یک آرگومان را به صورت خودکار دریافت می کند که همان document شما در هر گردش است. شما می توانید نامش را هر چه می خواهید بگذارید (من passengerData را انتخاب کرده ام). حالا هر کاری که می خواهید، با داده هایتان می کنید. من کار خاصی نکرده ام بلکه از یک تابع به نام printjson استفاده کرده ام که کارش پرینت کردن اشیاء JSON به شکل خوانا می باشد (دقیقا معادل pretty) و passengerData را به آن پاس داده ام. بنابراین حلقه روی cursor object اجرا شده و همه document ها را از آن می گیرد. سپس آن ها را به arrow function ما پاس می دهد و نهایتا printjson آن ها را به صورت خوانا (مانند pretty) نمایش می دهد. خروجی پس از اجرای کد:

        "_id" : ObjectId("5e8423be6d27a360fec5c8cb"),
        "name" : "Max Schwarzmueller",
        "age" : 29
}

        "_id" : ObjectId("5e8423be6d27a360fec5c8cc"),
        "name" : "Manu Lorenz",
        "age" : 30
}
// دیگر مسافران //
        "_id" : ObjectId("5e8423be6d27a360fec5c8de"),
        "name" : "Albert Twostone",
        "age" : 68
}

        "_id" : ObjectId("5e8423be6d27a360fec5c8df"),
        "name" : "Gordon Black",
        "age" : 38
}

نکته: باید بدانید که دستور forEach بسیار بهینه تر از toArray است. چرا؟ forEach در هر گردش، یک document را دریافت کرده و به شما پاس می دهد اما بسیاری از دستورات، تمام داده ها را یکجا گرفته و آن ها را یکجا درون مموری دستگاه یا سرور شما بارگذاری می کنند، سپس شما همه را یکجا دریافت می کنید بنابراین پهنای باند و مموری شما درگیر می شود.

با این حساب روشن می شود که چرا نمی توانیم از pretty روی findOne استفاده کنیم. pretty متدی است که روی cursor اجرا می شود اما findOne خود داده را به ما می دهد نه اینکه cursor را برگرداند. در findOne حداکثر یک document برگردانده می شود بنابراین قرار نیست مموری یا پهنای باند درگیر شوند و نیازی به پاس دادن cursor نیست.

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

دیدگاه‌های شما (1 دیدگاه)

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

پیمان
20 دی 1400
سلام ممنون از دوره بسیار خوبتون . لطفا اگه ممکنه یه دوره جامع برای nest js هم تهیه کنید تو وبسایت های فارسی آموزش مناسبی ندیدم برای این فریمورک.

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

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