Cloud Firestore Android Codelab

Ziele

In diesem Codelab erstellen Sie eine Restaurantempfehlungs-App auf Android, die von Cloud Firestore unterstützt wird. Du wirst lernen wie:

  • Lesen und Schreiben von Daten in Firestore von einer Android-App
  • Hören Sie sich Änderungen in Firestore-Daten in Echtzeit an
  • Verwenden Sie Firebase Authentication und Sicherheitsregeln, um Firestore-Daten zu schützen
  • Schreiben Sie komplexe Firestore-Abfragen

Voraussetzungen

Stellen Sie vor dem Starten dieses Codelabs sicher, dass Sie Folgendes haben:

  • Android Studio 4.0 oder höher
  • Ein Android-Emulator
  • Node.js Version 10 oder höher
  • Java Version 8 oder höher
  1. Melden Sie sich bei der Firebase - Konsole mit Ihrem Google - Konto.
  2. In der Firebase - Konsole , klicken Sie auf Hinzufügen Projekt.
  3. Wie in der unten stehenden Abbildung gezeigt, einen Namen für das Projekt Firebase (zum Beispiel „Freundlich Essen“), ein und klicken Sie auf Weiter.

9d2f625aebcab6af.png

  1. Möglicherweise werden Sie aufgefordert, Google Analytics zu aktivieren. Für die Zwecke dieses Codelabs spielt Ihre Auswahl keine Rolle.
  2. Nach ungefähr einer Minute ist Ihr Firebase-Projekt fertig. Klicken Sie auf Weiter.

Laden Sie den Code herunter

Führen Sie den folgenden Befehl aus, um den Beispielcode für dieses Codelab zu klonen. Dadurch wird ein Ordner mit dem Namen erstellen friendlyeats-android auf Ihrem Computer:

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

Wenn Sie kein Git auf Ihrem Computer haben, können Sie den Code auch direkt von GitHub herunterladen.

Importieren Sie das Projekt in Android Studio. Sie werden wahrscheinlich einige Kompilierungsfehlern oder vielleicht eine Warnung über eine fehlende siehe google-services.json Datei. Wir werden dies im nächsten Abschnitt korrigieren.

Firebase-Konfiguration hinzufügen

  1. In der Firebase - Konsole , wählen Sie Projektübersicht im linken Navigationsbereich . Klicken Sie auf die Android , um die Plattform zu wählen. Wenn für einen Paketnamen aufgefordert com.google.firebase.example.fireeats

73d151ed16016421.png

  1. Klicken Sie auf Registrieren App und folgen Sie den Anweisungen , um die zum Download google-services.json - Datei, und es in die bewegen app/ Ordner des Beispielcodes. Dann klicken Sie auf Weiter.

In diesem Code - Lab finden Sie die verwenden Firebase Emulator Suite lokal Wolke Firestor und andere Dienste Firebase zu emulieren. Dies bietet eine sichere, schnelle und kostenlose lokale Entwicklungsumgebung zum Erstellen Ihrer App.

Installieren Sie die Firebase-CLI

Zuerst müssen Sie die Installation Firebase CLI . Der einfachste Weg , dies zu tun , ist die Verwendung npm :

npm install -g firebase-tools

Wenn Sie nicht haben npm oder ein Fehler auftreten, lesen Sie die Installationsanweisungen eine eigenständige binäre für Ihre Plattform zu erhalten.

Sobald Sie die CLI installiert haben, läuft firebase --version sollten berichten über eine Version von 9.0.0 oder höher:

$ firebase --version
9.0.0

Einloggen

Führen firebase login die CLI zu Ihrem Google - Konto zu verbinden. Dadurch wird ein neues Browserfenster geöffnet, um den Anmeldevorgang abzuschließen. Stellen Sie sicher, dass Sie dasselbe Konto auswählen, das Sie beim Erstellen Ihres Firebase-Projekts zuvor verwendet haben.

