डेटा पढ़ें और लिखें

(वैकल्पिक) फायरबेस एमुलेटर सूट के साथ प्रोटोटाइप और परीक्षण

इस बारे में बात करने से पहले कि आपका ऐप रीयलटाइम डेटाबेस को कैसे पढ़ता और लिखता है, आइए टूल का एक सेट पेश करें जिसका उपयोग आप रीयलटाइम डेटाबेस कार्यक्षमता को प्रोटोटाइप और परीक्षण करने के लिए कर सकते हैं: फायरबेस एमुलेटर सूट। यदि आप अलग-अलग डेटा मॉडल आज़मा रहे हैं, अपने सुरक्षा नियमों को अनुकूलित कर रहे हैं, या बैक-एंड के साथ बातचीत करने का सबसे लागत प्रभावी तरीका ढूंढने के लिए काम कर रहे हैं, तो लाइव सेवाओं को तैनात किए बिना स्थानीय स्तर पर काम करने में सक्षम होना एक अच्छा विचार हो सकता है।

रीयलटाइम डेटाबेस एमुलेटर एम्यूलेटर सूट का हिस्सा है, जो आपके ऐप को आपके अनुकरणित डेटाबेस सामग्री और कॉन्फ़िगरेशन के साथ-साथ वैकल्पिक रूप से आपके अनुकरणीय प्रोजेक्ट संसाधनों (फ़ंक्शन, अन्य डेटाबेस और सुरक्षा नियमों) के साथ इंटरैक्ट करने में सक्षम बनाता है।emulator_suite_short

रीयलटाइम डेटाबेस एमुलेटर का उपयोग करने में बस कुछ ही चरण शामिल हैं:

  1. एम्यूलेटर से कनेक्ट करने के लिए अपने ऐप के परीक्षण कॉन्फ़िगरेशन में कोड की एक पंक्ति जोड़ना।
  2. आपकी स्थानीय प्रोजेक्ट निर्देशिका के मूल से, firebase emulators:start
  3. हमेशा की तरह रीयलटाइम डेटाबेस प्लेटफ़ॉर्म SDK का उपयोग करके, या रीयलटाइम डेटाबेस REST API का उपयोग करके अपने ऐप के प्रोटोटाइप कोड से कॉल करना।

रीयलटाइम डेटाबेस और क्लाउड फ़ंक्शंस से संबंधित एक विस्तृत पूर्वाभ्यास उपलब्ध है। आपको एम्यूलेटर सुइट परिचय पर भी एक नज़र डालनी चाहिए।

एक डेटाबेस संदर्भ प्राप्त करें

डेटाबेस से डेटा पढ़ने या लिखने के लिए, आपको DatabaseReference का एक उदाहरण चाहिए:

DatabaseReference ref = FirebaseDatabase.instance.ref();

डेटा लिखें

यह दस्तावेज़ फायरबेस डेटा को पढ़ने और लिखने की मूल बातें शामिल करता है।

फायरबेस डेटा को DatabaseReference में लिखा जाता है और संदर्भ द्वारा उत्सर्जित घटनाओं की प्रतीक्षा या सुनकर पुनर्प्राप्त किया जाता है। डेटा की प्रारंभिक स्थिति के लिए इवेंट एक बार उत्सर्जित होते हैं और फिर कभी भी डेटा में परिवर्तन होता है।

बुनियादी लेखन संचालन

बुनियादी लेखन कार्यों के लिए, आप डेटा को एक निर्दिष्ट संदर्भ में सहेजने के लिए, उस पथ पर किसी भी मौजूदा डेटा को प्रतिस्थापित करने के लिए set() उपयोग कर सकते हैं। आप निम्न प्रकारों का संदर्भ सेट कर सकते हैं: String , boolean , int , double , Map , List

उदाहरण के लिए, आप एक उपयोगकर्ता को set() के साथ इस प्रकार जोड़ सकते हैं:

DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");

await ref.set({
  "name": "John",
  "age": 18,
  "address": {
    "line1": "100 Mountain View"
  }
});

