هيكلة البيانات باستخدام قاعدة بيانات Firebase Realtime لـ C++

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

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

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

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

يتم تخزين جميع بيانات قاعدة بيانات Firebase Realtime ككائنات 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 تسمح بتداخل البيانات حتى عمق 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
      }
    },
    ...
  }
}

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

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

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

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