Von innerhalb der friendlyeats-android - Ordner laufen firebase use --add Ihr lokales Projekt zu Ihrem Projekt Firebase zu verbinden. Folgen Sie den Anweisungen , um das Projekt , das Sie zuvor erstellt haben zu wählen und wenn gefragt ein Alias eingeben wählen default .

Jetzt ist es an der Zeit, die Firebase Emulator Suite und die FriendlyEats Android App zum ersten Mal auszuführen.

Führen Sie die Emulatoren aus

In Ihrem Terminal aus dem friendlyeats-android - Verzeichnis laufen firebase emulators:start Sie die Firebase Emulatoren zu starten. Sie sollten Protokolle wie diese sehen:

$ 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.

Sie haben jetzt eine vollständige lokale Entwicklungsumgebung auf Ihrem Computer! Stellen Sie sicher, dass dieser Befehl für den Rest des Codelabs ausgeführt wird. Ihre Android-App muss sich mit den Emulatoren verbinden.

App mit Emulatoren verbinden

Öffnen Sie die Datei FirebaseUtil.java in Android Studio. Diese Datei enthält die Logik zum Verbinden der Firebase SDKs mit den lokalen Emulatoren, die auf Ihrem Computer ausgeführt werden.

Untersuchen Sie oben in der Datei diese Zeile:

    /** Use emulators only in debug builds **/
    private static final boolean sUseEmulators = BuildConfig.DEBUG;

Wir verwenden BuildConfig um sicherzustellen , dass wir nur auf die Emulatoren verbinden , wenn unsere App im laufenden debug - Modus. Wenn wir die App in kompilieren release - Modus wird diese Bedingung falsch sein.

Nun nehmen Sie einen Blick auf die getFirestore() Methode:

    public static FirebaseFirestore getFirestore() {
        if (FIRESTORE == null) {
            FIRESTORE = FirebaseFirestore.getInstance();

            // Connect to the Cloud Firestore emulator when appropriate. The host '10.0.2.2' is a
            // special IP address to let the Android emulator connect to 'localhost'.
            if (sUseEmulators) {
                FIRESTORE.useEmulator("10.0.2.2", 8080);
            }
        }

        return FIRESTORE;
    }

Wir können sehen , dass es den unter Verwendung von useEmulator(host, port) Methode , um die Firebase SDK auf das lokale Firestore - Emulator zu verbinden. In der gesamten App verwenden wir FirebaseUtil.getFirestore() diese Instanz zugreifen FirebaseFirestore , so dass wir sicher sind , dass wir immer auf den Firestore - Emulator zu verbinden , wenn im laufenden debug - Modus.

Führen Sie die App aus

Wenn Sie die hinzugefügt google-services.json Datei korrekt, sollte das Projekt nun kompilieren. In Android Studio Klicken Sie auf Build> Projekt neu und stellen Sie sicher , dass es keine verbleibenden Fehler sind.

Im Android Studio Führen Sie die App auf Ihrem Android - Emulator. Zuerst wird Ihnen ein "Anmelden"-Bildschirm angezeigt. Sie können eine beliebige E-Mail-Adresse und ein beliebiges Passwort verwenden, um sich bei der App anzumelden. Dieser Anmeldevorgang stellt eine Verbindung zum Firebase Authentication-Emulator her, sodass keine echten Anmeldeinformationen übertragen werden.

Jetzt die Emulatoren UI öffnen , indem Sie zu http: // localhost: 4000 in Ihrem Web - Browser. Dann klicken Sie auf die Registerkarte Authentifizierung und Sie sollten das Konto , das Sie gerade erstellt haben sehen:

Firebase Auth-Emulator

Sobald Sie den Anmeldevorgang abgeschlossen haben, sollten Sie den Startbildschirm der App sehen:

de06424023ffb4b9.png

In Kürze werden wir einige Daten hinzufügen, um den Startbildschirm zu füllen.

In diesem Abschnitt werden wir einige Daten in Firestore schreiben, damit wir den derzeit leeren Startbildschirm füllen können.

