هيكلة قاعدة البيانات

يتناول هذا الدليل بعض المفاهيم الأساسية في بنية البيانات وأفضل الممارسات لتنظيم بيانات JSON في Firebase Realtime Database.

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

طريقة تنظيم البيانات: عبارة عن شجرة JSON

يتم تخزين جميع بيانات "Firebase Realtime Database" كعناصر 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 المتاحة لمساعدتك في كتابة تعليمات برمجية أكثر قابلية للصيانة

أفضل الممارسات لهيكل البيانات

تجنب دمج البيانات

بما أنّ Firebase Realtime Database يسمح بتداخل البيانات حتى 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 الشخصي. إذًا لحذف أدا من المجموعة، يجب تحديثها في مكانين.

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

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

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