क्लाउड फायरस्टोर एंड्रॉइड कोडेलैब

1 अवलोकन

लक्ष्य

इस कोडलैब में आप क्लाउड फायरस्टोर द्वारा समर्थित एंड्रॉइड पर एक रेस्तरां अनुशंसा ऐप बनाएंगे। आप जान जायेंगे कैसे:

  • एंड्रॉइड ऐप से फायरस्टोर पर डेटा पढ़ें और लिखें
  • रीयलटाइम में फायरस्टोर डेटा में बदलावों को सुनें
  • फायरस्टोर डेटा को सुरक्षित करने के लिए फायरबेस प्रमाणीकरण और सुरक्षा नियमों का उपयोग करें
  • जटिल फायरस्टोर क्वेरीज़ लिखें

आवश्यक शर्तें

इस कोडलैब को शुरू करने से पहले सुनिश्चित करें कि आपके पास:

  • एंड्रॉइड स्टूडियो फ्लेमिंगो या नया
  • एपीआई 19 या उच्चतर वाला एक एंड्रॉइड एमुलेटर
  • Node.js संस्करण 16 या उच्चतर
  • जावा संस्करण 17 या उच्चतर

2. एक फायरबेस प्रोजेक्ट बनाएं

  1. अपने Google खाते से फायरबेस कंसोल में साइन इन करें।
  2. फायरबेस कंसोल में, प्रोजेक्ट जोड़ें पर क्लिक करें।
  3. जैसा कि नीचे स्क्रीन कैप्चर में दिखाया गया है, अपने फायरबेस प्रोजेक्ट के लिए एक नाम दर्ज करें (उदाहरण के लिए, "फ्रेंडली ईट्स"), और जारी रखें पर क्लिक करें।

9d2f625aebcab6af.png

  1. आपसे Google Analytics को सक्षम करने के लिए कहा जा सकता है, इस कोडलैब के प्रयोजनों के लिए आपका चयन कोई मायने नहीं रखता।
  2. एक या दो मिनट के बाद, आपका फायरबेस प्रोजेक्ट तैयार हो जाएगा। जारी रखें पर क्लिक करें.

3. नमूना प्रोजेक्ट सेट करें

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

इस कोडलैब के लिए नमूना कोड क्लोन करने के लिए निम्न कमांड चलाएँ। यह आपकी मशीन पर friendlyeats-android नामक एक फ़ोल्डर बनाएगा:

$ git clone https://github.com/firebase/friendlyeats-android

यदि आपकी मशीन पर git नहीं है, तो आप सीधे GitHub से भी कोड डाउनलोड कर सकते हैं।

फायरबेस कॉन्फ़िगरेशन जोड़ें

  1. फायरबेस कंसोल में, बाईं ओर नेविगेशन में प्रोजेक्ट अवलोकन चुनें। प्लेटफ़ॉर्म चुनने के लिए एंड्रॉइड बटन पर क्लिक करें। पैकेज नाम के लिए पूछे जाने पर com.google.firebase.example.fireeats का उपयोग करें

73d151ed16016421.png

  1. रजिस्टर ऐप पर क्लिक करें और google-services.json फ़ाइल डाउनलोड करने के लिए निर्देशों का पालन करें, और इसे आपके द्वारा अभी डाउनलोड किए गए कोड के app/ फ़ोल्डर में ले जाएं। फिर Next पर क्लिक करें।

प्रोजेक्ट आयात करें

एंड्रॉइड स्टूडियो खोलें। फ़ाइल > नया > प्रोजेक्ट आयात करें पर क्लिक करें और फ्रेंडलीईट्स-एंड्रॉइड फ़ोल्डर चुनें।

4. फायरबेस एमुलेटर सेट करें

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

फायरबेस सीएलआई स्थापित करें

सबसे पहले आपको फायरबेस सीएलआई इंस्टॉल करना होगा। यदि आप macOS या Linux का उपयोग कर रहे हैं, तो आप निम्न cURL कमांड चला सकते हैं:

