Cloud Firestore iOS कोडलैब (कोड बनाना सीखना)

1. खास जानकारी

लक्ष्य

इस कोडलैब में, Swift में iOS पर Firestore की मदद से रेस्टोरेंट के सुझाव देने वाला ऐप्लिकेशन बनाया जाएगा. आपको इनके बारे में जानकारी मिलेगी:

  1. iOS ऐप्लिकेशन से Firestore में डेटा पढ़ना और लिखना
  2. Firestore के डेटा में हुए बदलावों को रीयलटाइम में सुनना
  3. Firestore के डेटा को सुरक्षित रखने के लिए, Firebase Authentication और सुरक्षा के नियमों का इस्तेमाल करना
  4. Firestore की मुश्किल क्वेरी लिखना

ज़रूरी शर्तें

इस कोडलैब को शुरू करने से पहले, पक्का करें कि आपने ये इंस्टॉल किए हों:

  • Xcode 14.0 या इसके बाद का वर्शन
  • CocoaPods 1.12.0 (या इसके बाद का वर्शन)

2. सैंपल प्रोजेक्ट पाना

कोड डाउनलोड करना

सबसे पहले, सैंपल प्रोजेक्ट को क्लोन करें और प्रोजेक्ट डायरेक्ट्री में pod update चलाएं:

git clone https://github.com/firebase/friendlyeats-ios
cd friendlyeats-ios
pod update

Xcode में FriendlyEats.xcworkspace खोलें और इसे चलाएं (Cmd+R). ऐप्लिकेशन को सही तरीके से कंपाइल किया जाना चाहिए. साथ ही, लॉन्च होने पर तुरंत क्रैश हो जाना चाहिए, क्योंकि इसमें GoogleService-Info.plist फ़ाइल मौजूद नहीं है. हम अगले चरण में इसे ठीक करेंगे.

3. Firebase सेट अप करना

Firebase प्रोजेक्ट बनाना

  1. अपने Google खाते का इस्तेमाल करके, Firebase कंसोल में साइन इन करें.
  2. नया प्रोजेक्ट बनाने के लिए, बटन पर क्लिक करें. इसके बाद, प्रोजेक्ट का नाम डालें. उदाहरण के लिए, FriendlyEats.
  3. जारी रखें पर क्लिक करें.
  4. अगर आपसे कहा जाए, तो Firebase की शर्तें पढ़ें और स्वीकार करें. इसके बाद, जारी रखें पर क्लिक करें.
  5. (ज़रूरी नहीं) Firebase कंसोल में एआई की मदद पाने की सुविधा चालू करें. इसे "Firebase में Gemini" कहा जाता है.
  6. इस कोडलैब के लिए, आपको Google Analytics की ज़रूरत नहीं है. इसलिए, Google Analytics के विकल्प को टॉगल करके बंद करें.
  7. प्रोजेक्ट बनाएं पर क्लिक करें. इसके बाद, प्रोजेक्ट के प्रोविज़न होने का इंतज़ार करें. इसके बाद, जारी रखें पर क्लिक करें.

अपने ऐप्लिकेशन को Firebase से कनेक्ट करना

अपने नए Firebase प्रोजेक्ट में एक iOS ऐप्लिकेशन बनाएं.

अपने प्रोजेक्ट की GoogleService-Info.plist फ़ाइल को Firebase कंसोल से डाउनलोड करें और उसे Xcode प्रोजेक्ट के रूट में खींचें. प्रोजेक्ट को फिर से चलाएं, ताकि यह पक्का किया जा सके कि ऐप्लिकेशन सही तरीके से कॉन्फ़िगर हो गया है और अब लॉन्च होने पर क्रैश नहीं हो रहा है. लॉग इन करने के बाद, आपको नीचे दिए गए उदाहरण की तरह एक खाली स्क्रीन दिखेगी. अगर आपको लॉग इन करने में समस्या आ रही है, तो पक्का करें कि आपने Firebase कंसोल में, पुष्टि करने की सुविधा के तहत ईमेल/पासवर्ड से साइन-इन करने का तरीका चालू किया हो.

