تنظيم قاعدة البيانات

يتناول هذا الدليل بعض المفاهيم الأساسية في بنية البيانات وأفضل الممارسات لهيكلة بيانات JSON في "قاعدة بيانات Firebase في الوقت الفعلي".

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

كيفية تنظيم البيانات: إنها شجرة JSON

يتم تخزين جميع بيانات قاعدة بيانات Firebase في الوقت الفعلي ككائنات JSON. يمكنك التفكير في قاعدة البيانات على أنها شجرة JSON مستضافة على السحابة. على عكس قاعدة بيانات SQL، لا توجد جداول أو سجلات. عند إضافة بيانات إلى شجرة JSON، تصبح عقدة في بنية JSON الحالية مع مفتاح مرتبط بها. يمكنك تقديم مفاتيحك الخاصة، مثل أرقام تعريف المستخدمين أو الأسماء الدلالية، أو يمكن توفيرها لك باستخدام طريقة push().

إذا أنشأت مفاتيحك الخاصة، يجب أن تكون بترميز UTF-8، ويمكن أن تصل إلى 768 بايت كحد أقصى، ولا يمكن أن تتضمن .، أو $، أو #، أو [، أو ]، أو /، أو ASCII أحرف تحكم 0-31 أو 127. لا يمكنك استخدام أحرف تحكم ASCII في القيم نفسها أيضًا.

على سبيل المثال، ضع في اعتبارك تطبيقًا للدردشة يتيح للمستخدمين تخزين ملف شخصي أساسي وقائمة جهات اتصال. يقع الملف الشخصي النموذجي للمستخدم على مسار، مثل /users/$uid. قد يكون لدى المستخدم alovelace إدخال قاعدة بيانات يبدو على النحو التالي:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

على الرغم من أن قاعدة البيانات تستخدم شجرة JSON، يمكن تمثيل البيانات المخزنة في قاعدة البيانات كأنواع أصلية معينة تتوافق مع أنواع JSON المتاحة لمساعدتك في كتابة تعليمات برمجية أكثر قابلية للصيانة.

أفضل الممارسات المتعلقة ببنية البيانات

تجنُّب تداخل البيانات

نظرًا لأن قاعدة بيانات Firebase في الوقت الفعلي تسمح بدمج البيانات حتى عمق 32 مستوى، فقد تميل إلى الاعتقاد بأن هذا يجب أن يكون الهيكل الافتراضي. ومع ذلك، عند جلب البيانات في موقع ما في قاعدة البيانات، يمكنك أيضًا استرداد جميع العُقد الفرعية لها. بالإضافة إلى ذلك، عندما تمنح شخصًا ما إذن الوصول للقراءة أو الكتابة في عقدة في قاعدة البيانات، فإنك تمنحه أيضًا حق الوصول إلى جميع البيانات بموجب تلك العقدة. لذلك، من الناحية العملية، من الأفضل إبقاء بنية البيانات مسطحة قدر الإمكان.

للحصول على مثال عن سبب سوء البيانات المتداخلة، ضع في اعتبارك الهيكل المضاعف التالي التالي:

{
  // This is a poorly nested data architecture, because iterating the children
  // of the "chats" node to get a list of conversation titles requires
  // potentially downloading hundreds of megabytes of messages
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "messages": {
        "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
        "m2": { ... },
        // a very long list of messages
      }
    },
    "two": { ... }
  }
}

مع هذا التصميم المتداخل، يصبح التكرار عبر البيانات إشكاليًا. على سبيل المثال، يتطلب إدراج عناوين محادثات المحادثة تنزيل شجرة chats بالكامل، بما في ذلك كل الأعضاء والرسائل، إلى العميل.

تنظيم هياكل البيانات

إذا تم بدلاً من ذلك تقسيم البيانات إلى مسارات منفصلة تُسمى أيضًا إلغاء التطبيع، يمكن تنزيلها بكفاءة في استدعاءات منفصلة، حسب الحاجة. ضع في اعتبارك هذا الهيكل المسطح:

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // conversation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

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

إنشاء بيانات قابلة للقياس

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

في بعض الأحيان، تكون هذه العلاقة أكثر ديناميكية، أو قد يكون من الضروري إلغاء تطبيع هذه البيانات. يمكنك في كثير من الأحيان تغيير تنسيق البيانات باستخدام طلب بحث لاسترداد مجموعة فرعية من البيانات، كما هو موضَّح في مقالة استرداد البيانات.

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

المطلوب توفير طريقة أنيقة لإدراج المجموعات التي ينتمي إليها المستخدم وجلب البيانات لتلك المجموعات فقط. يمكن أن يساعد فهرس المجموعات بشكل كبير هنا:

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

قد تلاحظ أن هذا يكرر بعض البيانات عن طريق تخزين العلاقة ضمن سجل أدا وضمن المجموعة. الآن تمت فهرسة alovelace ضمن مجموعة، وتم إدراج techpioneers في ملف أدا الشخصي. لذا لحذف أدا من المجموعة، يجب تحديثها في مكانين.

هذا تكرار ضروري للعلاقات ثنائية الاتجاه. فهي تتيح لك استرجاع عضويات Ada بسرعة وكفاءة حتى عندما تتسع قائمة المستخدمين أو المجموعات إلى الملايين أو عندما تمنع قواعد الأمان في "قاعدة بيانات الوقت الفعلي" الوصول إلى بعض السجلات.

تهدف هذه الطريقة إلى عكس البيانات من خلال إدراج أرقام التعريف كمفاتيح وضبط القيمة على "صحيح"، ما يجعل عملية البحث عن المفتاح عملية سهلة مثل قراءة /users/$uid/groups/$group_id والتحقّق ممّا إذا كانت null. الفهرس أسرع وأكثر كفاءة بكثير من الاستعلام عن البيانات أو فحصها.

الخطوات التالية