curl -sL https://firebase.tools | bash

यदि आप विंडोज़ का उपयोग कर रहे हैं, तो स्टैंडअलोन बाइनरी प्राप्त करने के लिए या npm के माध्यम से इंस्टॉल करने के लिए इंस्टॉलेशन निर्देश पढ़ें।

एक बार जब आप सीएलआई स्थापित कर लेते हैं, तो firebase --version चलाने पर 9.0.0 या उच्चतर संस्करण की सूचना मिलनी चाहिए:

$ firebase --version
9.0.0

लॉग इन करें

सीएलआई को अपने Google खाते से कनेक्ट करने के लिए firebase login चलाएँ। यह लॉगिन प्रक्रिया को पूरा करने के लिए एक नई ब्राउज़र विंडो खोलेगा। सुनिश्चित करें कि आपने वही खाता चुना है जिसका उपयोग आपने पहले अपना फायरबेस प्रोजेक्ट बनाते समय किया था।

friendlyeats-android फ़ोल्डर के भीतर से अपने स्थानीय प्रोजेक्ट को अपने फायरबेस प्रोजेक्ट से कनेक्ट करने के लिए firebase use --add चलाएं। आपके द्वारा पहले बनाए गए प्रोजेक्ट का चयन करने के लिए संकेतों का पालन करें और यदि उपनाम चुनने के लिए कहा जाए तो default दर्ज करें।

5. ऐप चलाएं

अब पहली बार फायरबेस एमुलेटर सूट और फ्रेंडलीईट्स एंड्रॉइड ऐप चलाने का समय आ गया है।

एम्यूलेटर चलाएँ

अपने टर्मिनल में friendlyeats-android डायरेक्टरी के भीतर से firebase emulators:start चलाएं। आपको इस तरह लॉग देखना चाहिए:

$ firebase emulators:start
i  emulators: Starting emulators: auth, firestore
i  firestore: Firestore Emulator logging to firestore-debug.log
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ localhost:8080 │ http://localhost:4000/firestore │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

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

ऐप को एम्यूलेटर से कनेक्ट करें

एंड्रॉइड स्टूडियो में util/FirestoreInitializer.kt और util/AuthInitializer.kt फ़ाइलें खोलें। इन फ़ाइलों में एप्लिकेशन स्टार्टअप पर फायरबेस एसडीके को आपकी मशीन पर चल रहे स्थानीय एमुलेटर से कनेक्ट करने का तर्क शामिल है।

FirestoreInitializer वर्ग की create() विधि पर, कोड के इस टुकड़े की जाँच करें:

    // Use emulators only in debug builds
    if (BuildConfig.DEBUG) {
        firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
    }

हम BuildConfig उपयोग यह सुनिश्चित करने के लिए कर रहे हैं कि हम केवल तभी एमुलेटर से कनेक्ट हों जब हमारा ऐप debug मोड में चल रहा हो। जब हम ऐप को release मोड में संकलित करेंगे तो यह स्थिति झूठी होगी।

हम देख सकते हैं कि यह फायरबेस एसडीके को स्थानीय फायरस्टोर एमुलेटर से कनेक्ट करने के लिए useEmulator(host, port) विधि का उपयोग कर रहा है। पूरे ऐप में हम FirebaseFirestore के इस उदाहरण तक पहुंचने के लिए FirebaseUtil.getFirestore() का उपयोग करेंगे, ताकि हमें यकीन हो कि debug मोड में चलते समय हम हमेशा Firestore एमुलेटर से कनेक्ट रहें।

ऐप चलाएँ

यदि आपने google-services.json फ़ाइल को ठीक से जोड़ा है, तो प्रोजेक्ट को अब संकलित होना चाहिए। एंड्रॉइड स्टूडियो में बिल्ड > रीबिल्ड प्रोजेक्ट पर क्लिक करें और सुनिश्चित करें कि कोई त्रुटि शेष नहीं है।

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