d5225270159c040b.png

4. Firestore में डेटा सेव करने की अनुमति

इस सेक्शन में, हम Firestore में कुछ डेटा लिखेंगे, ताकि हम ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) को पॉप्युलेट कर सकें. इसे Firebase कंसोल के ज़रिए मैन्युअल तरीके से किया जा सकता है. हालांकि, हम इसे ऐप्लिकेशन में ही करेंगे, ताकि Firestore में डेटा लिखने का तरीका दिखाया जा सके.

हमारे ऐप्लिकेशन में मुख्य मॉडल ऑब्जेक्ट, रेस्टोरेंट है. Firestore के डेटा को दस्तावेज़ों, कलेक्शन, और सब-कलेक्शन में बांटा जाता है. हम हर रेस्टोरेंट को एक दस्तावेज़ के तौर पर, टॉप-लेवल के कलेक्शन में सेव करेंगे. इस कलेक्शन का नाम restaurants होगा. अगर आपको Firestore के डेटा मॉडल के बारे में ज़्यादा जानना है, तो दस्तावेज़ में जाकर, दस्तावेज़ों और कलेक्शन के बारे में पढ़ें.

Firestore में डेटा जोड़ने से पहले, हमें रेस्टोरेंट के कलेक्शन का रेफ़रंस पाना होगा. RestaurantsTableViewController.didTapPopulateButton(_:) तरीके में, इनर फ़ॉर लूप में यह कोड जोड़ें.

let collection = Firestore.firestore().collection("restaurants")

अब हमारे पास कलेक्शन का रेफ़रंस है. इसलिए, हम कुछ डेटा लिख सकते हैं. हमने जो कोड जोड़ा है उसकी आखिरी लाइन के ठीक बाद यह कोड जोड़ें:

let collection = Firestore.firestore().collection("restaurants")

// ====== ADD THIS ======
let restaurant = Restaurant(
  name: name,
  category: category,
  city: city,
  price: price,
  ratingCount: 0,
  averageRating: 0
)

collection.addDocument(data: restaurant.dictionary)

ऊपर दिए गए कोड से, रेस्टोरेंट कलेक्शन में एक नया दस्तावेज़ जुड़ जाता है. दस्तावेज़ का डेटा, डिक्शनरी से मिलता है. यह डिक्शनरी हमें Restaurant struct से मिलती है.

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

Firebase कंसोल के नियम टैब में जाकर, यहां दिए गए नियम जोड़ें. इसके बाद, पब्लिश करें पर क्लिक करें.

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurants/{any}/ratings/{rating} {
      // Users can only write ratings with their user ID
      allow read;
      allow write: if request.auth != null
                   && request.auth.uid == request.resource.data.userId;
    }

    match /restaurants/{any} {
      // Only authenticated users can read or write data
      allow read, write: if request.auth != null;
    }
  }
}

हम सुरक्षा के नियमों के बारे में बाद में विस्तार से बात करेंगे. हालांकि, अगर आपको तुरंत जानकारी चाहिए, तो सुरक्षा के नियमों से जुड़े दस्तावेज़ देखें.

ऐप्लिकेशन खोलें और साइन इन करें. इसके बाद, सबसे ऊपर बाईं ओर मौजूद "भरें" बटन पर टैप करें. इससे रेस्टोरेंट के दस्तावेज़ों का एक बैच बन जाएगा. हालांकि, आपको यह बैच अभी ऐप्लिकेशन में नहीं दिखेगा.

इसके बाद, Firebase कंसोल में Firestore डेटा टैब पर जाएं. अब आपको रेस्टोरेंट के कलेक्शन में नई एंट्री दिखेंगी:

Screen Shot 2017-07-06 at 12.45.38 PM.png