इस तरह से set() का उपयोग किसी भी चाइल्ड नोड सहित निर्दिष्ट स्थान पर डेटा को ओवरराइट कर देता है। हालाँकि, आप अभी भी संपूर्ण ऑब्जेक्ट को दोबारा लिखे बिना किसी बच्चे को अपडेट कर सकते हैं। यदि आप उपयोगकर्ताओं को अपनी प्रोफ़ाइल अपडेट करने की अनुमति देना चाहते हैं तो आप निम्नानुसार उपयोगकर्ता नाम अपडेट कर सकते हैं:

DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");

// Only update the name, leave the age and address!
await ref.update({
  "age": 19,
});

update() विधि नोड्स के लिए एक उप-पथ स्वीकार करती है, जिससे आप एक साथ डेटाबेस पर कई नोड्स को अपडेट कर सकते हैं:

DatabaseReference ref = FirebaseDatabase.instance.ref("users");

await ref.update({
  "123/age": 19,
  "123/address/line1": "1 Mountain View",
});

डेटा पढ़ें

मूल्य घटनाओं को सुनकर डेटा पढ़ें

पथ पर डेटा पढ़ने और परिवर्तनों को सुनने के लिए, DatabaseEvent s को सुनने के लिए DatabaseReference की onValue प्रॉपर्टी का उपयोग करें।

आप किसी दिए गए पथ पर डेटा को पढ़ने के लिए DatabaseEvent उपयोग कर सकते हैं, क्योंकि यह ईवेंट के समय मौजूद होता है। श्रोता के संलग्न होने पर यह घटना एक बार ट्रिगर होती है और हर बार किसी भी बच्चे सहित डेटा में परिवर्तन होता है। इवेंट में एक snapshot प्रॉपर्टी होती है जिसमें चाइल्ड डेटा सहित उस स्थान का सारा डेटा होता है। यदि कोई डेटा नहीं है, तो स्नैपशॉट की exists संपत्ति false होगी और इसकी value संपत्ति शून्य होगी।

निम्नलिखित उदाहरण एक सामाजिक ब्लॉगिंग एप्लिकेशन को डेटाबेस से किसी पोस्ट का विवरण पुनर्प्राप्त करते हुए दर्शाता है:

DatabaseReference starCountRef =
        FirebaseDatabase.instance.ref('posts/$postId/starCount');
starCountRef.onValue.listen((DatabaseEvent event) {
    final data = event.snapshot.value;
    updateStarCount(data);
});

श्रोता को एक DataSnapshot प्राप्त होता है जिसमें घटना के समय डेटाबेस में निर्दिष्ट स्थान पर डेटा उसकी value प्रॉपर्टी में होता है।

एक बार डेटा पढ़ें

get() का उपयोग करके एक बार पढ़ें

एसडीके को डेटाबेस सर्वर के साथ इंटरैक्शन प्रबंधित करने के लिए डिज़ाइन किया गया है, चाहे आपका ऐप ऑनलाइन हो या ऑफलाइन।

आम तौर पर, आपको बैकएंड से डेटा के अपडेट की सूचना प्राप्त करने के लिए डेटा को पढ़ने के लिए ऊपर वर्णित वैल्यू इवेंट तकनीकों का उपयोग करना चाहिए। वे तकनीकें आपके उपयोग और बिलिंग को कम करती हैं, और आपके उपयोगकर्ताओं को ऑनलाइन और ऑफलाइन होने पर सर्वोत्तम अनुभव देने के लिए अनुकूलित की जाती हैं।

यदि आपको केवल एक बार डेटा की आवश्यकता है, तो आप डेटाबेस से डेटा का स्नैपशॉट प्राप्त करने के लिए get() उपयोग कर सकते हैं। यदि किसी भी कारण से get() सर्वर मान वापस करने में असमर्थ है, तो क्लाइंट स्थानीय स्टोरेज कैश की जांच करेगा और यदि मान अभी भी नहीं मिला है तो एक त्रुटि लौटाएगा।

निम्नलिखित उदाहरण डेटाबेस से उपयोगकर्ता के सार्वजनिक-सामना वाले उपयोगकर्ता नाम को एक बार पुनः प्राप्त करना दर्शाता है:

final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child('users/$userId').get();
if (snapshot.exists) {
    print(snapshot.value);
} else {
    print('No data available.');
}

