इस गाइड में, डेटा आर्किटेक्चर से जुड़े कुछ मुख्य कॉन्सेप्ट के बारे में बताया गया है. साथ ही, इसमें Firebase Realtime Database में JSON डेटा को स्ट्रक्चर करने के सबसे सही तरीकों के बारे में भी बताया गया है.
सही तरीके से स्ट्रक्चर किया गया डेटाबेस बनाने के लिए, काफ़ी सोच-विचार करने की ज़रूरत होती है. सबसे अहम बात यह है कि आपको यह प्लान करना होगा कि डेटा को कैसे सेव किया जाएगा और बाद में उसे कैसे वापस लाया जाएगा, ताकि यह प्रोसेस ज़्यादा से ज़्यादा आसान हो सके.
डेटा को कैसे स्ट्रक्चर किया जाता है: यह एक 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": { "..." } } }
अब चैट रूम की सूची में एक-एक करके जाने की सुविधा उपलब्ध है. इसके लिए, आपको हर बातचीत के लिए सिर्फ़ कुछ बाइट डाउनलोड करनी होंगी. इससे यूज़र इंटरफ़ेस (यूआई) में चैट रूम की सूची बनाने या उन्हें दिखाने के लिए, मेटाडेटा को तुरंत फ़ेच किया जा सकता है. मैसेज को अलग-अलग फ़ेच किया जा सकता है और जैसे ही वे आते हैं वैसे ही उन्हें दिखाया जा सकता है. इससे यूज़र इंटरफ़ेस (यूआई) तेज़ी से काम करता है और रिस्पॉन्सिव रहता है.
स्केल किया जा सकने वाला डेटा बनाना
ऐप्लिकेशन बनाते समय, अक्सर सूची के किसी सबसेट को डाउनलोड करना बेहतर होता है. ऐसा खास तौर पर तब होता है, जब सूची में हज़ारों रिकॉर्ड मौजूद हों. जब यह संबंध स्टैटिक और एकतरफ़ा होता है, तब चाइल्ड ऑब्जेक्ट को पैरंट ऑब्जेक्ट के नीचे नेस्ट किया जा सकता है.
कभी-कभी, यह संबंध ज़्यादा डाइनैमिक होता है. इसके अलावा, इस डेटा को डीनॉर्मलाइज़ करना ज़रूरी हो सकता है. कई बार, डेटा के सबसेट को वापस पाने के लिए क्वेरी का इस्तेमाल करके, डेटा को डीनॉर्मलाइज़ किया जा सकता है. इसके बारे में डेटा वापस पाना लेख में बताया गया है.
हालांकि, यह भी काफ़ी नहीं हो सकता. उदाहरण के लिए, उपयोगकर्ताओं और ग्रुप के बीच दोतरफ़ा संबंध के बारे में सोचें. उपयोगकर्ता किसी ग्रुप का हिस्सा हो सकते हैं. ग्रुप में उपयोगकर्ताओं की सूची होती है. जब यह तय करना होता है कि कोई उपयोगकर्ता किन ग्रुप से जुड़ा है, तो यह प्रोसेस काफ़ी मुश्किल हो जाती है.
हमें एक ऐसा तरीका चाहिए जिससे किसी उपयोगकर्ता से जुड़े ग्रुप की सूची आसानी से बनाई जा सके. साथ ही, सिर्फ़ उन ग्रुप का डेटा फ़ेच किया जा सके. ग्रुप के इंडेक्स से यहां काफ़ी मदद मिल सकती है:
// 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 की सदस्यताएं तुरंत और आसानी से मिल जाती हैं. भले ही, उपयोगकर्ताओं या ग्रुप की सूची लाखों में हो या Realtime Database सुरक्षा नियमों की वजह से कुछ रिकॉर्ड ऐक्सेस न किए जा सकें.
इस तरीके में, आईडी को कुंजियों के तौर पर लिस्ट करके डेटा को उलटा कर दिया जाता है और वैल्यू को सही पर सेट कर दिया जाता है. इससे किसी कुंजी की जांच करना उतना ही आसान हो जाता है जितना कि /users/$uid/groups/$group_id
को पढ़ना और यह देखना कि यह null
है या नहीं. इंडेक्स, डेटा को स्कैन करने या क्वेरी करने की तुलना में ज़्यादा तेज़ और ज़्यादा असरदार होता है.