আপনার ডাটাবেস গঠন

শুরু করার আগে

Realtime Database ব্যবহার করার আগে, আপনাকে যা করতে হবে তা হলো:

  • আপনার ইউনিটি প্রজেক্টটি রেজিস্টার করুন এবং ফায়ারবেস ব্যবহারের জন্য কনফিগার করুন।

    • আপনার ইউনিটি প্রজেক্টে যদি আগে থেকেই ফায়ারবেস ব্যবহার করা হয়, তাহলে এটি ফায়ারবেসের জন্য ইতোমধ্যেই নিবন্ধিত এবং কনফিগার করা আছে।

    • আপনার যদি কোনো ইউনিটি প্রজেক্ট না থাকে, তাহলে আপনি একটি স্যাম্পল অ্যাপ ডাউনলোড করতে পারেন।

  • আপনার ইউনিটি প্রজেক্টে Firebase Unity এসডিকে (বিশেষত, FirebaseDatabase.unitypackage ) যোগ করুন।

মনে রাখবেন যে, আপনার ইউনিটি প্রজেক্টে ফায়ারবেস যোগ করার জন্য Firebase কনসোল এবং আপনার খোলা ইউনিটি প্রজেক্ট উভয় স্থানেই কাজ করতে হয় (উদাহরণস্বরূপ, আপনাকে কনসোল থেকে ফায়ারবেস কনফিগারেশন ফাইল ডাউনলোড করতে হবে, তারপর সেগুলোকে আপনার ইউনিটি প্রজেক্টে স্থানান্তর করতে হবে)।

ডেটা কাঠামোবদ্ধকরণ

এই নির্দেশিকায় ডেটা আর্কিটেকচারের কিছু মূল ধারণা এবং আপনার Firebase Realtime Database JSON ডেটা কাঠামোবদ্ধ করার সর্বোত্তম অনুশীলনগুলো আলোচনা করা হয়েছে।

একটি যথাযথভাবে কাঠামোবদ্ধ ডেটাবেস তৈরি করতে যথেষ্ট পূর্বচিন্তার প্রয়োজন হয়। সবচেয়ে গুরুত্বপূর্ণ হলো, ডেটা কীভাবে সংরক্ষণ করা হবে এবং পরবর্তীতে তা পুনরুদ্ধার করা হবে, তার পরিকল্পনা করা, যাতে এই প্রক্রিয়াটি যতটা সম্ভব সহজ হয়।

ডেটার গঠন: এটি একটি JSON ট্রি

All Firebase Realtime Database data is stored as JSON objects. You can think of the database as a cloud-hosted JSON tree. Unlike a SQL database, there are no tables or records. When you add data to the JSON tree, it becomes a node in the existing JSON structure with an associated key. You can provide your own keys, such as user IDs or semantic names, or they can be provided for you using the Push() method.

উদাহরণস্বরূপ, এমন একটি চ্যাট অ্যাপ্লিকেশনের কথা ভাবুন যা ব্যবহারকারীদের একটি সাধারণ প্রোফাইল এবং পরিচিতি তালিকা সংরক্ষণ করার সুযোগ দেয়। একটি সাধারণ ব্যবহারকারী প্রোফাইল /users/$uid মতো একটি পাথে অবস্থিত থাকে। alovelace নামের ব্যবহারকারীর একটি ডাটাবেস এন্ট্রি থাকতে পারে যা দেখতে অনেকটা এইরকম:

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

যদিও ডাটাবেসটি একটি JSON ট্রি ব্যবহার করে, তবুও ডাটাবেসে সংরক্ষিত ডেটাকে উপলব্ধ JSON টাইপগুলোর সাথে সঙ্গতিপূর্ণ নির্দিষ্ট নেটিভ টাইপ হিসেবে উপস্থাপন করা যেতে পারে, যা আপনাকে আরও রক্ষণাবেক্ষণযোগ্য কোড লিখতে সাহায্য করে।

ডেটা কাঠামোর জন্য সর্বোত্তম অনুশীলন

নেস্টিং ডেটা এড়িয়ে চলুন

Because the Firebase Realtime Database allows nesting data up to 32 levels deep, you might be tempted to think that this should be the default structure. However, when you fetch data at a location in your database, you also retrieve all of its child nodes. In addition, when you grant someone read or write access at a node in your database, you also grant them access to all data under that node. Therefore, in practice, it's best to keep your data structure as flat as possible.

