برنامه نویسی

پرس و جو آرایه های تعبیه شده در JSON (اسناد Postgresql JSONB و MongoDB)

هنگام کار با ساختار داده های مبتنی بر اسناد ، زمینه های موجود در ریشه را می توان با استفاده از شاخص های بیان ساده فهرست بندی کرد. با این حال ، هنگامی که یک آرایه در مسیر وجود دارد ، که نمایانگر یک رابطه یک به یک است ، PostgreSQL به یک شاخص GIN و استفاده از اپراتورهای مسیر JSON برای نمایه سازی ، کارآمدتر از نمایش داده های SQL/JSON نیاز دارد.

نمونه

من جدول زیر را برای ذخیره کتاب ایجاد می کنم. من تصمیم گرفتم که اطلاعات بیشتری را با یک طرح انعطاف پذیر جاسازی کنم و یک ستون “داده” برای ذخیره داده های JSON اضافه کردم:

create table books (  
   primary key(book_id),  
   book_id bigint,  
   title text,  
   data jsonb  
);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

من یک کتاب را وارد می کنم و برخی از نظرات را در سند طرح انعطاف پذیر خود اضافه می کنم:

insert into books values (  
   8675309,  
   'Brave New World',  
   '{ "reviews":[  
      { "name": "John", "text": "Amazing!" },  
      { "name": "Jane", "text": "Incredible book!" }  
   ] }'
);
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نیازی به جدول دیگری نیست ، زیرا بررسی ها ذاتاً با کتابهایی که در مورد آنها بحث می کنند مرتبط است. یک کتاب بدون نمایش در کنار بررسی آن قابل بررسی نیست ، و هر جدول جداگانه ای را غیر ضروری می کند. من می دانم که به نظر می رسد نقض اولین فرم عادی است ، اما هیچ ناهنجاری به روزرسانی در اینجا امکان پذیر نیست زیرا هیچ تکثیر وجود ندارد. از نظر عادی سازی ، این با ذخیره متن ، که مجموعه ای از کاراکتر یا تعبیه شده است ، که آرایه هایی از اعداد هستند ، تفاوت چندانی ندارد.

پرس و جو ناکارآمد با SQL پیوستن

اگر با SQL راحت هستید ، ممکن است بخواهید این ساختار را با استفاده از SQL پرس و جو کنید. به سادگی آرایه سند JSON را غیرقانونی کنید و از آن مانند یک جدول رابطه ای استفاده کنید:

SELECT DISTINCT title FROM books    
JOIN LATERAL jsonb_array_elements(books.data->'reviews') AS review 
ON review->>'name' = 'John'
;  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

jsonb_array_elements یک آرایه JSON را برای نمایش داده های SQL در ردیف ها گسترش می دهد. The Lateral Join اطلاعات کتاب ، فیلترهای بند OR یا Where Where را با نام Reverseer اضافه می کند و عناوین تکراری را حذف می کند. این نحو استاندارد SQL است اما نمی تواند از یک فهرست برای فیلتر کردن نام بازرسان قبل از عدم استفاده استفاده کند ، و نیاز به خواندن همه ردیف ها و اسناد دارد:

                              QUERY PLAN                               
-----------------------------------------------------------------------
 Unique
   ->  Sort
         Sort Key: books.title
         ->  Nested Loop
               ->  Seq Scan on books
               ->  Function Scan on jsonb_array_elements review
                     Filter: ((value ->> 'name'::text) = 'John'::text)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در حالی که این یک نحو معتبر SQL است ، و JSON یک داده SQL معتبر است ، اما آنها چندان دوستانه نیستند زیرا یک پایگاه داده رابطه ای یک پایگاه داده اسناد نیست. هنگام استفاده از اسناد در PostgreSQL ، باید یاد بگیرید که چگونه از آنها پرس و جو کنید و از آنها فهرست بندی کنید.

توجه داشته باشید که jsonb_array_elements استاندارد SQL نیست ، اما PostgreSQL 17 JSON_TABLE را معرفی کرد که APRT استاندارد است. پرس و جو را می توان دوباره نوشت:

SELECT books.title  
FROM books  
JOIN JSON_TABLE(  
  books.data->'reviews',  
  '$[*]' COLUMNS (  
    name TEXT PATH '$.name' 
  )  
) AS review  
ON review.name = 'John'
;  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این روش استاندارد SQL/JSON برای جستجوی اسناد است. متأسفانه ، کارآمد نیست زیرا اسکن شاخص امکان پذیر نیست. فراموش نکنید که شاخص های SQL بخشی از استاندارد SQL نیستند.

پرس و جو کارآمد با اپراتورهای JSON

برای پرس و جو کارآمد از داده های JSONB برای بررسی توسط یک شخص خاص ، ما باید از اپراتور مهار PostgreSQL استفاده کنیم @> به جای تکیه بر SQL استاندارد:

SELECT title FROM books  
WHERE data->'reviews' @> '[{"name": "John"}]'
;
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اکنون که مستقیماً بدون تغییر سند روی جدول فیلتر می کنم ، می توانم یک فهرست ایجاد کنم. از آنجا که می توان چندین کلید در هر ردیف جدول وجود داشت ، یک شاخص معکوس لازم است:

CREATE INDEX ON books USING gin ((data->'reviews') jsonb_path_ops)
;
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

با داشتن یک شاخص برای اپراتورهای مسیر JSON ، هر کلید با یک مورد در آرایه مطابقت دارد. این می تواند هنگام پرس و جو با فیلتر برابری در قسمت آرایه تعبیه شده استفاده شود:

                                   QUERY PLAN                                   