get() के अनावश्यक उपयोग से बैंडविड्थ का उपयोग बढ़ सकता है और प्रदर्शन में कमी आ सकती है, जिसे ऊपर दिखाए अनुसार रियलटाइम श्रोता का उपयोग करके रोका जा सकता है।

एक बार के साथ एक बार डेटा पढ़ें()

कुछ मामलों में आप सर्वर पर अद्यतन मूल्य की जाँच करने के बजाय, स्थानीय कैश से मूल्य तुरंत वापस करना चाह सकते हैं। उन मामलों में आप स्थानीय डिस्क कैश से तुरंत डेटा प्राप्त करने के लिए once() उपयोग कर सकते हैं।

यह उस डेटा के लिए उपयोगी है जिसे केवल एक बार लोड करने की आवश्यकता होती है और जिसके बार-बार बदलने या सक्रिय रूप से सुनने की आवश्यकता नहीं होती है। उदाहरण के लिए, पिछले उदाहरणों में ब्लॉगिंग ऐप किसी उपयोगकर्ता की प्रोफ़ाइल को लोड करने के लिए इस पद्धति का उपयोग करता है जब वे एक नई पोस्ट लिखना शुरू करते हैं:

final event = await ref.once(DatabaseEventType.value);
final username = event.snapshot.value?.username ?? 'Anonymous';

डेटा अपडेट करना या हटाना

विशिष्ट फ़ील्ड अपडेट करें

अन्य चाइल्ड नोड्स को ओवरराइट किए बिना एक नोड के विशिष्ट बच्चों को एक साथ लिखने के लिए, update() विधि का उपयोग करें।

update() को कॉल करते समय, आप कुंजी के लिए पथ निर्दिष्ट करके निचले स्तर के चाइल्ड मानों को अपडेट कर सकते हैं। यदि बेहतर पैमाने पर डेटा को कई स्थानों पर संग्रहीत किया जाता है, तो आप डेटा फैन-आउट का उपयोग करके उस डेटा के सभी उदाहरणों को अपडेट कर सकते हैं। उदाहरण के लिए, एक सोशल ब्लॉगिंग ऐप एक पोस्ट बनाना चाहता है और साथ ही उसे हालिया गतिविधि फ़ीड और पोस्ट करने वाले उपयोगकर्ता की गतिविधि फ़ीड में अपडेट करना चाहता है। ऐसा करने के लिए, ब्लॉगिंग एप्लिकेशन इस तरह कोड का उपयोग करता है:

void writeNewPost(String uid, String username, String picture, String title,
        String body) async {
    // A post entry.
    final postData = {
        'author': username,
        'uid': uid,
        'body': body,
        'title': title,
        'starCount': 0,
        'authorPic': picture,
    };

    // Get a key for a new Post.
    final newPostKey =
        FirebaseDatabase.instance.ref().child('posts').push().key;

    // Write the new post's data simultaneously in the posts list and the
    // user's post list.
    final Map<String, Map> updates = {};
    updates['/posts/$newPostKey'] = postData;
    updates['/user-posts/$uid/$newPostKey'] = postData;

    return FirebaseDatabase.instance.ref().update(updates);
}

यह उदाहरण /posts/$postid पर सभी उपयोगकर्ताओं के लिए पोस्ट युक्त नोड में एक पोस्ट बनाने के लिए push() का उपयोग करता है और साथ ही key के साथ कुंजी पुनर्प्राप्त करता है। कुंजी का उपयोग उपयोगकर्ता की पोस्ट में /user-posts/$userid/$postid पर दूसरी प्रविष्टि बनाने के लिए किया जा सकता है।

इन पथों का उपयोग करके, आप update() पर एक ही कॉल के साथ JSON ट्री में कई स्थानों पर एक साथ अपडेट कर सकते हैं, जैसे कि यह उदाहरण दोनों स्थानों पर नई पोस्ट कैसे बनाता है। इस तरह से किए गए एक साथ किए गए अपडेट परमाणु होते हैं: या तो सभी अपडेट सफल होते हैं या सभी अपडेट विफल हो जाते हैं।