নেস্টেড ডেটা কেন খারাপ, তার একটি উদাহরণ হিসেবে নিম্নলিখিত একাধিক-নেস্টেড কাঠামোটি বিবেচনা করুন:

{
  // 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": { "..." }
  }
}

এখন প্রতিটি কথোপকথনের জন্য মাত্র কয়েক বাইট ডেটা ডাউনলোড করে রুমের তালিকার মধ্যে দিয়ে যাওয়া সম্ভব, যা UI-তে রুম তালিকাভুক্ত বা প্রদর্শন করার জন্য দ্রুত মেটাডেটা সংগ্রহ করে। মেসেজগুলো আলাদাভাবে সংগ্রহ করে আসার সাথে সাথেই প্রদর্শন করা যায়, ফলে UI-টি রেসপন্সিভ এবং দ্রুত থাকে।

এমন ডেটা তৈরি করুন যা পরিমাপযোগ্য।

অ্যাপ তৈরি করার সময়, প্রায়শই একটি তালিকার উপসেট ডাউনলোড করা শ্রেয়। বিশেষ করে যদি তালিকাটিতে হাজার হাজার রেকর্ড থাকে, তবে এটি বেশি প্রচলিত। যখন এই সম্পর্কটি স্থির এবং একমুখী হয়, তখন আপনি সহজেই চাইল্ড অবজেক্টগুলোকে প্যারেন্টের অধীনে নেস্ট করতে পারেন।

কখনও কখনও, এই সম্পর্কটি আরও গতিশীল হয়, অথবা এই ডেটাকে ডিনরমালাইজ করার প্রয়োজন হতে পারে। অনেক সময়, ‘ডেটা পুনরুদ্ধার’ অংশে যেমন আলোচনা করা হয়েছে, একটি কোয়েরি ব্যবহার করে ডেটার একটি উপসেট পুনরুদ্ধার করার মাধ্যমে ডেটাকে ডিনরমালাইজ করা যায়।

কিন্তু এমনকি এটাও অপর্যাপ্ত হতে পারে। উদাহরণস্বরূপ, ব্যবহারকারী এবং গ্রুপের মধ্যে একটি দ্বিমুখী সম্পর্কের কথা বিবেচনা করুন। ব্যবহারকারীরা একটি গ্রুপের সদস্য হতে পারে, এবং গ্রুপগুলো ব্যবহারকারীদের একটি তালিকা নিয়ে গঠিত। যখন একজন ব্যবহারকারী কোন কোন গ্রুপের অন্তর্ভুক্ত হবে তা নির্ধারণ করার সময় আসে, তখন বিষয়টি জটিল হয়ে ওঠে।

একজন ব্যবহারকারী কোন কোন গ্রুপের সদস্য, তা তালিকাভুক্ত করার এবং শুধুমাত্র সেই গ্রুপগুলোর ডেটা সংগ্রহ করার একটি মার্জিত উপায় প্রয়োজন। এক্ষেত্রে গ্রুপগুলোর একটি ইনডেক্স অনেক সাহায্য করতে পারে:

// 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 অ্যাডার প্রোফাইলে তালিকাভুক্ত। সুতরাং, গ্রুপ থেকে অ্যাডাকে মুছে ফেলতে হলে, এটি দুটি জায়গাতেই আপডেট করতে হবে।

দ্বিমুখী সম্পর্কের জন্য এটি একটি প্রয়োজনীয় অতিরিক্ত ব্যবস্থা। এটি আপনাকে দ্রুত এবং দক্ষতার সাথে অ্যাডার মেম্বারশিপগুলো সংগ্রহ করতে সাহায্য করে, এমনকি যখন ব্যবহারকারী বা গ্রুপের তালিকা লক্ষ লক্ষ ছাড়িয়ে যায় অথবা যখন Realtime Database নিরাপত্তা বিধি কিছু রেকর্ডে প্রবেশে বাধা দেয়।

এই পদ্ধতিটি, যেখানে আইডিগুলোকে কী (key) হিসেবে তালিকাভুক্ত করে এবং ভ্যালু 'true' সেট করে ডেটাকে উল্টে দেওয়া হয়, তা কোনো কী-কে যাচাই করাকে /users/$uid/groups/$group_id পড়ে সেটি null কিনা তা পরীক্ষা করার মতোই সহজ করে তোলে। ডেটা কোয়েরি বা স্ক্যান করার চেয়ে ইনডেক্স দ্রুততর এবং অনেক বেশি কার্যকর।

পরবর্তী পদক্ষেপ