بياناتنا شبه المهيكلة كانت كابوساً: كيف أنقذنا دعم 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 قدمت لنا حلولاً أفضل وأكثر تكاملاً. 😉

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

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

أبو عمر

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

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

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

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

آخر المدونات

تجربة المستخدم والابداع البصري

تطبيقاتنا كانت تستبعد الملايين: كيف أنقذتنا ‘الوصولية الرقمية’ (a11y) من جحيم التجربة المنقوصة؟

أنا أبو عمر، وقبل سنوات، كنت أنا وفريقي نبني تطبيقات جميلة لكنها عمياء عن فئة كبيرة من المستخدمين. هذه حكايتي مع "الوصولية الرقمية" (a11y)، وكيف...

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

عمليات الدفع المتكررة كانت كارثة: كيف أنقذتنا ‘مفاتيح عدم التكرار’ (Idempotency Keys) من جحيم الطلبات المزدوجة؟

في عالم الشبكات غير الموثوق، الطلبات المزدوجة ليست احتمالاً، بل هي حتمية. أحكي لكم من واقع التجربة كيف أن "مفتاح عدم التكرار" (Idempotency Key) هو...

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

فاتورتنا السحابية كانت تلتهم ميزانيتنا: كيف أنقذتنا ‘الحوسبة بلا خوادم’ (Serverless) من جحيم الموارد الخاملة؟

أشارككم قصة حقيقية من قلب الميدان، عندما كانت فاتورة الحوسبة السحابية تهدد مشروعنا. سأشرح كيف أنقذتنا بنية "Serverless" مثل AWS Lambda، وحولت التكاليف الباهظة إلى...

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

مقابلاتنا التقنية كانت مسرحية: كيف أنقذتنا ‘البرمجة الثنائية’ من جحيم ألغاز السبورة البيضاء؟

أتذكر جيدًا ذلك اليوم الذي كاد أن يفقدنا مهندسًا عبقريًا بسبب لوح أبيض وسؤال سخيف عن الخوارزميات. هذه المقالة هي قصة كيف تخلينا عن مسرحيات...

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

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

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

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

إدارة التكوينات كانت فوضى: كيف أنقذنا Ansible من جحيم ‘انحراف الخوادم’ (Server Drift)؟

أشارككم قصة حقيقية من قلب المعاناة مع "انحراف الخوادم" (Server Drift)، وكيف تحولنا من الفوضى اليدوية إلى النظام والتحكم الكامل باستخدام أداة Ansible. هذه ليست...

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

كنا نخسر أفضل مهندسينا: كيف أنقذنا ‘المسار الوظيفي المزدوج’ من جحيم ‘إما مدير أو لا شيء’؟

كنا ندفع بأمهر مبرمجينا نحو الإدارة قسراً، فنخسرهم كمهندسين ونكسبهم كمدراء فاشلين. اكتشفوا كيف غيّر نموذج "المسار الوظيفي المزدوج" كل شيء، وحافظ على مواهبنا التقنية،...

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