पूर्णता कॉलबैक जोड़ें

यदि आप जानना चाहते हैं कि आपका डेटा कब प्रतिबद्ध किया गया है, तो आप पूर्णता कॉलबैक पंजीकृत कर सकते हैं। set() और update() दोनों Future s लौटाते हैं, जिसमें आप सफलता और त्रुटि कॉलबैक संलग्न कर सकते हैं जिन्हें तब कॉल किया जाता है जब लेखन डेटाबेस के लिए प्रतिबद्ध होता है और जब कॉल असफल होती है।

FirebaseDatabase.instance
    .ref('users/$userId/email')
    .set(emailAddress)
    .then((_) {
        // Data saved successfully!
    })
    .catchError((error) {
        // The write failed...
    });

डेटा हटाएँ

डेटा को हटाने का सबसे सरल तरीका उस डेटा के स्थान के संदर्भ में remove() को कॉल करना है।

आप किसी अन्य लेखन ऑपरेशन जैसे कि set() या update() के मान के रूप में null निर्दिष्ट करके भी हटा सकते हैं। आप एक ही एपीआई कॉल में एकाधिक बच्चों को हटाने के लिए update() के साथ इस तकनीक का उपयोग कर सकते हैं।

लेनदेन के रूप में डेटा सहेजें

ऐसे डेटा के साथ काम करते समय जो समवर्ती संशोधनों से दूषित हो सकता है, जैसे कि वृद्धिशील काउंटर, आप लेनदेन हैंडलर को runTransaction() में पास करके लेनदेन का उपयोग कर सकते हैं। एक लेन-देन हैंडलर डेटा की वर्तमान स्थिति को एक तर्क के रूप में लेता है और नई वांछित स्थिति लौटाता है जिसे आप लिखना चाहते हैं। यदि कोई अन्य क्लाइंट आपके नए मान के सफलतापूर्वक लिखे जाने से पहले स्थान पर लिखता है, तो आपके अपडेट फ़ंक्शन को नए वर्तमान मान के साथ फिर से कॉल किया जाता है, और लिखने का पुनः प्रयास किया जाता है।

उदाहरण के लिए, उदाहरण के सोशल ब्लॉगिंग ऐप में, आप उपयोगकर्ताओं को पोस्ट को स्टार और अनस्टार करने की अनुमति दे सकते हैं और इस प्रकार ट्रैक कर सकते हैं कि किसी पोस्ट को कितने स्टार मिले हैं:

void toggleStar(String uid) async {
  DatabaseReference postRef =
      FirebaseDatabase.instance.ref("posts/foo-bar-123");

  TransactionResult result = await postRef.runTransaction((Object? post) {
    // Ensure a post at the ref exists.
    if (post == null) {
      return Transaction.abort();
    }

    Map<String, dynamic> _post = Map<String, dynamic>.from(post as Map);
    if (_post["stars"] is Map && _post["stars"][uid] != null) {
      _post["starCount"] = (_post["starCount"] ?? 1) - 1;
      _post["stars"][uid] = null;
    } else {
      _post["starCount"] = (_post["starCount"] ?? 0) + 1;
      if (!_post.containsKey("stars")) {
        _post["stars"] = {};
      }
      _post["stars"][uid] = true;
    }

    // Return the new data.
    return Transaction.success(_post);
  });
}

डिफ़ॉल्ट रूप से, हर बार लेन-देन अद्यतन फ़ंक्शन चलने पर ईवेंट उठाए जाते हैं, इसलिए आप फ़ंक्शन रन को कई बार चलाते हैं, आप मध्यवर्ती स्थिति देख सकते हैं। आप इन मध्यवर्ती स्थितियों को दबाने के लिए applyLocally को false पर सेट कर सकते हैं और इसके बजाय घटनाओं के बढ़ने से पहले लेनदेन पूरा होने तक प्रतीक्षा कर सकते हैं:

await ref.runTransaction((Object? post) {
  // ...
}, applyLocally: false);

लेन-देन का परिणाम एक TransactionResult होता है, जिसमें यह जानकारी होती है कि क्या लेन-देन प्रतिबद्ध था, और नया स्नैपशॉट:

