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

قبل البدء

قبل أن تتمكّن من استخدام Realtime Database, عليك إجراء ما يلي:

  • سجِّل مشروعك على Unity واضبطه لاستخدام Firebase.

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

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

  • أضِف حزمة تطوير البرامج (SDK) لنظام التشغيل Unity Firebase (خاصةً FirebaseDatabase.unitypackage) إلى لمشروع Unity الخاص بك.

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

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

يتناول هذا الدليل بعض المفاهيم الأساسية في بنية البيانات وأفضل الممارسات لتنظيم بيانات 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، يمكن تمثيل البيانات المخزّنة في قاعدة البيانات كأنواع أصلية معيّنة تتوافق مع أنواع 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": { ... }
  }
}

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

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

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

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

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

ما عليك سوى توفير طريقة أنيقة لعرض المجموعات التي ينتمي إليها المستخدم و 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. إنّ الفهرس أسرع وأكثر فعالية بكثير من طلب البيانات أو فحصها.

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