बधाई हो, आपने iOS ऐप्लिकेशन से Firestore में डेटा लिख लिया है! अगले सेक्शन में, आपको Firestore से डेटा वापस पाने और उसे ऐप्लिकेशन में दिखाने का तरीका बताया जाएगा.

5. Firestore से डेटा दिखाना

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

सबसे पहले, हम ऐसी क्वेरी बनाते हैं जो रेस्टोरेंट की डिफ़ॉल्ट और फ़िल्टर नहीं की गई सूची दिखाएगी. RestaurantsTableViewController.baseQuery() को लागू करने का तरीका देखें:

return Firestore.firestore().collection("restaurants").limit(to: 50)

इस क्वेरी से, "रेस्टोरेंट" नाम के टॉप-लेवल कलेक्शन के ज़्यादा से ज़्यादा 50 रेस्टोरेंट मिलते हैं. अब हमारे पास एक क्वेरी है. हमें Firestore से डेटा को अपने ऐप्लिकेशन में लोड करने के लिए, स्नैपशॉट लिसनर अटैच करना होगा. stopObserving() को कॉल करने के ठीक बाद, RestaurantsTableViewController.observeQuery() तरीके में यह कोड जोड़ें.

listener = query.addSnapshotListener { [unowned self] (snapshot, error) in
  guard let snapshot = snapshot else {
    print("Error fetching snapshot results: \(error!)")
    return
  }
  let models = snapshot.documents.map { (document) -> Restaurant in
    if let model = Restaurant(dictionary: document.data()) {
      return model
    } else {
      // Don't use fatalError here in a real app.
      fatalError("Unable to initialize type \(Restaurant.self) with dictionary \(document.data())")
    }
  }
  self.restaurants = models
  self.documents = snapshot.documents

  if self.documents.count > 0 {
    self.tableView.backgroundView = nil
  } else {
    self.tableView.backgroundView = self.backgroundView
  }

  self.tableView.reloadData()
}

ऊपर दिया गया कोड, Firestore से कलेक्शन डाउनलोड करता है और उसे लोकल तौर पर एक ऐरे में सेव करता है. addSnapshotListener(_:) कॉल, क्वेरी में स्नैपशॉट लिसनर जोड़ता है. इससे सर्वर पर डेटा में बदलाव होने पर, व्यू कंट्रोलर अपडेट हो जाता है. हमें अपडेट अपने-आप मिलते हैं और हमें बदलावों को मैन्युअल तरीके से पुश नहीं करना पड़ता. ध्यान रखें कि सर्वर-साइड में हुए बदलाव की वजह से, इस स्नैपशॉट लिसनर को कभी भी शुरू किया जा सकता है. इसलिए, यह ज़रूरी है कि हमारा ऐप्लिकेशन बदलावों को मैनेज कर सके.

हमारी डिक्शनरी को स्ट्रक्चर में मैप करने के बाद (Restaurant.swift देखें), डेटा दिखाने के लिए व्यू प्रॉपर्टी असाइन करनी होती हैं. RestaurantsTableViewController.swift में मौजूद RestaurantTableViewCell.populate(restaurant:) में ये लाइनें जोड़ें.

nameLabel.text = restaurant.name
cityLabel.text = restaurant.city
categoryLabel.text = restaurant.category
starsView.rating = Int(restaurant.averageRating.rounded())
priceLabel.text = priceString(from: restaurant.price)

डेटा भरने के इस तरीके को टेबल व्यू डेटा सोर्स के tableView(_:cellForRowAtIndexPath:) तरीके से कॉल किया जाता है. यह तरीका, वैल्यू टाइप के कलेक्शन को पहले से अलग-अलग टेबल व्यू सेल में मैप करने का काम करता है.

ऐप्लिकेशन को फिर से चलाएं और पुष्टि करें कि कंसोल में पहले दिख रहे रेस्टोरेंट, अब सिम्युलेटर या डिवाइस पर दिख रहे हैं. अगर आपने इस सेक्शन को पूरा कर लिया है, तो अब आपका ऐप्लिकेशन Cloud Firestore की मदद से डेटा को पढ़ और लिख सकता है!