--------------------------------------------------------------------------------
 Bitmap Heap Scan on books
   Recheck Cond: ((data -> 'reviews'::text) @> '[{"name": "John"}]'::jsonb)
   ->  Bitmap Index Scan on books_expr_idx
         Index Cond: ((data -> 'reviews'::text) @> '[{"name": "John"}]'::jsonb)

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

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

مقایسه با یک پایگاه داده سند

در حالی که PostgreSQL انعطاف پذیری را در ذخیره و نمایه سازی اسناد JSON ارائه می دهد ، این یک پایگاه داده سند را جایگزین نمی کند که اسناد انواع بومی هستند. به عنوان مثال ، در MongoDB زمینه های موجود در یک آرایه مانند هر زمینه دیگر استفاده می شود. من سند مشابه را در MongoDB وارد می کنم:

db.books.insertOne({  
  book_id: 8675309,  
  title: "Brave New World",  
  reviews: [  
    { name: "John", text: "Amazing!" },  
    { name: "Jane", text: "Incredible book!" }  
  ]  
}); 
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نیازی به اپراتورهای ویژه نیست و من می توانم مانند هر زمینه دیگری از زمینه تعبیه شده پرس و جو کنم:

db.books.find(
  { "reviews.name": "John" } // filter
, { title: 1, _id: 0 }      // projection
);  

[ { title: 'Brave New World' } ]

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نیازی به نوع شاخص خاص نیست و من می توانم مانند هر زمینه دیگری قسمت تعبیه شده را فهرست بندی کنم:

db.books.createIndex({ "reviews.name": 1 })
;  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برنامه اجرای تأیید می کند که از این فهرست برای فیلتر کردن در “بررسی ها” استفاده می شود:

db.books.find(
  { "reviews.name": "John" } // filter
, { title: 1, _id: 0 }      // projection
).explain().queryPlanner.winningPlan
; 

{
  isCached: false,
  stage: 'PROJECTION_SIMPLE',
  transformBy: { title: 1, _id: 0 },
  inputStage: {
    stage: 'FETCH',
    inputStage: {
      stage: 'IXSCAN',
      keyPattern: { 'reviews.name': 1 },
      indexName: 'reviews.name_1',
      isMultiKey: true,
      multiKeyPaths: { 'reviews.name': [ 'reviews' ] },
      isUnique: false,
      isSparse: false,
      isPartial: false,
      indexVersion: 2,
      direction: 'forward',
      indexBounds: { 'reviews.name': [ '["John", "John"]' ] }
    }
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این یک شاخص معمولی است ، تنها خاصیت این است که اجازه می دهد تا ورودی های چند کلید را انجام دهد.

بر خلاف شاخص GIN PostgreSQL ، که به اسکن Bitmap نیاز دارد که سفارش ورود را حفظ نمی کند ، MongoDB از یک شاخص معمولی استفاده می کند که از نمایش داده های دامنه پشتیبانی می کند. به عنوان مثال ، اگر من فقط از آغاز یک نام می دانم ، می توانم از یک عبارت منظم برای فیلتر کردن نتایج به طور مؤثر استفاده کنم:

db.books.find(  
  { "reviews.name": { $regex: "^Joh" } }, // filter using regex  
  { title: 1, _id: 0 }                    // projection  
).explain().queryPlanner.winningPlan
;  

{
  isCached: false,
  stage: 'PROJECTION_SIMPLE',
  transformBy: { title: 1, _id: 0 },
  inputStage: {
    stage: 'FETCH',
    inputStage: {
      stage: 'IXSCAN',
      keyPattern: { 'reviews.name': 1 },
      indexName: 'reviews.name_1',
      isMultiKey: true,
      multiKeyPaths: { 'reviews.name': [ 'reviews' ] },
      isUnique: false,
      isSparse: false,
      isPartial: false,
      indexVersion: 2,
      direction: 'forward',
      indexBounds: { 'reviews.name': [ '["Joh", "Joi")', '[/^Joh/, /^Joh/]' ] }
    }
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

MongoDB از شاخص به طور کارآمد استفاده کرد ، زیرا برنامه ریز پرس و جو بیان منظم را تغییر داد /^Joh/ به طور خاص به یک اسکن دامنه ["Joh", "Joi")بشر

پایان

هنگام مقایسه PostgreSQL و MongoDB ، درک مکانیسم های پرس و جو و نمایه سازی آنها ضروری است و فقط به توانایی آنها در ذخیره JSON تکیه نمی کنند.
مانند سایر RDBMS ، PostgreSQL به عنوان یک بانک اطلاعاتی متمرکز و یکپارچه که توسط چندین برنامه استفاده می شود ، برتری می یابد. با توابع تخصصی JSONB و شاخص های جین ، انعطاف پذیری را به جداول عادی اضافه می کند.
MongoDB برای توسعه چابکی به ویژه در میکروسرویس و طراحی دامنه محور ایده آل است ، جایی که الگوهای دسترسی به خوبی تعریف شده است ، اما برنامه با سرعت بالا تکامل می یابد. مدل سند آن به خوبی با اشیاء تجاری هماهنگ است.

در نهایت ، انتخاب یک بانک اطلاعاتی باید بر اساس تخصص تیم شما ، راحتی با نحو پایگاه داده ، مدل سازی داده ها ، نمایه سازی بهینه و دسترسی به استخدام های جدید و منابع آموزشی باشد. اگر هیچ تخصصی برای کدگذاری پرس و جوهای کارآمد ، خواندن برنامه های اجرای و فهرست کردن مسیرهای دسترسی وجود ندارد ، بهترین بانک اطلاعاتی برای یک بار کاری خاص انجام نمی شود. یک بانک اطلاعاتی دیگر ممکن است به اندازه کافی خوب باشد که با سازمان توسعه متناسب باشد و تجربه توسعه دهنده بهتری را فراهم کرده و بهینه سازی را ساده می کند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا