بياناتنا شبه المهيكلة كانت كابوساً: كيف أنقذنا دعم JSONB من جحيم نماذج EAV المعقدة؟

يا جماعة الخير، السلام عليكم ورحمة الله. معكم أخوكم أبو عمر.

خلوني أحكيلكم قصة صارت معي قبل كم سنة، قصة فيها ورطة كبيرة ودرس تعلمته بالطريقة الصعبة. كنا شغالين على نظام إدارة محتوى متطور لعميل، نظام فيه أنواع مختلفة من “الأشياء”: مقالات، منتجات، فعاليات، وصفات طبخ… يعني كوكتيل ما شاء الله. المشكلة كانت إنه كل نوع من هالأشياء إله خصائص مختلفة تماماً. المقال إله “كاتب” و”عدد كلمات”، والمنتج إله “سعر” و”لون” و”مقاس”، والفعالية إلها “تاريخ بداية” و”موقع”.

في البداية، أنا وفريقي فكرنا حالنا أذكياء وقلنا: “بسيطة! بنستخدم نموذج EAV أو Entity-Attribute-Value”. يعني جدول للكيانات (مقالات، منتجات)، وجدول للخصائص (كاتب، لون، سعر)، وجدول ثالث يربط بينهم ويحط القيمة. شعرنا بقمة العبقرية، حسينا حالنا حلينا كل مشاكل المستقبل. يا ريتنا ما حليناها! بعد كم شهر، لما كبرت قاعدة البيانات، صارت الاستعلامات البسيطة كابوس. عشان تجيب منتج مع كل خصائصه، بدك تعمل JOINs بالهبل. وإذا بدك تبحث عن “كل القمصان الحمراء مقاس لارج”، يا ساتر! الاستعلام كان يطلع أطول من خطبة الجمعة، وأبطأ من سلحفاة طالعة جبل. صرنا في ورطة حقيقية، والنظام صار بطيء والعميل بلش يتذمر. كانت أيام صعبة، وكنت كل ما أفتح كود الاستعلامات أحكي لحالي: “شو هاد يا أبو عمر؟ كيف الأمور وصلت لهون؟”.

ومن هداك اليوم، تعلمت درس مهم جداً عن نمذجة البيانات، وعن قوة الأدوات الحديثة اللي بين إيدينا لو عرفنا نستخدمها صح. واليوم بدي أشاركم هالتجربة، وكيف كان الحل في أداة رائعة اسمها JSONB.

ما هو جحيم نموذج EAV (الكيان-السمة-القيمة)؟

قبل ما نحكي عن المنقذ، خلينا نفهم طبيعة الوحش اللي كنا بنحاربه. نموذج EAV هو أسلوب لتصميم قواعد البيانات يسمح بمرونة عالية في تحديد خصائص الكيانات. بدل ما تحط كل الخصائص كأعمدة في جدول واحد، بتقسمهم لثلاث جداول رئيسية:

  • Entities (الكيانات): جدول بسيط يحتوي على الكيانات الأساسية. مثلاً، جدول `products` فيه `id` و `name`.
  • Attributes (السمات/الخصائص): جدول يحتوي على كل الخصائص الممكنة. مثلاً، جدول `attributes` فيه `id` و `name` (مثل ‘لون’، ‘مقاس’، ‘ذاكرة RAM’).
  • Values (القيم): الجدول اللي بربط كل اشي ببعضه. يحتوي على `entity_id`, `attribute_id`, و `value` (القيمة الفعلية للخاصية).

مثال عملي على هيكل EAV

لنفترض عنا منتجين: قميص (T-Shirt) ولابتوب.

جدول `products`:


id | name
---|-----------
1  | T-Shirt
2  | Laptop

جدول `attributes`:


id | name
---|-----------
1  | color
2  | size
3  | ram
4  | storage

جدول `product_values`:


product_id | attribute_id | value
-----------|--------------|---------
1          | 1            | 'Red'
1          | 2            | 'L'
2          | 3            | '16GB'
2          | 4            | '512GB SSD'

الألم الحقيقي: الاستعلامات المعقدة

على الورق، الشكل حلو ومرن. لكن تعال جرب استعلم عن البيانات. لو بدك تجيب كل خصائص اللابتوب (منتج رقم 2)، بدك تعمل اشي زي هيك:


SELECT
    p.name AS product_name,
    a.name AS attribute_name,
    pv.value AS attribute_value
FROM
    products p
JOIN
    product_values pv ON p.id = pv.product_id
JOIN
    attributes a ON pv.attribute_id = a.id
WHERE
    p.id = 2;

لهون، ممكن تمشي. لكن الكابوس ببلش لما بدك تبحث بناءً على خصائص متعددة. مثلاً: “أعطيني كل المنتجات اللي لونها ‘Red’ ومقاسها ‘L'”.

شوف هالكود البشع (والبطيء):


SELECT p.*
FROM products p
WHERE EXISTS (
    SELECT 1
    FROM product_values pv
    JOIN attributes a ON pv.attribute_id = a.id
    WHERE pv.product_id = p.id AND a.name = 'color' AND pv.value = 'Red'
) AND EXISTS (
    SELECT 1
    FROM product_values pv
    JOIN attributes a ON pv.attribute_id = a.id
    WHERE pv.product_id = p.id AND a.name = 'size' AND pv.value = 'L'
);

هذا استعلام بسيط نسبياً، ومع هيك صار معقد وبطيء جداً مع نمو البيانات لأنه بيعمل مسح (scan) للجدول الضخم `product_values` أكثر من مرة. تخيل لو عندك 5 شروط بحث! الوضع بصير “مش زابط بالمرة”.

المنقذ القادم من المستقبل: دعم JSONB الأصلي في PostgreSQL

وهنا يأتي دور PostgreSQL، قاعدة البيانات الرائعة اللي بتفاجئنا دايماً بميزاتها القوية. واحدة من أقوى هذه الميزات هي الدعم الأصلي لبيانات JSON، وتحديداً نوع البيانات JSONB.

ما الفرق بين JSON و JSONB؟

باختصار شديد:

  • JSON: يخزن النص كما هو بالضبط. هو مجرد حقل نصي، وقاعدة البيانات لا تفهم محتواه. سريع في الإدخال، بطيء جداً في الاستعلام.
  • JSONB: يخزن البيانات بصيغة “ثنائية” (Binary). عند الإدخال، PostgreSQL تقوم بتحليل الـ JSON، وتفهم هيكله، وتخزنه بطريقة محسّنة جداً للاستعلام. هذا يسمح بإنشاء فهارس (indexes) متقدمة عليه، مما يجعل البحث داخله سريعاً جداً.

نصيحة من أبو عمر: دائماً وأبداً، استخدم JSONB وليس JSON إلا إذا كان عندك سبب قوي جداً ومحدد (مثل الحفاظ على ترتيب المفاتيح أو الفراغات في النص الأصلي)، وهذا نادر جداً.

إعادة بناء الهيكل باستخدام JSONB

الآن، دعونا ننسى كل الجداول المعقدة السابقة ونعيد بناء جدول المنتجات باستخدام JSONB. كل ما نحتاجه هو جدول واحد بسيط:

جدول `products_new`:


CREATE TABLE products_new (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    attributes JSONB
);

فقط! جدول واحد بسيط. الخصائص الديناميكية كلها ستعيش داخل حقل `attributes` من نوع JSONB.

كيف سنقوم بإدخال بياناتنا الآن؟


INSERT INTO products_new (name, attributes) VALUES
('T-Shirt', '{"color": "Red", "size": "L", "material": "Cotton"}'),
('Laptop', '{"ram": "16GB", "storage": "512GB SSD", "screen_size": 14}');

لاحظ كيف أن كل منتج له مجموعة خصائص مختلفة تماماً، وكلها في نفس الحقل وبنفس الجدول. يا سلام على البساطة!

سحر الاستعلامات مع JSONB

الآن لنرى كيف أصبحت الاستعلامات التي كانت كابوساً في الماضي، بسيطة وسريعة.

للحصول على قيمة خاصية معينة (مثلاً، لون القميص):

نستخدم المعامل `->>`. السهم الأول `->` يجلب القيمة كـ JSON، والسهمين `->>` يجلبها كنص.


SELECT name, attributes ->> 'color' AS color
FROM products_new
WHERE name = 'T-Shirt';

والآن، الاستعلام السحري (البحث عن قميص أحمر مقاس L):

هنا نستخدم المعامل `@>` الذي يعني “يحتوي على”. هذا المعامل هو بطل القصة كلها.


SELECT *
FROM products_new
WHERE attributes @> '{"color": "Red", "size": "L"}';

شفت الفرق؟ شفت البساطة والجمال؟ استعلام واحد، واضح، ومباشر. لا يوجد `JOIN` ولا `EXISTS` ولا أي تعقيدات. هذا ليس مجرد كود أجمل، بل هو أسرع بمرات لا تحصى، خصوصاً مع وجود فهرس.

لا تنسَ الفهرس! (The GIN Index)

لكي تحصل على أقصى سرعة من استعلامات JSONB، يجب عليك إنشاء فهرس من نوع GIN (Generalized Inverted Index). هذا الفهرس مصمم خصيصاً للبيانات التي تحتوي على عناصر متعددة داخل حقل واحد، مثل المصفوفات أو JSONB.


CREATE INDEX idx_products_attributes ON products_new USING GIN (attributes);

بمجرد إنشاء هذا الفهرس، ستعمل استعلامات البحث التي تستخدم المعامل `@>` بسرعة البرق، حتى لو كان لديك ملايين السجلات.

نصائح عملية من خبرة أبو عمر

بعد ما انتقلنا من جحيم EAV لنعيم JSONB، تعلمت كم درس مهم بحب أشاركم إياها:

  1. JSONB ليس حلاً لكل شيء: لا تقع في فخ وضع كل شيء في حقل JSONB. إذا كانت لديك خاصية ثابتة وموجودة دائماً لكل المنتجات (مثل `price` أو `sku`)، فمن الأفضل والأكثر كفاءة أن تكون عموداً خاصاً بها في الجدول. استخدم JSONB للبيانات “شبه المهيكلة” فعلاً، أي الخصائص التي تختلف من كيان لآخر.
  2. الفهرسة هي مفتاح الأداء: بدون فهرس GIN، أداء JSONB سيكون سيئاً مع البيانات الكبيرة. تذكر دائماً: “بدون فهرسة، أنت بتسبح في بحر بدون شط”. الفهرس هو الذي يجعل السحر يحدث.
  3. ضع بعض القواعد: مرونة JSONB لا تعني الفوضى. حاول أن تضع بعض القواعد والتناسق في مفاتيح الـ JSON التي تستخدمها. مثلاً، لا تستخدم `color` مرة و `Color` مرة أخرى. هذا التناسق يسهل عليك كتابة الكود في طبقة التطبيق (Application Layer).
  4. فكر في التحقق من صحة البيانات (Validation): يمكنك استخدام `CHECK constraints` في PostgreSQL للتأكد من أن البيانات المدخلة في حقل JSONB تتبع هيكلاً معيناً إذا احتجت لذلك، أو يمكنك القيام بهذا التحقق في كود التطبيق قبل إرسال البيانات إلى قاعدة البيانات.

الخلاصة، يا جماعة الخير

تجربتنا في الانتقال من نموذج EAV إلى JSONB كانت بمثابة نقلة نوعية. تخلصنا من استعلامات معقدة وبطيئة، وحصلنا على نظام أبسط، أسرع، وأسهل بكثير في الصيانة والتطوير. EAV كان فكرة جيدة في وقتها، لكن قواعد البيانات الحديثة مثل PostgreSQL قدمت لنا حلولاً أفضل وأكثر تكاملاً. 😉

نصيحتي الأخيرة لكل مطور ومبرمج: لا تخف من استكشاف الميزات المتقدمة في الأدوات التي تستخدمها. اقرأ التوثيق، جرب أشياء جديدة، ولا تلتزم بطريقة واحدة فقط لأنك “اعتدت عليها”. العالم يتغير، والبرمجة تتطور، وعلينا أن نتطور معها. فاستخدام الأداة الصحيحة للمشكلة الصحيحة هو ما يميز المطور الخبير عن غيره.

والله ولي التوفيق.

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

برمجة وقواعد بيانات

كانت تحديثات قاعدة البيانات كابوساً: كيف أنقذتنا أدوات الترحيل (Migrations) من جحيم التعديلات اليدوية؟

هل عانيت يوماً من تحديث مخطط قاعدة البيانات يدوياً بين فريقك؟ أبو عمر يشارككم قصة حقيقية حول كيف غيّرت أدوات الترحيل (Migrations) طريقة عمل فريقه،...

17 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت خوادمنا تستجدي التحديثات: كيف أنقذتنا ‘خطاطيف الويب’ (Webhooks) من جحيم الاستقصاء المستمر (Polling)؟

تخيل خوادمك تلهث من كثرة الطلبات غير الضرورية. في هذه المقالة، أشارككم قصة حقيقية من الميدان حول كيفية انتقالنا من جحيم الاستقصاء المستمر (Polling) إلى...

17 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت بنيتنا التحتية قصراً من رمال: كيف أنقذتنا “البنية التحتية ككود” (IaC) من جحيم البيئات المتضاربة؟

أشارككم قصة حقيقية عن ليلة كادت أن تنهار فيها شركتنا بسبب الفوضى في البنية التحتية، وكيف كانت "البنية التحتية ككود" (IaC) طوق النجاة الذي انتشلنا...

17 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كان ملفي الشخصي مقبرة لمشاريع الدورات: كيف أنقذتني ‘المساهمة في المصادر المفتوحة’ من جحيم الرفض التلقائي؟

هل تشعر أن ملفك الشخصي على GitHub لا يعكس قدراتك الحقيقية؟ في هذه المقالة، أشاركك يا صديقي تجربتي الشخصية، أنا أبو عمر، وكيف انتقلت من...

17 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت قاعدة بياناتنا تتوسل الرحمة: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات البطيئة

قصة حقيقية من واقع العمل عن كيفية انهيار نظامنا تحت ضغط الاستعلامات المتكررة، وكيف كان التخزين المؤقت (Caching) هو طوق النجاة. مقالة عملية للمطورين تشرح...

17 مايو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

كان التحقق من هوية عملائنا يستغرق أياماً: كيف أنقذنا الذكاء الاصطناعي (eKYC) من جحيم الإجراءات اليدوية؟

بصفتي مبرمجاً فلسطينياً، سأروي لكم حكايتنا مع كابوس التحقق اليدوي من هوية العملاء (KYC) وكيف كانت رحلة الانتقال إلى التحقق الإلكتروني (eKYC) باستخدام الذكاء الاصطناعي...

17 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت أعطالنا تكتشف بعد فوات الأوان: كيف أنقذنا Prometheus من جحيم المراقبة التفاعلية؟

أشارككم قصة حقيقية من قلب المعركة التقنية، كيف انتقلنا من فوضى الأعطال المفاجئة إلى نظام مراقبة استباقي باستخدام Prometheus وGrafana. هذه المقالة ليست مجرد شرح...

17 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

كانت فرقنا صامتة أمام الأخطاء: كيف أنقذتنا ‘السلامة النفسية’ من جحيم ثقافة اللوم؟

أشارككم قصة حقيقية من الميدان، عن اجتماع تحول إلى محكمة بسبب خطأ برمجي، وكيف كان هذا الموقف هو الشرارة التي قادتنا لاكتشاف "السلامة النفسية". هذه...

17 مايو، 2026 قراءة المزيد
البودكاست