DatabaseReference ref = FirebaseDatabase.instance.ref("posts/123");

TransactionResult result = await ref.runTransaction((Object? post) {
  // ...
});

print('Committed? ${result.committed}'); // true / false
print('Snapshot? ${result.snapshot}'); // DataSnapshot

लेन-देन रद्द करना

यदि आप किसी लेन-देन को सुरक्षित रूप से रद्द करना चाहते हैं, तो AbortTransactionException को फेंकने के लिए Transaction.abort() पर कॉल करें:

TransactionResult result = await ref.runTransaction((Object? user) {
  if (user !== null) {
    return Transaction.abort();
  }

  // ...
});

print(result.committed); // false

परमाणु सर्वर-साइड वृद्धि

उपरोक्त उपयोग के मामले में हम डेटाबेस में दो मान लिख रहे हैं: उस उपयोगकर्ता की आईडी जो पोस्ट को स्टार/अनस्टार करता है, और बढ़ी हुई स्टार गिनती। यदि हम पहले से ही जानते हैं कि उपयोगकर्ता पोस्ट को अभिनीत कर रहा है, तो हम लेनदेन के बजाय परमाणु वृद्धि ऑपरेशन का उपयोग कर सकते हैं।

void addStar(uid, key) async {
  Map<String, Object?> updates = {};
  updates["posts/$key/stars/$uid"] = true;
  updates["posts/$key/starCount"] = ServerValue.increment(1);
  updates["user-posts/$key/stars/$uid"] = true;
  updates["user-posts/$key/starCount"] = ServerValue.increment(1);
  return FirebaseDatabase.instance.ref().update(updates);
}

यह कोड लेन-देन ऑपरेशन का उपयोग नहीं करता है, इसलिए यदि कोई विरोधाभासी अद्यतन होता है तो यह स्वचालित रूप से दोबारा नहीं चलता है। हालाँकि, चूंकि वेतन वृद्धि कार्रवाई सीधे डेटाबेस सर्वर पर होती है, इसलिए विरोध की कोई संभावना नहीं है।

यदि आप एप्लिकेशन-विशिष्ट विरोधों का पता लगाना और उन्हें अस्वीकार करना चाहते हैं, जैसे कि किसी उपयोगकर्ता द्वारा किसी पोस्ट को तारांकित करना जिसे उन्होंने पहले ही तारांकित कर दिया है, तो आपको उस उपयोग के मामले के लिए कस्टम सुरक्षा नियम लिखना चाहिए।

डेटा के साथ ऑफ़लाइन कार्य करें

यदि कोई क्लाइंट अपना नेटवर्क कनेक्शन खो देता है, तो आपका ऐप सही ढंग से काम करता रहेगा।

फायरबेस डेटाबेस से जुड़ा प्रत्येक क्लाइंट किसी भी सक्रिय डेटा का अपना आंतरिक संस्करण रखता है। जब डेटा लिखा जाता है, तो उसे पहले इस स्थानीय संस्करण में लिखा जाता है। फ़ायरबेस क्लाइंट तब उस डेटा को दूरस्थ डेटाबेस सर्वर और अन्य क्लाइंट के साथ "सर्वोत्तम प्रयास" के आधार पर सिंक्रनाइज़ करता है।

परिणामस्वरूप, सर्वर पर कोई भी डेटा लिखे जाने से पहले, डेटाबेस पर लिखी जाने वाली सभी चीज़ें तुरंत स्थानीय घटनाओं को ट्रिगर करती हैं। इसका मतलब है कि आपका ऐप नेटवर्क विलंबता या कनेक्टिविटी की परवाह किए बिना प्रतिक्रियाशील बना रहता है।

एक बार कनेक्टिविटी पुनः स्थापित हो जाने पर, आपके ऐप को ईवेंट का उचित सेट प्राप्त होता है ताकि क्लाइंट बिना कोई कस्टम कोड लिखे वर्तमान सर्वर स्थिति के साथ समन्वयित हो सके।

हम ऑनलाइन और ऑफ़लाइन क्षमताओं के बारे में और जानें में ऑफ़लाइन व्यवहार के बारे में अधिक बात करेंगे।

अगले कदम