يتناول هذا الدليل بعض المفاهيم الأساسية في بنية البيانات وأفضل الممارسات لتنظيم بيانات 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 بسرعة وكفاءة، حتى عندما تكون قائمة المستخدمين تتعدد المجموعات إلى الملايين أو عند تطبيق قواعد الأمان في Realtime Database تمنع الوصول إلى بعض السجلات.
وبهذه الطريقة، يتم عكس البيانات عن طريق إدراج المعرفات كمفاتيح وتعيين
إلى "صواب"، يجعل التحقق من مفتاح أمرًا بسيطًا مثل القراءة
/users/$uid/groups/$group_id
جارٍ التحقّق مما إذا كانت null
. الفهرس أسرع
وأكثر كفاءة من الاستعلام عن البيانات أو مسحها ضوئيًا.