अब अपने वेब ब्राउज़र में http://localhost:4000 पर नेविगेट करके एमुलेटर यूआई खोलें। फिर प्रमाणीकरण टैब पर क्लिक करें और आपको वह खाता देखना चाहिए जिसे आपने अभी बनाया है:

फायरबेस ऑथ एम्यूलेटर

एक बार जब आप साइन इन प्रक्रिया पूरी कर लें तो आपको ऐप होम स्क्रीन दिखनी चाहिए:

de06424023ffb4b9.png

जल्द ही हम होम स्क्रीन को पॉप्युलेट करने के लिए कुछ डेटा जोड़ेंगे।

6. फायरस्टोर पर डेटा लिखें

इस अनुभाग में हम फायरस्टोर को कुछ डेटा लिखेंगे ताकि हम वर्तमान में खाली होम स्क्रीन को पॉप्युलेट कर सकें।

हमारे ऐप में मुख्य मॉडल ऑब्जेक्ट एक रेस्तरां है ( model/Restaurant.kt देखें)। फायरस्टोर डेटा को दस्तावेज़ों, संग्रहों और उपसंग्रहों में विभाजित किया गया है। हम प्रत्येक रेस्तरां को "restaurants" नामक शीर्ष-स्तरीय संग्रह में एक दस्तावेज़ के रूप में संग्रहीत करेंगे। फायरस्टोर डेटा मॉडल के बारे में अधिक जानने के लिए, दस्तावेज़ीकरण में दस्तावेज़ों और संग्रहों के बारे में पढ़ें।

प्रदर्शन उद्देश्यों के लिए, जब हम ओवरफ्लो मेनू में "यादृच्छिक आइटम जोड़ें" बटन पर क्लिक करेंगे तो हम दस यादृच्छिक रेस्तरां बनाने के लिए ऐप में कार्यक्षमता जोड़ देंगे। MainFragment.kt फ़ाइल खोलें और सामग्री को onAddItemsClicked() विधि से बदलें:

    private fun onAddItemsClicked() {
        val restaurantsRef = firestore.collection("restaurants")
        for (i in 0..9) {
            // Create random restaurant / ratings
            val randomRestaurant = RestaurantUtil.getRandom(requireContext())

            // Add restaurant
            restaurantsRef.add(randomRestaurant)
        }
    }

उपरोक्त कोड के बारे में ध्यान देने योग्य कुछ महत्वपूर्ण बातें हैं:

  • हमने "restaurants" संग्रह का संदर्भ प्राप्त करके शुरुआत की। जब दस्तावेज़ जोड़े जाते हैं तो संग्रह अंतर्निहित रूप से बनाए जाते हैं, इसलिए डेटा लिखने से पहले संग्रह बनाने की कोई आवश्यकता नहीं थी।
  • कोटलिन डेटा क्लास का उपयोग करके दस्तावेज़ बनाए जा सकते हैं, जिसका उपयोग हम प्रत्येक रेस्तरां दस्तावेज़ बनाने के लिए करते हैं।
  • add() विधि एक ऑटो-जनरेटेड आईडी के साथ एक संग्रह में एक दस्तावेज़ जोड़ती है, इसलिए हमें प्रत्येक रेस्तरां के लिए एक अद्वितीय आईडी निर्दिष्ट करने की आवश्यकता नहीं है।

अब ऐप को फिर से चलाएं और आपके द्वारा अभी लिखे गए कोड को लागू करने के लिए ओवरफ्लो मेनू (ऊपरी दाएं कोने पर) में "रैंडम आइटम जोड़ें" बटन पर क्लिक करें:

95691e9b71ba55e3.png

अब अपने वेब ब्राउज़र में http://localhost:4000 पर नेविगेट करके एमुलेटर यूआई खोलें। फिर फायरस्टोर टैब पर क्लिक करें और आपको वह डेटा देखना चाहिए जो आपने अभी जोड़ा है:

फायरबेस ऑथ एम्यूलेटर