391c0259bf05ac25.png

6. डेटा को क्रम से लगाना और फ़िल्टर करना

फ़िलहाल, हमारा ऐप्लिकेशन रेस्टोरेंट की सूची दिखाता है. हालांकि, इसमें लोगों की ज़रूरतों के हिसाब से फ़िल्टर करने का कोई विकल्प नहीं है. इस सेक्शन में, फ़िल्टर करने की सुविधा चालू करने के लिए, Firestore की ऐडवांस क्वेरी का इस्तेमाल किया जाएगा.

डिम सम रेस्टोरेंट की जानकारी पाने के लिए, यहां एक सामान्य क्वेरी का उदाहरण दिया गया है:

let filteredQuery = query.whereField("category", isEqualTo: "Dim Sum")

नाम से ही पता चलता है कि whereField(_:isEqualTo:) तरीके से, हमारी क्वेरी सिर्फ़ उन सदस्यों को डाउनलोड करेगी जिनके फ़ील्ड, हमारी तय की गई पाबंदियों को पूरा करते हैं. इस मामले में, यह सिर्फ़ उन रेस्टोरेंट की जानकारी डाउनलोड करेगा जहां category, "Dim Sum" है.

इस ऐप्लिकेशन में उपयोगकर्ता, खास क्वेरी बनाने के लिए कई फ़िल्टर जोड़ सकता है. जैसे, "सैन फ़्रांसिस्को में पिज़्ज़ा" या "लॉस एंजेलिस में लोकप्रियता के हिसाब से सीफ़ूड ऑर्डर करें".

RestaurantsTableViewController.swift खोलें और query(withCategory:city:price:sortBy:) के बीच में यह कोड ब्लॉक जोड़ें:

if let category = category, !category.isEmpty {
  filtered = filtered.whereField("category", isEqualTo: category)
}

if let city = city, !city.isEmpty {
  filtered = filtered.whereField("city", isEqualTo: city)
}

if let price = price {
  filtered = filtered.whereField("price", isEqualTo: price)
}

if let sortBy = sortBy, !sortBy.isEmpty {
  filtered = filtered.order(by: sortBy)
}

ऊपर दिए गए स्निपेट में, उपयोगकर्ता के इनपुट के आधार पर एक ही कंपाउंड क्वेरी बनाने के लिए, कई whereField और order क्लॉज़ जोड़े गए हैं. अब हमारी क्वेरी में सिर्फ़ वे रेस्टोरेंट दिखेंगे जो उपयोगकर्ता की ज़रूरतों के मुताबिक हैं.

अपने प्रोजेक्ट को चलाएं और पुष्टि करें कि कीमत, शहर, और कैटगरी के हिसाब से फ़िल्टर किया जा सकता है. पक्का करें कि कैटगरी और शहर के नाम सही तरीके से टाइप किए गए हों. जांच के दौरान, आपको अपने लॉग में इस तरह की गड़बड़ियां दिख सकती हैं:

Error fetching snapshot results: Error Domain=io.grpc Code=9
"The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=..."
UserInfo={NSLocalizedDescription=The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...}

ऐसा इसलिए है, क्योंकि Firestore को ज़्यादातर कंपाउंड क्वेरी के लिए इंडेक्स की ज़रूरत होती है. क्वेरी के लिए इंडेक्स की ज़रूरत होने पर, Firestore बड़े पैमाने पर तेज़ी से काम करता है. गड़बड़ी के मैसेज में दिए गए लिंक को खोलने पर, Firebase कंसोल में इंडेक्स बनाने का यूज़र इंटरफ़ेस (यूआई) अपने-आप खुल जाएगा. इसमें सही पैरामीटर पहले से भरे होंगे. Firestore में इंडेक्स के बारे में ज़्यादा जानने के लिए, दस्तावेज़ पढ़ें.

