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

قبل البدء

لاستخدام Realtime Database، عليك إجراء ما يلي:

  • سجِّل مشروع Unity وأعدّه لاستخدام Firebase.

    • إذا كان مشروعك على Unity يستخدم Firebase، يعني ذلك أنّه سبق تسجيله وإعداده لاستخدام Firebase.

    • إذا لم يكن لديك مشروع Unity، يمكنك تنزيل نموذج تطبيق.

  • أضِف Firebase حزمة تطوير البرامج (SDK) Unity (على وجه التحديد، FirebaseDatabase.unitypackage) إلى مشروعك على Unity.

تجدر الإشارة إلى أنّ إضافة Firebase إلى مشروعك على Unity تتضمن مهامًا في كلّ من وحدة تحكّم Firebase ومشروع Unity المفتوح (على سبيل المثال، يمكنك تنزيل ملفات إعدادات Firebase من وحدة التحكّم، ثم نقلها إلى مشروع Unity).

هيكلة البيانات

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

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

بنية البيانات: هي شجرة JSON

يتم تخزين جميع بيانات "Firebase Realtime Database" كعناصر JSON. يمكنك اعتبار قاعدة البيانات شجرة JSON مستضافة على السحابة الإلكترونية. على عكس قاعدة بيانات SQL، لا تتضمّن قاعدة بيانات NoSQL جداول أو سجلّات. عند إضافة بيانات إلى شجرة 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 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": { ... }
  }
}

مع هذا التصميم المتداخل، يصبح التكرار عبر البيانات مشكلة. على سبيل المثال، يتطلّب إدراج عناوين المحادثات في Chat تنزيل شجرة 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": { ... }
  }
}

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

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

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

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

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

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

// 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 الشخصي. وبالتالي، لحذف Ada من المجموعة، يجب تعديلها في مكانَين.

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

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

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