यह डेटा आपकी मशीन के लिए 100% स्थानीय है। वास्तव में, आपके वास्तविक प्रोजेक्ट में अभी तक फायरस्टोर डेटाबेस भी शामिल नहीं है! इसका मतलब यह है कि बिना किसी परिणाम के इस डेटा को संशोधित करने और हटाने का प्रयोग करना सुरक्षित है।

बधाई हो, आपने अभी-अभी फायरस्टोर को डेटा लिखा है! अगले चरण में हम सीखेंगे कि इस डेटा को ऐप में कैसे प्रदर्शित किया जाए।

7. फायरस्टोर से डेटा प्रदर्शित करें

इस चरण में हम सीखेंगे कि फायरस्टोर से डेटा कैसे पुनर्प्राप्त करें और इसे अपने ऐप में कैसे प्रदर्शित करें। फायरस्टोर से डेटा पढ़ने का पहला कदम एक Query बनाना है। MainFragment.kt फ़ाइल खोलें और onViewCreated() विधि की शुरुआत में निम्नलिखित कोड जोड़ें:

        // Firestore
        firestore = Firebase.firestore

        // Get the 50 highest rated restaurants
        query = firestore.collection("restaurants")
            .orderBy("avgRating", Query.Direction.DESCENDING)
            .limit(LIMIT.toLong())

अब हम क्वेरी सुनना चाहते हैं, ताकि हमें सभी मेल खाने वाले दस्तावेज़ मिलें और वास्तविक समय में भविष्य के अपडेट के बारे में सूचित किया जा सके। क्योंकि हमारा अंतिम लक्ष्य इस डेटा को RecyclerView से जोड़ना है, हमें डेटा को सुनने के लिए एक RecyclerView.Adapter क्लास बनाने की आवश्यकता है।

FirestoreAdapter क्लास खोलें, जिसे आंशिक रूप से पहले ही लागू किया जा चुका है। सबसे पहले, आइए एडाप्टर को EventListener लागू करें और onEvent फ़ंक्शन को परिभाषित करें ताकि यह फायरस्टोर क्वेरी के अपडेट प्राप्त कर सके:

abstract class FirestoreAdapter<VH : RecyclerView.ViewHolder>(private var query: Query?) :
        RecyclerView.Adapter<VH>(),
        EventListener<QuerySnapshot> { // Add this implements
    
    // ...

    // Add this method
    override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
        
        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e)
            return
        }

        // Dispatch the event
        if (documentSnapshots != null) {
            for (change in documentSnapshots.documentChanges) {
                // snapshot of the changed document
                when (change.type) {
                    DocumentChange.Type.ADDED -> {
                        // TODO: handle document added
                    }
                    DocumentChange.Type.MODIFIED -> {
                        // TODO: handle document changed
                    }
                    DocumentChange.Type.REMOVED -> {
                        // TODO: handle document removed
                    }
                }
            }
        }

        onDataChanged()
    }
    
    // ...
}

प्रारंभिक लोड पर श्रोता को प्रत्येक नए दस्तावेज़ के लिए एक ADDED ईवेंट प्राप्त होगा। जैसे-जैसे क्वेरी का परिणाम सेट समय के साथ बदलता है, श्रोता को परिवर्तनों वाले अधिक ईवेंट प्राप्त होंगे। अब श्रोता का कार्यान्वयन समाप्त करते हैं। सबसे पहले तीन नए तरीके जोड़ें: onDocumentAdded , onDocumentModified , और onDocumentRemoved :

    private fun onDocumentAdded(change: DocumentChange) {
        snapshots.add(change.newIndex, change.document)
        notifyItemInserted(change.newIndex)
    }

    private fun onDocumentModified(change: DocumentChange) {
        if (change.oldIndex == change.newIndex) {
            // Item changed but remained in same position
            snapshots[change.oldIndex] = change.document
            notifyItemChanged(change.oldIndex)
        } else {
            // Item changed and changed position
            snapshots.removeAt(change.oldIndex)
            snapshots.add(change.newIndex, change.document)
            notifyItemMoved(change.oldIndex, change.newIndex)
        }
    }

    private fun onDocumentRemoved(change: DocumentChange) {
        snapshots.removeAt(change.oldIndex)
        notifyItemRemoved(change.oldIndex)
    }

फिर onEvent से इन नई विधियों को कॉल करें:

    override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {

        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e)
            return
        }

        // Dispatch the event
        if (documentSnapshots != null) {
            for (change in documentSnapshots.documentChanges) {
                // snapshot of the changed document
                when (change.type) {
                    DocumentChange.Type.ADDED -> {
                        onDocumentAdded(change) // Add this line
                    }
                    DocumentChange.Type.MODIFIED -> {
                        onDocumentModified(change) // Add this line
                    }
                    DocumentChange.Type.REMOVED -> {
                        onDocumentRemoved(change) // Add this line
                    }
                }
            }
        }

        onDataChanged()
    }

अंत में श्रोता को संलग्न करने के लिए startListening() विधि को लागू करें:

    fun startListening() {
        if (registration == null) {
            registration = query.addSnapshotListener(this)
        }
    }

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

9e45f40faefce5d0.png

अब अपने ब्राउज़र में एमुलेटर यूआई पर वापस जाएं और रेस्तरां के नामों में से एक को संपादित करें। आपको इसे ऐप में लगभग तुरंत ही बदलता हुआ देखना चाहिए!

8. डेटा को सॉर्ट और फ़िल्टर करें

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

ऐप के शीर्ष पर सफेद बार पर क्लिक करने से एक फ़िल्टर डायलॉग सामने आता है। इस अनुभाग में हम इस संवाद को कार्यान्वित करने के लिए फायरस्टोर क्वेरीज़ का उपयोग करेंगे:

67898572a35672a5.png

आइए MainFragment.kt की onFilter() विधि को संपादित करें। यह विधि एक Filters ऑब्जेक्ट को स्वीकार करती है जो एक सहायक ऑब्जेक्ट है जिसे हमने फ़िल्टर संवाद के आउटपुट को कैप्चर करने के लिए बनाया है। हम फ़िल्टर से क्वेरी बनाने के लिए इस विधि को बदल देंगे:

    override fun onFilter(filters: Filters) {
        // Construct query basic query
        var query: Query = firestore.collection("restaurants")

        // Category (equality filter)
        if (filters.hasCategory()) {
            query = query.whereEqualTo(Restaurant.FIELD_CATEGORY, filters.category)
        }

        // City (equality filter)
        if (filters.hasCity()) {
            query = query.whereEqualTo(Restaurant.FIELD_CITY, filters.city)
        }

        // Price (equality filter)
        if (filters.hasPrice()) {
            query = query.whereEqualTo(Restaurant.FIELD_PRICE, filters.price)
        }

        // Sort by (orderBy with direction)
        if (filters.hasSortBy()) {
            query = query.orderBy(filters.sortBy.toString(), filters.sortDirection)
        }

        // Limit items
        query = query.limit(LIMIT.toLong())

        // Update the query
        adapter.setQuery(query)

        // Set header
        binding.textCurrentSearch.text = HtmlCompat.fromHtml(
            filters.getSearchDescription(requireContext()),
            HtmlCompat.FROM_HTML_MODE_LEGACY
        )
        binding.textCurrentSortBy.text = filters.getOrderDescription(requireContext())

        // Save filters
        viewModel.filters = filters
    }

उपरोक्त स्निपेट में हम दिए गए फ़िल्टर से मिलान करने के लिए where और orderBy क्लॉज को जोड़कर एक Query ऑब्जेक्ट बनाते हैं।

ऐप को दोबारा चलाएं और सबसे लोकप्रिय कम कीमत वाले रेस्तरां दिखाने के लिए निम्नलिखित फ़िल्टर का चयन करें:

7a67a8a400c80c50.png

अब आपको केवल कम कीमत वाले विकल्पों वाले रेस्तरां की फ़िल्टर की गई सूची देखनी चाहिए:

a670188398c3c59.png

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

9. डेटा को उपसंग्रह में व्यवस्थित करें

इस अनुभाग में हम ऐप में रेटिंग जोड़ेंगे ताकि उपयोगकर्ता अपने पसंदीदा (या सबसे कम पसंदीदा) रेस्तरां की समीक्षा कर सकें।

संग्रह और उपसंग्रह

अब तक हमने सभी रेस्तरां डेटा को "रेस्तरां" नामक शीर्ष-स्तरीय संग्रह में संग्रहीत किया है। जब कोई उपयोगकर्ता किसी रेस्तरां को रेटिंग देता है तो हम रेस्तरां में एक नई Rating ऑब्जेक्ट जोड़ना चाहते हैं। इस कार्य के लिए हम एक उपसंग्रह का उपयोग करेंगे। आप उपसंग्रह को एक ऐसे संग्रह के रूप में सोच सकते हैं जो किसी दस्तावेज़ से जुड़ा हुआ है। इसलिए प्रत्येक रेस्तरां दस्तावेज़ में रेटिंग दस्तावेज़ों से भरा एक रेटिंग उपसंग्रह होगा। उपसंग्रह हमारे दस्तावेज़ों को फूलाये बिना या जटिल प्रश्नों की आवश्यकता के बिना डेटा को व्यवस्थित करने में मदद करते हैं।

उपसंग्रह तक पहुँचने के लिए, मूल दस्तावेज़ पर .collection() कॉल करें:

val subRef = firestore.collection("restaurants")
        .document("abc123")
        .collection("ratings")

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

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

उचित उपसंग्रह में Rating जोड़ने के लिए केवल .add() को कॉल करने की आवश्यकता होती है, लेकिन हमें नए डेटा को प्रतिबिंबित करने के लिए Restaurant ऑब्जेक्ट की औसत रेटिंग और रेटिंग की संख्या को भी अपडेट करना होगा। यदि हम इन दो परिवर्तनों को करने के लिए अलग-अलग ऑपरेशनों का उपयोग करते हैं तो कई दौड़ स्थितियाँ होती हैं जिनके परिणामस्वरूप पुराना या गलत डेटा हो सकता है।

यह सुनिश्चित करने के लिए कि रेटिंग ठीक से जोड़ी गई हैं, हम किसी रेस्तरां में रेटिंग जोड़ने के लिए लेनदेन का उपयोग करेंगे। यह लेन-देन कुछ क्रियाएं निष्पादित करेगा:

  • रेस्तरां की वर्तमान रेटिंग पढ़ें और नई रेटिंग की गणना करें
  • उपसंग्रह में रेटिंग जोड़ें
  • रेस्तरां की औसत रेटिंग और रेटिंग की संख्या अपडेट करें

RestaurantDetailFragment.kt खोलें और addRating फ़ंक्शन लागू करें:

    private fun addRating(restaurantRef: DocumentReference, rating: Rating): Task<Void> {
        // Create reference for new rating, for use inside the transaction
        val ratingRef = restaurantRef.collection("ratings").document()

        // In a transaction, add the new rating and update the aggregate totals
        return firestore.runTransaction { transaction ->
            val restaurant = transaction.get(restaurantRef).toObject<Restaurant>()
                ?: throw Exception("Restaurant not found at ${restaurantRef.path}")

            // Compute new number of ratings
            val newNumRatings = restaurant.numRatings + 1

            // Compute new average rating
            val oldRatingTotal = restaurant.avgRating * restaurant.numRatings
            val newAvgRating = (oldRatingTotal + rating.rating) / newNumRatings

            // Set new restaurant info
            restaurant.numRatings = newNumRatings
            restaurant.avgRating = newAvgRating

            // Commit to Firestore
            transaction.set(restaurantRef, restaurant)
            transaction.set(ratingRef, rating)

            null
        }
    }

addRating() फ़ंक्शन संपूर्ण लेनदेन का प्रतिनिधित्व करने वाला एक Task लौटाता है। onRating() फ़ंक्शन में लेन-देन के परिणाम पर प्रतिक्रिया देने के लिए श्रोताओं को कार्य में जोड़ा जाता है।

अब ऐप को दोबारा चलाएं और किसी एक रेस्तरां पर क्लिक करें, जिससे रेस्तरां विवरण स्क्रीन सामने आ जाएगी। समीक्षा जोड़ना शुरू करने के लिए + बटन पर क्लिक करें। कई सितारे चुनकर और कुछ पाठ दर्ज करके एक समीक्षा जोड़ें।

78fa16cdf8ef435a.png

सबमिट दबाने से लेनदेन शुरू हो जाएगा। जब लेन-देन पूरा हो जाएगा, तो आपको अपनी समीक्षा नीचे प्रदर्शित दिखाई देगी और रेस्तरां की समीक्षा संख्या का अपडेट दिखाई देगा:

f9e670f40bd615b0.png

बधाई! अब आपके पास क्लाउड फायरस्टोर पर बनाया गया एक सामाजिक, स्थानीय, मोबाइल रेस्तरां समीक्षा ऐप है। मैंने सुना है कि ये आजकल बहुत लोकप्रिय हैं।

10. अपना डेटा सुरक्षित करें

अभी तक हमने इस एप्लिकेशन की सुरक्षा पर विचार नहीं किया है। हमें कैसे पता चलेगा कि उपयोगकर्ता केवल सही डेटा ही पढ़ और लिख सकते हैं? फायरस्टोर डेटाबेस को सुरक्षा नियम नामक कॉन्फ़िगरेशन फ़ाइल द्वारा सुरक्षित किया जाता है।

firestore.rules फ़ाइल खोलें, आपको निम्नलिखित देखना चाहिए:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      //
      // WARNING: These rules are insecure! We will replace them with
      // more secure rules later in the codelab
      //
      allow read, write: if request.auth != null;
    }
  }
}

आइए अवांछित डेटा एक्सेस या परिवर्तनों को रोकने के लिए इन नियमों को बदलें, firestore.rules फ़ाइल खोलें और सामग्री को निम्नलिखित से बदलें:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Determine if the value of the field "key" is the same
    // before and after the request.
    function isUnchanged(key) {
      return (key in resource.data)
        && (key in request.resource.data)
        && (resource.data[key] == request.resource.data[key]);
    }

    // Restaurants
    match /restaurants/{restaurantId} {
      // Any signed-in user can read
      allow read: if request.auth != null;

      // Any signed-in user can create
      // WARNING: this rule is for demo purposes only!
      allow create: if request.auth != null;

      // Updates are allowed if no fields are added and name is unchanged
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && isUnchanged("name");

      // Deletes are not allowed.
      // Note: this is the default, there is no need to explicitly state this.
      allow delete: if false;

      // Ratings
      match /ratings/{ratingId} {
        // Any signed-in user can read
        allow read: if request.auth != null;

        // Any signed-in user can create if their uid matches the document
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;

        // Deletes and updates are not allowed (default)
        allow update, delete: if false;
      }
    }
  }
}

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

सुरक्षा नियमों के बारे में अधिक पढ़ने के लिए दस्तावेज़ीकरण पर जाएँ।

11. निष्कर्ष

अब आपने फायरस्टोर के शीर्ष पर एक पूर्ण-विशेषताओं वाला ऐप बनाया है। आपने फायरस्टोर की सबसे महत्वपूर्ण सुविधाओं के बारे में सीखा, जिनमें शामिल हैं:

  • दस्तावेज़ और संग्रह
  • डेटा पढ़ना और लिखना
  • प्रश्नों के साथ क्रमबद्ध करना और फ़िल्टर करना
  • उपसंग्रह
  • लेनदेन

और अधिक जानें

फायरस्टोर के बारे में सीखते रहने के लिए, शुरुआत करने के लिए यहां कुछ अच्छी जगहें दी गई हैं:

इस कोडलैब में रेस्तरां ऐप "फ्रेंडली ईट्स" उदाहरण एप्लिकेशन पर आधारित था। आप यहां उस ऐप के लिए स्रोत कोड ब्राउज़ कर सकते हैं।

वैकल्पिक: उत्पादन में तैनात करें

अभी तक इस ऐप ने केवल फायरबेस एमुलेटर सूट का उपयोग किया है। यदि आप सीखना चाहते हैं कि इस ऐप को वास्तविक फायरबेस प्रोजेक्ट पर कैसे तैनात किया जाए, तो अगले चरण पर जारी रखें।

12. (वैकल्पिक) अपना ऐप परिनियोजित करें

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

फायरबेस प्रमाणीकरण

फायरबेस कंसोल में प्रमाणीकरण अनुभाग पर जाएं और प्रारंभ करें पर क्लिक करें। साइन-इन विधि टैब पर जाएँ और मूल प्रदाताओं से ईमेल/पासवर्ड विकल्प चुनें।

ईमेल/पासवर्ड साइन-इन विधि सक्षम करें और सहेजें पर क्लिक करें।

साइन-इन-प्रदाता.पीएनजी

इस firestore

डेटाबेस बनाएं

कंसोल के फायरस्टोर डेटाबेस अनुभाग पर जाएँ और डेटाबेस बनाएँ पर क्लिक करें:

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

परिनियोजन नियम

आपके द्वारा पहले लिखे गए सुरक्षा नियमों को लागू करने के लिए, कोडलैब निर्देशिका में निम्नलिखित कमांड चलाएँ:

$ firebase deploy --only firestore:rules

यह आपके प्रोजेक्ट में firestore.rules की सामग्री को तैनात करेगा, जिसकी पुष्टि आप कंसोल में नियम टैब पर जाकर कर सकते हैं।

अनुक्रमणिका परिनियोजित करें

FriendlyEats ऐप में जटिल सॉर्टिंग और फ़िल्टरिंग है जिसके लिए कई कस्टम कंपाउंड इंडेक्स की आवश्यकता होती है। इन्हें फायरबेस कंसोल में हाथ से बनाया जा सकता है लेकिन firestore.indexes.json फ़ाइल में उनकी परिभाषा लिखना और फायरबेस सीएलआई का उपयोग करके उन्हें तैनात करना आसान है।

यदि आप firestore.indexes.json फ़ाइल खोलते हैं तो आप देखेंगे कि आवश्यक अनुक्रमणिकाएँ पहले ही प्रदान की जा चुकी हैं:

{
  "indexes": [
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    }
  ],
  "fieldOverrides": []
}

इन इंडेक्स को तैनात करने के लिए निम्नलिखित कमांड चलाएँ:

$ firebase deploy --only firestore:indexes

ध्यान दें कि इंडेक्स निर्माण तात्कालिक नहीं है, आप फायरबेस कंसोल में प्रगति की निगरानी कर सकते हैं।

ऐप कॉन्फ़िगर करें

util/FirestoreInitializer.kt और util/AuthInitializer.kt फ़ाइलों में हमने डिबग मोड में एमुलेटर से कनेक्ट करने के लिए Firebase SDK को कॉन्फ़िगर किया है:

    override fun create(context: Context): FirebaseFirestore {
        val firestore = Firebase.firestore
        // Use emulators only in debug builds
        if (BuildConfig.DEBUG) {
            firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
        }
        return firestore
    }

यदि आप अपने वास्तविक फायरबेस प्रोजेक्ट के साथ अपने ऐप का परीक्षण करना चाहते हैं तो आप यह कर सकते हैं:

  1. ऐप को रिलीज़ मोड में बनाएं और इसे डिवाइस पर चलाएं।
  2. अस्थायी रूप से BuildConfig.DEBUG false से बदलें और ऐप को फिर से चलाएँ।

ध्यान दें कि उत्पादन से ठीक से जुड़ने के लिए आपको ऐप से साइन आउट करने और फिर से साइन इन करने की आवश्यकता हो सकती है।