برنامه نویسی

mysql: استفاده از داده های json و متنفر نبودن از آن

توسعه دهندگان بک اند برای ذخیره داده‌های json اغلب از firehose json استفاده می‌کنند و این داده‌ها را در ستون‌های مرتب‌شده دیتابیس خود ذخیره می‌کنند. اما در برخی موارد، ذخیره یک شی یا آرایه json به عنوان یک حباب نیز مورد نیاز است. اما MySQL امکان استفاده از json به عنوان یک نوع داده بومی را فراهم می‌کند که اجازه می‌دهد با داده‌های json به روشی جذاب کار کنیم، به‌جای استفاده از ستون‌های text. می‌توانیم مقادیر از json استخراج کنیم و از آنها در WHERE بندها استفاده کنیم.

برای استخراج مقادیر از json در MySQL از تابع JSON_EXTRACT استفاده می‌شود که دو آرگومان، یعنی ستون و مسیر مورد نظر را می‌گیرد. همچنین می‌توان از عملگر -> برای جستجوی سریعتر در json استفاده کرد. همچنین می‌توان از JSON_UNQUOTE برای حذف نقل قول از مقادیر استخراج شده استفاده کرد. می‌توان همچنین با استفاده از عملگر ** و wildcard اطلاعات از آرایه‌ها استخراج کرد.

برای شبیه‌سازی WHERE IN بندها از عملگر MEMBER OF استفاده می‌شود که این عملگر به شکلی مشابه تابع عمل می‌کند و به مقدار سمت چپ آن می‌گوید آیا در آرایه json موجود است یا خیر. این روش بهتر از استفاده از WHERE IN برای استخراج اطلاعات از json در MySQL است. همچنین این روش برای کار با TEXT ستون‌ها نیز معتبر است.

در کل، برای استخراج اطلاعات از json و استفاده از آنها در WHERE بندها، استفاده از json به عنوان یک نوع داده بومی در MySQL موثرتر و سریعتر خواهد بود و بهتر است از این روش استفاده شود تا از TEXT ستون‌ها استفاده شود.

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

در تاریکی، روزهای قدیم، این json به یک text ستون، که اگر تنها کاری که ما نیاز داشتیم این بود که آن را ذخیره کرده و برگردانیم خوب بود. اما اگر لازم بود کار پیچیده‌تری انجام دهیم، مثلاً مقادیر خاصی را از آن json استخراج کنیم یا خدای ناکرده از یکی در یک استفاده کنیم. WHERE بند، همه چیز می تواند خیلی سریع از کنترل خارج شود.

خوشبختانه، mysql مدرن پشتیبانی می کند JSON به عنوان یک نوع داده بومی، به ما امکان می دهد با داده های json به روشی تقریبا لذت بخش کار کنیم. در این پست می خواهیم به استخراج مقادیر از json و استفاده از داده های json در آن بپردازیم WHERE بندها

ایجاد یک JSON ستون

بیایید با ایجاد یک جدول با یک ستون از نوع شروع کنیم JSON.

CREATE TABLE `some_data` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL,
  `some_json` json NULL,
  `some_text` text NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
وارد حالت تمام صفحه شوید

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

داده‌های آزمایشی که قرار است استفاده کنیم، نمایش json تک‌آهنگ «بوس و سوار» bratmobile است.[https://www.discogs.com/master/409458-Bratmobile-Kiss-And-Ride].

{
  "artist": "Bratmobile",
  "title": "Kiss and Ride",
  "format": "single",
  "year": 1992,
  "label": "Kill Rock Stars",
  "tracks": {
    "A": [
      {
        "title": "Kiss and Ride",
        "duration_seconds": 88
      }
    ],
    "B": [
      {
        "title": "No You Don't",
        "duration_seconds": 105
      },
      {
        "title": "Queenie",
        "duration_seconds": 79
      }
    ]
  }
}
وارد حالت تمام صفحه شوید

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

ما آن json را در هر دو یک Native قرار می دهیم JSON ستون و همچنین در a تایپ کنید TEXT ستون بعداً به آن col متن باز خواهیم گشت.

INSERT INTO some_data VALUES(
  null,
  'some name',
  -- as json
  '{ "artist": "Bratmobile", "title": "Kiss and Ride", "format": "single", "year": 1992, "label": "Kill Rock Stars", "tracks": { "A": [ { "title": "Kiss and Ride", "duration_seconds": 88 } ], "B": [ { "title": "No You Don\'t", "duration_seconds": 105 }, { "title": "Queenie", "duration_seconds": 79 } ] } }',
  -- as string
  '{ "artist": "Bratmobile", "title": "Kiss and Ride", "format": "single", "year": 1992, "label": "Kill Rock Stars", "tracks": { "A": [ { "title": "Kiss and Ride", "duration_seconds": 88 } ], "B": [ { "title": "No You Don\'t", "duration_seconds": 105 }, { "title": "Queenie", "duration_seconds": 79 } ] } }'
);
وارد حالت تمام صفحه شوید

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

استخراج برخی از داده ها از آن json

اکنون که مقداری json در خود داریم JSON ستون، ما می توانیم روی انتخاب مقادیر فردی کار کنیم. ما این کار را با mysql انجام می دهیم JSON_EXTRACT تابع.

JSON_EXTRACT دو آرگومان می گیرد: ستون و مسیر عنصری که می خواهید انتخاب کنید. مسیرها با یک نقطه از هم جدا می شوند، به همان روشی که مسیرهای دایرکتوری با a جدا می شوند /، و سطح بالای مسیر با نشان داده می شود $. اگر تا به حال با آن مبارزه کرده اید jq، باید احساس راحتی کنید که با این دست و پنجه نرم کنید.

در این مثال، ما را انتخاب می کنیم artist عنصر از سطح بالای شی json ما.

SELECT  JSON_EXTRACT(some_json, '$.artist') as artist
FROM    some_data
WHERE   id = 1;
+--------------+
| artist       |
+--------------+
| "Bratmobile" |
+--------------+
وارد حالت تمام صفحه شوید

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

mysql نیز فراهم می کند -> عملگر به عنوان مخفف JSON_EXTRACT کمک کند تا سوالات ما کمی تمیزتر شود.

SELECT  some_json -> '$.artist' as artist
FROM    some_data
WHERE   id = 1;
+--------------+
| artist       |
+--------------+
| "Bratmobile" |
+--------------+
وارد حالت تمام صفحه شوید

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

مزاحمتی که بلافاصله با این نتایج متوجه می‌شویم این است که آنها به صورت نقل قول هستند. ما این را نمی خواهیم هیچ کس این را نمی خواهد اجازه دهید آنها را با تابع حذف کنیم JSON_UNQUOTE

SELECT  JSON_UNQUOTE(JSON_EXTRACT(some_json, '$.artist')) as artist
FROM    some_data
WHERE   id = 1;
+------------+
| artist     |
+------------+
| Bratmobile |
+------------+
وارد حالت تمام صفحه شوید

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

کوتاه نویسی -> عملگری که قبلا استفاده کردیم را می توان به آن تغییر داد ->> به طور خودکار شامل شود JSON_UNQUOTE. استفاده از این اپراتور مانند محصور کردن یک تماس است JSON_EXTRACT که در JSON_UNQUOTE:

SELECT  some_json ->> '$.artist' as artist
FROM    some_data
WHERE   id = 1;
+------------+
| artist     |
+------------+
| Bratmobile |
+------------+
وارد حالت تمام صفحه شوید

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

برخورد با آرایه ها

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

می توانیم در مسیری که به آن پاس می دهیم به آرایه ها ارجاع دهیم JSON_EXTRACT با استفاده از یک شاخص در داخل پرانتز. این چیزهای بسیار آشنا است به عنوان مثال، در داده های نمونه خود، یک شی داریم که به آن نامیده می شود tracks که شامل دو آرایه است که به عنوان کلید شده است A و B; این ها آهنگ های طرف “a” و “b” هستند. آن ها A و B عناصر آرایه هایی از اشیاء آهنگ هستند. اگر می خواستیم اولین آهنگ ساید را بگیریم B، ما خود را می سازیم JSON_EXTRACT اینطوری زنگ بزن:

SELECT  JSON_EXTRACT(some_json, '$.tracks.B[0].title') as side_b_song_one
FROM    some_data
WHERE   id = 1;
+-----------------+
| side_b_song_one |
+-----------------+
| "No You Don't"  |
+-----------------+
وارد حالت تمام صفحه شوید

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

با نگاه کردن به آرگومان مسیر، می‌توانیم ببینیم که از بالای سلسله مراتب شروع می‌کنیم $، سپس به tracks هدف – شی. بعدی آرایه است B. نشان می دهیم که اولین عنصر این آرایه را با آن می خواهیم [0]، سپس با دادن کلید مسیر خود را ادامه دهید title. نتیجه عنوان اولین آهنگ سمت b است.

اگر ما بخواهیم همه آهنگ های سمت b، می توانیم آن شاخص را با علامت عام جایگزین کنیم *. این یک آرایه json از عناوین را برمی گرداند.

SELECT  JSON_EXTRACT(some_json, '$.tracks.B[*].title') as side_b
FROM    some_data
WHERE   id = 1;
+-----------------------------+
| side_b                      |
+-----------------------------+
| ["No You Don't", "Queenie"] |
+-----------------------------+
وارد حالت تمام صفحه شوید

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

ما همچنین می توانیم استفاده کنیم ** wildcard برای انجام این کار.

SELECT  JSON_EXTRACT(some_json, '$.tracks.B**.title') as side_b
FROM    some_data
WHERE   id = 1;
+-----------------------------+
| side_b                      |
+-----------------------------+
| ["No You Don't", "Queenie"] |
+-----------------------------+
وارد حالت تمام صفحه شوید

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

مهم است که توجه داشته باشید ** در واقع یعنی همه مسیرهای بین پیشوند و پسوند. این بدان معنی است که ما می توانیم از آن برای دریافت همه موارد استفاده کنیم title عناصر زیر tracks، صرف نظر از اینکه آیا در آرایه کلید خورده است یا خیر A یا B مانند:

SELECT  JSON_EXTRACT(some_json, '$.tracks**.title') as side_a_and_side_be
FROM    some_data
WHERE   id = 1;
+----------------------------------------------+
| side_a_and_side_be                           |
+----------------------------------------------+
| ["Kiss and Ride", "No You Don't", "Queenie"] |
+----------------------------------------------+
وارد حالت تمام صفحه شوید

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

در آن مثال، ** با هر دو مطابقت دارد A و B. چیزهای بسیار قدرتمند

ما همچنین می توانیم در هنگام تعریف شاخص های آرایه خود از محدوده استفاده کنیم. به عنوان مثال، اگر ما می خواستیم همه را بدست آوریم titleدر کنار B بین موقعیت صفر و یک، می‌توانیم از شاخص استفاده کنیم [0 to 1]:

SELECT  JSON_EXTRACT(some_json, '$.tracks.B[0 to 1].title') as side_b_first_two
FROM    some_data
WHERE   id = 1;
+-----------------------------+
| side_b_first_two            |
+-----------------------------+
| ["No You Don't", "Queenie"] |
+-----------------------------+
وارد حالت تمام صفحه شوید

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

و اگر تنها چیزی که می خواهیم آخرین عنصر یک آرایه باشد، mysql به ما اجازه می دهد از کلمه تحت اللفظی استفاده کنیم last:

SELECT  JSON_EXTRACT(some_json, '$.tracks.B[last].title') as side_b_last_track
FROM    some_data
WHERE   id = 1;
+-------------------+
| side_b_last_track |
+-------------------+
| "Queenie"         |
+-------------------+
وارد حالت تمام صفحه شوید

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

با استفاده از این در WHERE بندها

استخراج داده‌ها از json مفید است، اما در واقع، می‌توانیم کل ستون را انتخاب کرده و داده‌ها را اجرا کنیم json_decode در کنترل کننده ما را واقعی پول از این استفاده می کند WHERE بندها بیایید نگاه بیندازیم:

SELECT  JSON_EXTRACT(some_json, '$.title') as title
FROM    some_data
WHERE   JSON_EXTRACT(some_json, '$.year') = 1992;
+-----------------+
| title           |
+-----------------+
| "Kiss and Ride" |
+-----------------+
وارد حالت تمام صفحه شوید

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

در اینجا ما فقط استخراج کردیم year از ستون json ما و در a استفاده می شود WHERE عبارت.

یک کلاژ برای شبیه سازی WHERE IN بندها

اگر بخواهیم الف بنویسیم WHERE IN بند در برابر یک آرایه در شی json ما، همه چیز کمی پیچیده تر می شود. ایده آل اولیه ما ممکن است این باشد که به سادگی استفاده کنیم IN در یک آرایه استخراج شده از json ما:

-- this absolutely DOES NOT WORK
SELECT  JSON_EXTRACT(some_json, '$.title') as title
FROM    some_data
WHERE   "No You Don't" IN JSON_EXTRACT(some_json, '$.tracks**.title');
وارد حالت تمام صفحه شوید

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

بدون تاس. البته مشکل اینجاست که WHERE IN لیستی از مقادیر را انتظار دارد، نه یک آرایه json. یک عدم تطابق نوع وجود دارد.

راه حل استفاده است MEMBER OF.

این سازه به نوعی مانند یک تابع و به نوعی مانند یک عملگر رفتار می کند. مقدار سمت چپ آن را می گیرد و آزمایش می کند که آیا در آرایه json که به عنوان آرگومان به آن ارسال می شود قرار دارد یا خیر.

SELECT  JSON_UNQUOTE(JSON_EXTRACT(some_json, '$.artist')) as artist,
        JSON_UNQUOTE(JSON_EXTRACT(some_json, '$.title')) as title
FROM    some_data
WHERE   "Queenie" MEMBER OF(CAST(JSON_EXTRACT(some_json, '$.tracks**.title') AS json));
+------------+---------------+
| artist     | title         |
+------------+---------------+
| Bratmobile | Kiss and Ride |
+------------+---------------+
وارد حالت تمام صفحه شوید

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

چیزی که در اینجا باید به آن توجه کرد، استدلال به است MEMBER OF. ما همه را استخراج کرده ایم title مقادیر از ستون های json ما و آنها را با هم در یک آرایه له می کنیم. هر چند که «آرایه» واقعاً یک آرایه نیست. این یک رشته است و نه حتی یک رشته json، فقط یک رشته.

از آنجا که MEMBER OF به داده های سبک json به عنوان آرگومان نیاز دارد، باید فراخوانی کنیم CAST تا رشته آرایه خود را به یک آرایه json واقعی تبدیل کنیم.

این راه حل کمی آشفته است، و اگر من درخت اشتباهی را در اینجا پنهان می کنم و راه بهتر و ظریف تری برای انجام این کار وجود دارد، من بسیار آماده ورود هستم. با این حال، این ساختار کار می کند.

صبر کن. این کار می کند TEXT ستون ها هم؟

راز این است که همه چیزهایی که در اینجا پوشش داده ایم روی آنها نیز کار می کنند TEXT ستون ها. اگر آن را اجرا کنیم WHERE IN در مقابل ما انتخاب کنید some_text ستون، خوب کار می کند:

SELECT  JSON_UNQUOTE(JSON_EXTRACT(some_json, '$.artist')) as artist,
        JSON_UNQUOTE(JSON_EXTRACT(some_json, '$.title')) as title
FROM    some_data
WHERE   "Queenie" MEMBER OF(CAST(JSON_EXTRACT(some_text, '$.tracks**.title') AS json));
+------------+---------------+
| artist     | title         |
+------------+---------------+
| Bratmobile | Kiss and Ride |
+------------+---------------+
وارد حالت تمام صفحه شوید

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

پس چرا با JSON اصلا نوع ستون؟ خوب، مهمتر از همه، زمانی که ما از JSON تایپ کنید که در درج یا به روز رسانی اعتبار سنجی خودکار دریافت می کنیم. می توانیم invliad json را در آن قرار دهیم TEXT ستون ها و mysql هرگز شکایت نمی کنند. فقط منتظر خواهد ماند تا در زمان معینی ناامید شویم.

دوم، بومی JSON نوع بسیار کارآمدتر است. آیا می‌توانیم از مقادیر json استخراج‌شده در پیوند پنج جدولی استفاده کنیم؟ آره. اگر از آن استفاده کنیم به طرز دردناکی کند می شود؟ TEXT? همچنین بله بومی JSON بسیار سریعتر خواهد بود.

در نهایت، تقریباً به طور قطع توابع جدید json در نسخه‌های آینده mysql وجود خواهد داشت (شامل، امیدوارم، روشی منطقی‌تر برای انجام WHERE INs) و هیچ تضمینی وجود ندارد که آنها رفتار خوبی با آنها داشته باشند TEXT. خط پایانی: اگر می دانید که json را در یک ستون قرار می دهید، آن را a JSON ستون

🔎 این پست در اصل در وبلاگ فنی گرنت هوروود نوشته شده است

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

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

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

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