ساختار پایگاه داده

این راهنما برخی‌از مفاهیم کلیدی در معماری داده و روال‌های مطلوب برای ساختاردهی داده‌های JSON در Firebase Realtime Database را پوشش می‌دهد.

ساختن پایگاه داده‌ای با ساختار مناسب به کمی دوراندیشی نیاز دارد. مهم‌تر از همه، باید برای نحوه ذخیره شدن داده‌ها و بازیابی بعدی آن‌ها برنامه‌ریزی کنید تا این فرایند تا حد امکان آسان شود.

نحوه ساختاردهی داده‌ها: به‌صورت درخت JSON است

همه داده‌های Firebase Realtime Database به‌عنوان اشیاء JSON ذخیره می‌شوند. می‌توانید پایگاه داده را به‌عنوان درخت JSON میزبانی‌شده در فضای ابری درنظر بگیرید. برخلاف پایگاه داده SQL، هیچ جدول یا رکوردی وجود ندارد. وقتی داده‌ها را به درخت JSON اضافه می‌کنید، این داده‌ها به گرهی در ساختار JSON موجود با کلید مرتبط تبدیل می‌شوند. می‌توانید کلیدهای خودتان را ارائه دهید، مثل شناسه‌های کاربر یا نام‌های معنایی، یا می‌توان آن‌ها را بااستفاده از childByAutoId برای شما ارائه داد.

برای مثال، برنامه گپی را درنظر بگیرید که به کاربران اجازه می‌دهد نمایه پایه و فهرست مخاطبین را ذخیره کنند. نمایه کاربر معمولی در مسیری مانند /users/$uid قرار دارد. کاربر alovelace ممکن است ورودی پایگاه داده‌ای داشته باشد که چیزی شبیه به این باشد:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { "..." },
    "eclarke": { "..." }
  }
}

اگرچه پایگاه داده از درخت JSON استفاده می‌کند، داده‌های ذخیره‌شده در پایگاه داده را می‌توان به‌عنوان انواع بومی خاصی که با انواع JSON موجود مطابقت دارند نشان داد تا به شما کمک کند کد قابل‌نگهداری‌تری بنویسید.

روال‌های مطلوب برای ساختار داده

از داده‌های تودرتو استفاده نکنید

چون Firebase Realtime Database امکان تودرتو کردن داده‌ها را تا ۳۲ سطح فراهم می‌کند، ممکن است وسوسه شوید که این ساختار را به‌عنوان ساختار پیش‌فرض درنظر بگیرید. بااین‌حال، وقتی داده‌ها را در مکانی در پایگاه داده‌تان واکشی می‌کنید، همه گره‌های فرزند آن را نیز بازیابی می‌کنید. علاوه‌براین، وقتی به فردی اجازه دسترسی خواندن یا نوشتن در گرهی از پایگاه داده‌تان می‌دهید، به او اجازه دسترسی به همه داده‌های زیر آن گره را نیز می‌دهید. بنابراین، در عمل، بهتر است ساختار داده‌هایتان را تا حد امکان مسطح نگه دارید.

برای نمونه‌ای از اینکه چرا داده‌های تودرتو بد هستند، ساختار تودرتوی چندگانه زیر را درنظر بگیرید:

{
  // 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 در نمایه آدا فهرست شده است. بنابراین برای حذف کردن آدا از گروه، باید در دو جا به‌روزرسانی شود.

این افزونگی برای روابط دوطرفه ضروری است. این API به شما امکان می‌دهد عضویت‌های Ada را به‌سرعت و به‌طور مؤثر واکشی کنید، حتی زمانی که فهرست کاربران یا گروه‌ها به میلیون‌ها نفر می‌رسد یا زمانی که قوانین امنیتی Realtime Database مانع دسترسی به برخی‌از سوابق می‌شود.

این رویکرد، که داده‌ها را با فهرست کردن شناسه‌ها به‌عنوان کلید و تنظیم مقدار روی true معکوس می‌کند، بررسی کلید را به‌سادگی خواندن /users/$uid/groups/$group_id و بررسی اینکه آیا null است یا نه می‌کند. نمایه سریع‌تر است و بسیار کارآمدتر از جستجو یا اسکن داده‌ها است.

مراحل بعدی