7. लेन-देन में डेटा लिखना

इस सेक्शन में, हम उपयोगकर्ताओं के लिए रेस्टोरेंट की समीक्षाएं सबमिट करने की सुविधा जोड़ेंगे. अब तक, हमने जितने भी ऑपरेशन किए हैं वे एटॉमिक हैं और अपेक्षाकृत आसान हैं. अगर इनमें से किसी में कोई गड़बड़ी होती है, तो हम उपयोगकर्ता को फिर से कोशिश करने के लिए कहेंगे या हम अपने-आप फिर से कोशिश करेंगे.

किसी रेस्टोरेंट को रेटिंग देने के लिए, हमें कई बार डेटा को पढ़ना और लिखना पड़ता है. सबसे पहले, समीक्षा सबमिट करनी होगी. इसके बाद, रेस्टोरेंट की रेटिंग की संख्या और औसत रेटिंग को अपडेट करना होगा. अगर इनमें से कोई एक ऑपरेशन पूरा नहीं होता है, तो डेटाबेस में मौजूद डेटा में अंतर आ जाता है. ऐसा इसलिए होता है, क्योंकि डेटाबेस के एक हिस्से में मौजूद डेटा, दूसरे हिस्से में मौजूद डेटा से मेल नहीं खाता.

अच्छी बात यह है कि Firestore में लेन-देन की सुविधा उपलब्ध है. इसकी मदद से, हम एक ही ऐटॉमिक ऑपरेशन में कई बार डेटा पढ़ और लिख सकते हैं. इससे यह पक्का होता है कि हमारा डेटा एक जैसा बना रहे.

RestaurantDetailViewController.reviewController(_:didSubmitFormWithReview:) में सभी let डिक्लेरेशन के नीचे यह कोड जोड़ें.

let firestore = Firestore.firestore()
firestore.runTransaction({ (transaction, errorPointer) -> Any? in

  // Read data from Firestore inside the transaction, so we don't accidentally
  // update using stale client data. Error if we're unable to read here.
  let restaurantSnapshot: DocumentSnapshot
  do {
    try restaurantSnapshot = transaction.getDocument(reference)
  } catch let error as NSError {
    errorPointer?.pointee = error
    return nil
  }

  // Error if the restaurant data in Firestore has somehow changed or is malformed.
  guard let data = restaurantSnapshot.data(),
        let restaurant = Restaurant(dictionary: data) else {

    let error = NSError(domain: "FireEatsErrorDomain", code: 0, userInfo: [
      NSLocalizedDescriptionKey: "Unable to write to restaurant at Firestore path: \(reference.path)"
    ])
    errorPointer?.pointee = error
    return nil
  }

  // Update the restaurant's rating and rating count and post the new review at the
  // same time.
  let newAverage = (Float(restaurant.ratingCount) * restaurant.averageRating + Float(review.rating))
      / Float(restaurant.ratingCount + 1)

  transaction.setData(review.dictionary, forDocument: newReviewReference)
  transaction.updateData([
    "numRatings": restaurant.ratingCount + 1,
    "avgRating": newAverage
  ], forDocument: reference)
  return nil
}) { (object, error) in
  if let error = error {
    print(error)
  } else {
    // Pop the review controller on success
    if self.navigationController?.topViewController?.isKind(of: NewReviewViewController.self) ?? false {
      self.navigationController?.popViewController(animated: true)
    }
  }
}

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

8. सुरक्षा के नियम

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

सबसे पहले, हम कोडलैब की शुरुआत में लिखे गए सुरक्षा नियमों के बारे में ज़्यादा जानकारी देते हैं. Firebase कंसोल खोलें और Firestore टैब में Database > Rules पर जाएं.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurants/{any}/ratings/{rating} {
      // Users can only write ratings with their user ID
      allow read;
      allow write: if request.auth != null
                   && request.auth.uid == request.resource.data.userId;
    }

    match /restaurants/{any} {
      // Only authenticated users can read or write data
      allow read, write: if request.auth != null;
    }
  }
}