Das Hauptmodellobjekt in unserer App ist ein Restaurant (siehe model/Restaurant.java ). Firestore-Daten werden in Dokumente, Sammlungen und Untersammlungen aufgeteilt. Wir speichern jedes Restaurant als Dokument in einer Top-Level - Kollektion namens "restaurants" . Mehr über die Firestore - Datenmodell, lesen Sie über Dokumente und Sammlungen in lernen die Dokumentation .

Zu Demonstrationszwecken werden wir der App Funktionen hinzufügen, um zehn zufällige Restaurants zu erstellen, wenn wir im Überlaufmenü auf die Schaltfläche "Zufällige Elemente hinzufügen" klicken. Öffnen Sie die Datei MainActivity.java und Füllung im onAddItemsClicked() Methode:

    private void onAddItemsClicked() {
        // Get a reference to the restaurants collection
        CollectionReference restaurants = mFirestore.collection("restaurants");

        for (int i = 0; i < 10; i++) {
            // Get a random Restaurant POJO
            Restaurant restaurant = RestaurantUtil.getRandom(this);

            // Add a new document to the restaurants collection
            restaurants.add(restaurant);
        }
    }

Beim obigen Code sind einige wichtige Dinge zu beachten:

  • Wir begannen mit einem Verweis auf die immer "restaurants" Sammlung. Sammlungen werden beim Hinzufügen von Dokumenten implizit erstellt, sodass die Sammlung vor dem Schreiben von Daten nicht erstellt werden musste.
  • Dokumente können mit POJOs erstellt werden, die wir verwenden, um jedes Restaurantdokument zu erstellen.
  • Die add() Methode fügt ein Dokument zu einer Sammlung mit einer automatisch generierten ID, so dass wir nicht einer eindeutigen ID für jedes Restaurant angeben mussten.

Führen Sie nun die App erneut aus und klicken Sie im Überlaufmenü auf die Schaltfläche "Zufällige Elemente hinzufügen", um den gerade geschriebenen Code aufzurufen:

95691e9b71ba55e3.png

Jetzt die Emulatoren UI öffnen , indem Sie zu http: // localhost: 4000 in Ihrem Web - Browser. Dann klicken Sie auf die Registerkarte Firestore und Sie sollten die Daten , die Sie gerade hinzugefügt sehen:

Firebase Auth-Emulator

Diese Daten sind zu 100 % lokal auf Ihrem Computer. Tatsächlich enthält Ihr reales Projekt noch nicht einmal eine Firestore-Datenbank! Dies bedeutet, dass Sie ohne Konsequenzen mit dem Ändern und Löschen dieser Daten experimentieren können.

Herzlichen Glückwunsch, Sie haben gerade Daten an Firestore geschrieben! Im nächsten Schritt erfahren wir, wie Sie diese Daten in der App anzeigen.

In diesem Schritt lernen wir, wie Sie Daten aus Firestore abrufen und in unserer App anzeigen. Der erste Schritt von Daten von Firestor zum Lesen ist eine erstellen Query . Modifizieren , um die onCreate() Methode:

        mFirestore = FirebaseUtil.getFirestore();

        // Get the 50 highest rated restaurants
        mQuery = mFirestore.collection("restaurants")
                .orderBy("avgRating", Query.Direction.DESCENDING)
                .limit(LIMIT);

Nun wollen wir uns die Abfrage anhören, damit wir alle passenden Dokumente bekommen und in Echtzeit über zukünftige Updates informiert werden. Da unser Fernziel ist es, diese Daten zu einem binden RecyclerView , brauchen wir eine erstellen RecyclerView.Adapter Klasse , um die Daten zu hören.

Öffnen Sie die FirestoreAdapter - Klasse, die bereits teilweise umgesetzt wurde. Lassen Sie sich zuerst macht den Adapter implementieren EventListener und die definiert onEvent Funktion , so dass es Aktualisierungen eine Firestor Abfrage empfangen kann:

public abstract class FirestoreAdapter<VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH>
        implements EventListener<QuerySnapshot> { // Add this "implements"

    // ...

    // Add this method
    @Override
    public void onEvent(QuerySnapshot documentSnapshots,
                        FirebaseFirestoreException e) {

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

        // Dispatch the event
        for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
            // Snapshot of the changed document
            DocumentSnapshot snapshot = change.getDocument();

            switch (change.getType()) {
                case ADDED:
                    // TODO: handle document added
                    break;
                case MODIFIED:
                    // TODO: handle document modified
                    break;
                case REMOVED:
                    // TODO: handle document removed
                    break;
            }
        }

        onDataChanged();
    }

  // ...
}

Bei der ersten Last wird der Hörer eine Empfangs ADDED Ereignis für jedes neue Dokument. Da sich die Ergebnismenge der Abfrage im Laufe der Zeit ändert, erhält der Listener mehr Ereignisse, die die Änderungen enthalten. Lassen Sie uns nun die Implementierung des Listeners abschließen. Die ersten drei neue Methoden hinzu: onDocumentAdded , onDocumentModified und auf onDocumentRemoved :

    protected void onDocumentAdded(DocumentChange change) {
        mSnapshots.add(change.getNewIndex(), change.getDocument());
        notifyItemInserted(change.getNewIndex());
    }

    protected void onDocumentModified(DocumentChange change) {
        if (change.getOldIndex() == change.getNewIndex()) {
            // Item changed but remained in same position
            mSnapshots.set(change.getOldIndex(), change.getDocument());
            notifyItemChanged(change.getOldIndex());
        } else {
            // Item changed and changed position
            mSnapshots.remove(change.getOldIndex());
            mSnapshots.add(change.getNewIndex(), change.getDocument());
            notifyItemMoved(change.getOldIndex(), change.getNewIndex());
        }
    }

    protected void onDocumentRemoved(DocumentChange change) {
        mSnapshots.remove(change.getOldIndex());
        notifyItemRemoved(change.getOldIndex());
    }

Dann rufen Sie diese neuen Methoden aus onEvent :

    @Override
    public void onEvent(QuerySnapshot documentSnapshots,
                        FirebaseFirestoreException e) {

        // ...

        // Dispatch the event
        for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
            // Snapshot of the changed document
            DocumentSnapshot snapshot = change.getDocument();

            switch (change.getType()) {
                case ADDED:
                    onDocumentAdded(change); // Add this line
                    break;
                case MODIFIED:
                    onDocumentModified(change); // Add this line
                    break;
                case REMOVED:
                    onDocumentRemoved(change); // Add this line
                    break;
            }
        }

        onDataChanged();
    }

Schließlich implementieren die startListening() Methode , um die Zuhörer zu befestigen:

    public void startListening() {
        if (mQuery != null && mRegistration == null) {
            mRegistration = mQuery.addSnapshotListener(this);
        }
    }

Jetzt ist die App vollständig konfiguriert, um Daten aus Firestore zu lesen. Führen Sie die App erneut und Sie sollten die Restaurants , die Sie hinzugefügt im vorherigen Schritt sehen:

9e45f40faefce5d0.png

Gehen Sie nun zurück zur Emulator-Benutzeroberfläche in Ihrem Browser und bearbeiten Sie einen der Restaurantnamen. Sie sollten sehen, dass es sich in der App fast sofort ändert!

Die App zeigt derzeit die bestbewerteten Restaurants der gesamten Sammlung an, aber in einer echten Restaurant-App möchte der Benutzer die Daten sortieren und filtern. Die App soll zum Beispiel "Top Seafood Restaurants in Philadelphia" oder "Least Teure Pizza" anzeigen können.

Wenn Sie oben in der App auf die weiße Leiste klicken, wird ein Filterdialogfeld geöffnet. In diesem Abschnitt verwenden wir Firestore-Abfragen, damit dieses Dialogfeld funktioniert:

67898572a35672a5.png

Lassen Sie uns bearbeiten die onFilter() Methode von MainActivity.java . Diese Methode akzeptiert einen Filters Objekt , das ein Hilfsobjekt ist , dass wir den Ausgang des Filter - Dialog zu erfassen erstellt. Wir ändern diese Methode, um eine Abfrage aus den Filtern zu erstellen:

    @Override
    public void onFilter(Filters filters) {
        // Construct query basic query
        Query query = mFirestore.collection("restaurants");

        // Category (equality filter)
        if (filters.hasCategory()) {
            query = query.whereEqualTo("category", filters.getCategory());
        }

        // City (equality filter)
        if (filters.hasCity()) {
            query = query.whereEqualTo("city", filters.getCity());
        }

        // Price (equality filter)
        if (filters.hasPrice()) {
            query = query.whereEqualTo("price", filters.getPrice());
        }

        // Sort by (orderBy with direction)
        if (filters.hasSortBy()) {
            query = query.orderBy(filters.getSortBy(), filters.getSortDirection());
        }

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

        // Update the query
        mQuery = query;
        mAdapter.setQuery(query);

        // Set header
        mCurrentSearchView.setText(Html.fromHtml(filters.getSearchDescription(this)));
        mCurrentSortByView.setText(filters.getOrderDescription(this));

        // Save filters
        mViewModel.setFilters(filters);
    }

Im Snippet oben bauen wir eine Query Objekt durch das Anbringen where und orderBy Klauseln die angegebenen Filter entsprechen.

Führen Sie die App erneut und wählen Sie die folgenden Filter , um die beliebtesten Niedrigpreis - Restaurants zu zeigen:

7a67a8a400c80c50.png

Sie sollten nun eine gefilterte Liste von Restaurants sehen, die nur Niedrigpreisoptionen enthält:

a670188398c3c59.png

Wenn Sie es bis hierher geschafft haben, haben Sie jetzt eine voll funktionsfähige App zum Anzeigen von Restaurantempfehlungen auf Firestore erstellt! Sie können jetzt Restaurants in Echtzeit sortieren und filtern. In den nächsten Abschnitten veröffentlichen wir Bewertungen und Sicherheit für die App.

In diesem Abschnitt fügen wir der App Bewertungen hinzu, damit Benutzer ihre Lieblingsrestaurants (oder am wenigsten Lieblingsrestaurants) bewerten können.

Sammlungen und Untersammlungen

Bisher haben wir alle Restaurantdaten in einer Top-Level-Sammlung namens "Restaurants" gespeichert. Wenn ein Benutzer Rate wollen ein Restaurant , das wir ein neues hinzufügen Rating - Objekt zu den Restaurants. Für diese Aufgabe verwenden wir eine Untersammlung. Sie können sich eine Untersammlung als eine Sammlung vorstellen, die an ein Dokument angehängt ist. Jedes Restaurantdokument hat also eine Bewertungsuntersammlung voller Bewertungsdokumente. Untersammlungen helfen dabei, Daten zu organisieren, ohne unsere Dokumente aufzublähen oder komplexe Abfragen zu erfordern.

Für den Zugriff auf eine Untersammlung, Call .collection() auf dem übergeordneten Dokument:

CollectionReference subRef = mFirestore.collection("restaurants")
        .document("abc123")
        .collection("ratings");

Sie können auf eine Untersammlung genau wie auf eine Sammlung auf oberster Ebene zugreifen und sie abfragen, es gibt keine Größenbeschränkungen oder Leistungsänderungen. Sie können mehr über die Firestor Datenmodell lesen Sie hier .

Schreiben von Daten in eine Transaktion

Ein Hinzufügen von Rating auf die richtige Untersammlung erfordert Aufruf nur .add() , aber wir haben auch die aktualisieren müssen Restaurant Objekts durchschnittliche Bewertung und die Anzahl der Bewertungen die neuen Daten zu reflektieren. Wenn wir für diese beiden Änderungen separate Vorgänge verwenden, gibt es eine Reihe von Racebedingungen, die zu veralteten oder falschen Daten führen können.

Um sicherzustellen, dass Bewertungen ordnungsgemäß hinzugefügt werden, verwenden wir eine Transaktion, um Bewertungen zu einem Restaurant hinzuzufügen. Diese Transaktion führt einige Aktionen aus:

  • Lesen Sie die aktuelle Bewertung des Restaurants und berechnen Sie die neue Bewertung
  • Bewertung zur Untersammlung hinzufügen
  • Aktualisieren Sie die durchschnittliche Bewertung des Restaurants und die Anzahl der Bewertungen

Öffnen RestaurantDetailActivity.java und implementieren die addRating Funktion:

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

        // In a transaction, add the new rating and update the aggregate totals
        return mFirestore.runTransaction(new Transaction.Function<Void>() {
            @Override
            public Void apply(Transaction transaction)
                    throws FirebaseFirestoreException {

                Restaurant restaurant = transaction.get(restaurantRef)
                        .toObject(Restaurant.class);

                // Compute new number of ratings
                int newNumRatings = restaurant.getNumRatings() + 1;

                // Compute new average rating
                double oldRatingTotal = restaurant.getAvgRating() *
                        restaurant.getNumRatings();
                double newAvgRating = (oldRatingTotal + rating.getRating()) /
                        newNumRatings;

                // Set new restaurant info
                restaurant.setNumRatings(newNumRatings);
                restaurant.setAvgRating(newAvgRating);

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

                return null;
            }
        });
    }

Die addRating() Funktion gibt eine Task die gesamte Transaktion darstellt. In der onRating() Funktion Hörer , um die Aufgabe zu reagieren auf das Ergebnis der Transaktion hinzugefügt werden .

Jetzt Starten Sie die App erneut und klicken Sie auf eines der Restaurants, die das Restaurant Detailbild bringen sollte. Klicken Sie auf die Schaltfläche + Hinzufügen einer Überprüfung zu starten. Fügen Sie eine Bewertung hinzu, indem Sie eine Reihe von Sternen auswählen und einen Text eingeben.

78fa16cdf8ef435a.png

Schlagen Absenden wird die Transaktion beginnen. Wenn die Transaktion abgeschlossen ist, sehen Sie unten Ihre Bewertung und eine Aktualisierung des Bewertungszählers des Restaurants:

f9e670f40bd615b0.png

Herzlichen Glückwunsch! Sie haben jetzt eine soziale, lokale, mobile Restaurantbewertungs-App, die auf Cloud Firestore basiert. Ich habe gehört, dass diese heutzutage sehr beliebt sind.

Bisher haben wir die Sicherheit dieser Anwendung nicht berücksichtigt. Woher wissen wir, dass Benutzer nur die richtigen eigenen Daten lesen und schreiben können? Firestor datbases werden durch eine Konfigurationsdatei gesichert genannt Sicherheitsregeln .

Öffnen Sie die firestore.rules Datei, sollten Sie sehen , wie folgt vor :

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;
    }
  }
}

Lassen Sie uns ändern diese Regeln verhindern , dass unerwünschte Daten acesss oder Änderungen, öffnen Sie die firestore.rules Datei und ersetzen Sie den Inhalt mit dem folgenden:

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;
      }
    }
  }
}

Diese Regeln schränken den Zugriff ein, um sicherzustellen, dass Clients nur sichere Änderungen vornehmen. Beispielsweise können Aktualisierungen eines Restaurantdokuments nur die Bewertungen ändern, nicht den Namen oder andere unveränderliche Daten. Bewertungen können nur erstellt werden, wenn die Benutzer-ID mit dem angemeldeten Benutzer übereinstimmt, wodurch Spoofing verhindert wird.

Um mehr über die Sicherheitsregeln zu lesen, besuchen Sie die Dokumentation .

Sie haben jetzt zusätzlich zu Firestore eine voll funktionsfähige App erstellt. Sie haben die wichtigsten Firestore-Funktionen kennengelernt, darunter:

  • Dokumente und Sammlungen
  • Daten lesen und schreiben
  • Sortieren und filtern mit Abfragen
  • Untersammlungen
  • Transaktionen

Erfahren Sie mehr

Um mehr über Firestore zu erfahren, sind hier einige gute Einstiegspunkte:

Die Restaurant-App in diesem Codelab basiert auf der Beispielanwendung „Friendly Eats“. Sie können den Quellcode für diese App sehen hier .

Optional: In der Produktion bereitstellen

Bisher hat diese App nur die Firebase Emulator Suite verwendet. Wenn Sie erfahren möchten, wie Sie diese App in einem echten Firebase-Projekt bereitstellen, fahren Sie mit dem nächsten Schritt fort.

Bisher war diese App vollständig lokal, alle Daten sind in der Firebase Emulator Suite enthalten. In diesem Abschnitt erfahren Sie, wie Sie Ihr Firebase-Projekt konfigurieren, damit diese App in der Produktion funktioniert.

Firebase-Authentifizierung

Im Firebase consle zu Abschnitt Authentifizierung und navigieren Sie gehen Sign-in Registerkarte Provider .

Aktivieren Sie die E-Mail-Anmeldemethode:

334ef7f6ff4da4ce.png

Feuerladen

Datenbank erstellen

Navigieren Sie zu dem Firestor Abschnitt der Konsole und klicken Sie auf Datenbank erstellen:

  1. Wenn Sie gefragt werden über Sicherheitsregeln wählen in Locked - Modus zu starten, werden wir bald diese Regeln aktualisieren.
  2. Wählen Sie den Datenbankspeicherort aus, den Sie für Ihre App verwenden möchten. Beachten Sie, dass die Auswahl einer Datenbank Lage ist eine dauerhafte Entscheidung und es ändern Sie ein neues Projekt anlegen. Für weitere Informationen über ein Projekt Standortwahl finden Sie in der Dokumentation .

Regeln bereitstellen

Um die zuvor geschriebenen Sicherheitsregeln bereitzustellen, führen Sie den folgenden Befehl im Codelab-Verzeichnis aus:

$ firebase deploy --only firestore:rules

Dadurch werden die Inhalte bereitstellen firestore.rules zu einem Projekt, das Sie durch die Navigation auf der Registerkarte Regeln in der Konsole bestätigen können.

Indizes bereitstellen

Die FriendlyEats-App verfügt über eine komplexe Sortierung und Filterung, die eine Reihe von benutzerdefinierten zusammengesetzten Indizes erfordert. Diese können mit der Hand in der Firebase Konsole erstellt werden , aber es ist einfacher, ihre Definitionen in der Schreib firestore.indexes.json Datei und stellen Sie sie die Firebase CLI verwenden.

Wenn Sie die öffnen firestore.indexes.json Datei sehen Sie , dass die erforderlichen Indizes bereits zur Verfügung gestellt worden:

{
  "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": []
}

Führen Sie den folgenden Befehl aus, um diese Indizes bereitzustellen:

$ firebase deploy --only firestore:indexes

Beachten Sie, dass die Indexerstellung nicht sofort erfolgt. Sie können den Fortschritt in der Firebase-Konsole überwachen.

Konfigurieren Sie die App

In der FirebaseUtil Klasse konfigurierten wir die Firebase SDK zu verbinden , um die Emulatoren , wenn im Debug - Modus:

public class FirebaseUtil {

    /** Use emulators only in debug builds **/
    private static final boolean sUseEmulators = BuildConfig.DEBUG;

    // ...
}

Wenn Sie Ihre App mit Ihrem echten Firebase-Projekt testen möchten, können Sie entweder:

  1. Erstellen Sie die App im Release-Modus und führen Sie sie auf einem Gerät aus.
  2. Vorübergehend ändern sUseEmulators zu false und die App erneut ausführen.

Beachten Sie, dass Sie aus der App müssen sich anmelden , und melden Sie sich erneut, um richtig zu Produktion zu verbinden.