नियमों में मौजूद request वैरिएबल, एक ग्लोबल वैरिएबल है. यह सभी नियमों में उपलब्ध होता है. हमने जो शर्त जोड़ी है उससे यह पक्का होता है कि उपयोगकर्ताओं को कोई भी कार्रवाई करने की अनुमति देने से पहले, अनुरोध की पुष्टि हो जाए. इससे पुष्टि न किए गए उपयोगकर्ताओं को Firestore API का इस्तेमाल करके, आपके डेटा में अनधिकृत बदलाव करने से रोका जा सकता है. यह एक अच्छी शुरुआत है, लेकिन Firestore के नियमों का इस्तेमाल करके, हम और भी बेहतर काम कर सकते हैं.

हम समीक्षा लिखने की सुविधा को सीमित करना चाहते हैं, ताकि समीक्षा लिखने वाले व्यक्ति का आईडी, पुष्टि किए गए व्यक्ति के आईडी से मेल खाना चाहिए. इससे यह पक्का किया जाता है कि उपयोगकर्ता एक-दूसरे की पहचान का गलत इस्तेमाल न कर पाएं और धोखाधड़ी वाली समीक्षा न छोड़ें.

पहले मैच स्टेटमेंट में, restaurants कलेक्शन के किसी भी दस्तावेज़ के ratings नाम वाले सब-कलेक्शन से मैच किया जाता है. इसके बाद, allow write शर्त यह पक्का करती है कि अगर समीक्षा करने वाले व्यक्ति का आईडी, उपयोगकर्ता के आईडी से मेल नहीं खाता है, तो कोई भी समीक्षा सबमिट न की जाए. दूसरे मैच स्टेटमेंट से, पुष्टि किए गए किसी भी उपयोगकर्ता को डेटाबेस में रेस्टोरेंट की जानकारी पढ़ने और लिखने की अनुमति मिलती है.

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

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurants/{restaurant} {
      match /ratings/{rating} {
        allow read: if request.auth != null;
        allow write: if request.auth != null
                     && request.auth.uid == request.resource.data.userId;
      }

      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && request.resource.data.name == resource.data.name
                    && request.resource.data.city == resource.data.city
                    && request.resource.data.price == resource.data.price
                    && request.resource.data.category == resource.data.category;
    }
  }
}

यहां हमने लिखने की अनुमति को बनाने और अपडेट करने के लिए अलग-अलग किया है, ताकि हम यह तय कर सकें कि किन कार्रवाइयों की अनुमति दी जानी चाहिए. कोई भी उपयोगकर्ता, डेटाबेस में रेस्टोरेंट की जानकारी लिख सकता है. इससे, कोडलैब की शुरुआत में बनाए गए 'डेटा भरें' बटन की सुविधा बनी रहती है. हालांकि, किसी रेस्टोरेंट की जानकारी लिखने के बाद, उसका नाम, जगह, कीमत, और कैटगरी नहीं बदली जा सकती. खास तौर पर, आखिरी नियम के मुताबिक, रेस्टोरेंट की जानकारी अपडेट करने के लिए, डेटाबेस में मौजूद फ़ील्ड के नाम, शहर, कीमत, और कैटगरी को बनाए रखना ज़रूरी है.

सुरक्षा नियमों की मदद से क्या-क्या किया जा सकता है, इस बारे में ज़्यादा जानने के लिए दस्तावेज़ देखें.

9. नतीजा

इस कोडलैब में, आपने Firestore के साथ बुनियादी और ऐडवांस रीड और राइट करने के तरीके के बारे में जाना. साथ ही, सुरक्षा नियमों की मदद से डेटा ऐक्सेस को सुरक्षित करने के तरीके के बारे में भी जाना. आपको पूरा समाधान, codelab-complete ब्रांच पर मिल सकता है.

Firestore के बारे में ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें: