Cloud Firestore Android Codelab

1。概要

目標

このコードラボでは、CloudFirestoreを利用したAndroidでレストランおすすめアプリを作成します。次の方法を学習します:

  • AndroidアプリからFirestoreへのデータの読み取りと書き込み
  • Firestoreデータの変更をリアルタイムで聞く
  • Firebase認証とセキュリティルールを使用してFirestoreデータを保護する
  • 複雑なFirestoreクエリを作成する

前提条件

このコードラボを開始する前に、次のことを確認してください。

  • Androidのメーカー4.0以上
  • Androidエミュレーター
  • Node.jsのバージョン10以降
  • Javaのバージョン8以上

2.Firebaseプロジェクトを作成します

  1. サインインしFirebaseコンソールGoogleアカウントで。
  2. Firebaseコンソール追加のプロジェクトをクリックします。
  3. 以下のスクリーンキャプチャに示すように、(例えば、「フレンドリー食べる」)、あなたのFirebaseプロジェクトの名前を入力し、[続行]クリックします。

9d2f625aebcab6af.png

  1. このコードラボの目的上、選択は重要ではないため、GoogleAnalyticsを有効にするように求められる場合があります。
  2. 1分ほどすると、Firebaseプロジェクトの準備が整います。 [続行]クリックします。

3.サンプルプロジェクトを設定します

コードをダウンロードする

次のコマンドを実行して、このコードラボのサンプルコードのクローンを作成します。これは、という名前のフォルダが作成されますfriendlyeats-androidあなたのマシン上で:

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

マシンにgitがない場合は、GitHubから直接コードをダウンロードすることもできます。

アンドロイドStudioにプロジェクトをインポートします。おそらく、おそらくいくつかのコンパイルエラーまたは欠落に関する警告が表示されますgoogle-services.jsonファイルを。これは次のセクションで修正します。

Firebase構成を追加する

  1. Firebaseコンソール、左パネルのプロジェクトの概要を選択します。プラットフォームを選択するには、Androidのボタンをクリックしてください。パッケージ名利用の入力を求められたらcom.google.firebase.example.fireeats

73d151ed16016421.png

  1. 登録アプリケーション]クリックし、ダウンロードするための指示に従ってくださいgoogle-services.jsonファイルを、とに移動app/サンプルコードのフォルダ。次に、[次へ]クリックします。

4.Firebaseエミュレータを設定します

このコードラボでは、使用しますFirebaseエミュレータスイートをローカルクラウドFirestoreやその他のFirebaseサービスをエミュレートします。これにより、アプリを構築するための安全で高速な無料のローカル開発環境が提供されます。

FirebaseCLIをインストールします

まず、インストールする必要がありますFirebase CLIを。これを行う最も簡単な方法は、使用することですnpm

npm install -g firebase-tools

あなたが持っていない場合はnpmしたり、エラーが発生する、読んでインストール手順をご使用のプラットフォーム用のスタンドアロンのバイナリを取得します。

CLIをインストールしたら、実行しているfirebase --versionのバージョン報告しなければならない9.0.0以上を:

$ firebase --version
9.0.0

ログインする

実行にはfirebase login GoogleアカウントにCLIを接続します。これにより、新しいブラウザウィンドウが開き、ログインプロセスが完了します。以前にFirebaseプロジェクトを作成したときに使用したものと同じアカウントを選択してください。

内からfriendlyeats-androidフォルダに実行firebase use --addあなたFirebaseプロジェクトにローカルのプロジェクトを接続します。以前に作成したプロジェクトを選択するプロンプトに従い、エイリアス入力を選択するように求めであればdefault

5.アプリを実行します

次に、Firebase EmulatorSuiteとFriendlyEatsAndroidアプリを初めて実行します。

エミュレーターを実行します

内からあなたの端末ではfriendlyeats-androidディレクトリの実行firebase emulators:start Firebaseエミュレータを起動します。次のようなログが表示されます。

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

これで、マシン上で実行されている完全なローカル開発環境ができました。コードラボの残りの部分では、このコマンドを実行したままにしてください。Androidアプリはエミュレーターに接続する必要があります。

アプリをエミュレーターに接続します

ファイルの開きFirebaseUtil.javaのAndroid Studioでの。このファイルには、FirebaseSDKをマシンで実行されているローカルエミュレーターに接続するためのロジックが含まれています。

ファイルの先頭で、次の行を調べます。

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

私たちは、使用しているBuildConfig我々のアプリが実行されているときに我々は唯一のエミュレータへの接続を確認するためにdebugモード。私たちがアプリをコンパイルするとreleaseモードこの条件はfalseになります。

今見てみましょうgetFirestore()メソッド:

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

私たちは、使用されていることがわかりますuseEmulator(host, port)ローカルFirestoreエミュレータにFirebase SDKを接続する方法を。アプリを通じて、我々は、使用するFirebaseUtil.getFirestore()のこのインスタンスにアクセスするためにFirebaseFirestore我々は常にFirestoreに接続するエミュレータで実行しているときにしていることを確認しているので、 debugモード。

アプリを実行する

あなたが追加した場合はgoogle-services.jsonファイルを正しく、プロジェクトはコンパイルする必要があります。アンドロイドStudioで[ビルド]クリックします>プロジェクトを再構築し、残っエラーがないことを確認してください。

アンドロイドStudioで実行して、Androidのエミュレータのアプリ。最初に、「サインイン」画面が表示されます。任意のメールアドレスとパスワードを使用してアプリにサインインできます。このサインインプロセスはFirebaseAuthenticationエミュレータに接続しているため、実際の認証情報は送信されていません。

今すぐに移動してエミュレータUIを開きます。http:// localhostを:4000 、Webブラウザで。次に、[認証]タブをクリックすると、あなたが作成したアカウントが表示されます。

Firebase Auth Emulator

サインインプロセスが完了すると、アプリのホーム画面が表示されます。

de06424023ffb4b9.png

すぐに、ホーム画面に入力するデータを追加します。

6.Firestoreにデータを書き込む

このセクションでは、現在空のホーム画面にデータを入力できるように、Firestoreにデータを書き込みます。

我々のアプリの主なモデルオブジェクトには、レストラン(参照であるmodel/Restaurant.java )。 Firestoreデータは、ドキュメント、コレクション、およびサブコレクションに分割されます。我々はと呼ばれるトップレベルのコレクション内の文書としての各レストランを格納する"restaurants" 。 Firestoreデータモデルの詳細については、内のドキュメントやコレクションについて読んドキュメント

デモンストレーションの目的で、オーバーフローメニューの[ランダムアイテムの追加]ボタンをクリックすると、アプリに10個のランダムレストランを作成する機能を追加します。ファイルを開きMainActivity.javaでフィルをonAddItemsClicked()メソッド:

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

上記のコードについて注意すべき重要な点がいくつかあります。

  • 私たちは、への参照取得することによって開始した"restaurants"のコレクションを。コレクションはドキュメントが追加されるときに暗黙的に作成されるため、データを書き込む前にコレクションを作成する必要はありませんでした。
  • ドキュメントは、各レストランドキュメントの作成に使用するPOJOを使用して作成できます。
  • add()私たちは、各レストランの一意のIDを指定する必要はありませんでしたので、この方法では、自動生成されたIDを持つコレクションにドキュメントを追加します。

次に、アプリを再度実行し、オーバーフローメニューの[ランダムアイテムの追加]ボタンをクリックして、作成したコードを呼び出します。

95691e9b71ba55e3.png

今すぐに移動してエミュレータUIを開きます。http:// localhostを:4000 、Webブラウザで。そして、Firestore]タブをクリックし、あなたが追加したデータが表示されます。

Firebase Auth Emulator

このデータは、マシンに対して100%ローカルです。実際、実際のプロジェクトにはまだFirestoreデータベースが含まれていません。これは、結果なしにこのデータを変更および削除して実験しても安全であることを意味します。

おめでとうございます。Firestoreにデータを書き込んだだけです。次のステップでは、このデータをアプリに表示する方法を学習します。

7.Firestoreからのデータを表示します

このステップでは、Firestoreからデータを取得してアプリに表示する方法を学習します。 Firestoreからデータを読み込むための最初のステップは、作成することですQuery 。変更onCreate()メソッドを:

        mFirestore = FirebaseUtil.getFirestore();

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

次に、クエリをリッスンして、一致するすべてのドキュメントを取得し、将来の更新がリアルタイムで通知されるようにします。私たちの最終的な目標は、このデータをバインドすることであるためRecyclerView 、我々は作成する必要がありRecyclerView.Adapterデータを聞くためにクラスを。

開きFirestoreAdapter一部がすでに実装されているクラスを、。まずは、アダプタが実装してみましょうEventListenerし、定義onEventそれがFirestoreクエリへの更新を受け取ることができるように機能を:

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

  // ...
}

初期ロード上のリスナーは1つの受け取ることになりますADDEDそれぞれの新しいドキュメントのイベントを。クエリの結果セットが時間の経過とともに変化すると、リスナーはその変化を含むより多くのイベントを受け取ります。それでは、リスナーの実装を終了しましょう。最初の3つの新しいメソッドを追加: onDocumentAddedonDocumentModified 、とに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());
    }

その後から、これらの新しいメソッドを呼び出す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();
    }

最後に実装startListening()リスナーをアタッチする方法を:

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

これで、アプリはFirestoreからデータを読み取るように完全に構​​成されました。再びアプリを実行すると、あなたは、前の手順で追加したレストランが表示されます。

9e45f40faefce5d0.png

次に、ブラウザーのエミュレーターUIに戻り、レストラン名の1つを編集します。あなたはそれがアプリでほぼ瞬時に変化するのを見るはずです!

8.データの並べ替えとフィルタリング

このアプリは現在、コレクション全体でトップクラスのレストランを表示していますが、実際のレストランアプリでは、ユーザーはデータを並べ替えてフィルタリングする必要があります。たとえば、アプリは「フィラデルフィアのトップシーフードレストラン」または「最も安価なピザ」を表示できる必要があります。

アプリの上部にある白いバーをクリックすると、フィルターダイアログが表示されます。このセクションでは、Firestoreクエリを使用してこのダイアログを機能させます。

67898572a35672a5.png

レッツ・編集onFilter()のメソッドMainActivity.java 。この方法では、受け入れFilters我々は、フィルタダイアログの出力をキャプチャするために作成したヘルパーオブジェクトであるオブジェクトを。このメソッドを変更して、フィルターからクエリを作成します。

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

上記のスニペットでは、構築するQuery取り付けることによって、オブジェクトをwhereorderBy与えられたフィルタに一致するように句を。

再びアプリを実行し、最も人気のある低価格のレストランを表示するには、次のフィルタを選択します。

7a67a8a400c80c50.png

これで、低価格のオプションのみを含むレストランのフィルタリングされたリストが表示されます。

a670188398c3c59.png

ここまで進んだら、Firestoreで完全に機能するレストランおすすめの表示アプリを作成しました。レストランをリアルタイムで並べ替えてフィルタリングできるようになりました。次のいくつかのセクションでは、レビューとセキュリティをアプリに投稿します。

9.データをサブコレクションに整理する

このセクションでは、アプリに評価を追加して、ユーザーがお気に入りの(または最も嫌いな)レストランを確認できるようにします。

コレクションとサブコレクション

これまで、すべてのレストランデータを「レストラン」と呼ばれるトップレベルのコレクションに保存してきました。ユーザー率は、レストラン、我々は新しい追加したい場合はRatingレストランにオブジェクトを。このタスクでは、サブコレクションを使用します。サブコレクションは、ドキュメントに添付されているコレクションと考えることができます。したがって、各レストランドキュメントには、評価ドキュメントでいっぱいの評価サブコレクションがあります。サブコレクションは、ドキュメントを肥大化させたり、複雑なクエリを必要とせずにデータを整理するのに役立ちます。

サブコレクションにアクセスするには、コール.collection()親文書に:

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

トップレベルのコレクションと同じようにサブコレクションにアクセスしてクエリを実行できます。サイズの制限やパフォーマンスの変更はありません。あなたはFirestoreデータモデルの詳細を読むことができるここに

トランザクションでのデータの書き込み

追加Ratingの適切なサブコレクションにのみ呼び出す必要です.add() 、私たちも更新する必要があるRestaurant新しいデータを反映するために、オブジェクトの平均格付けおよび評価の数を。別々の操作を使用してこれら2つの変更を行うと、データが古くなったり不正確になったりする可能性のある競合状態がいくつかあります。

評価が適切に追加されるようにするために、トランザクションを使用してレストランに評価を追加します。このトランザクションは、いくつかのアクションを実行します。

  • レストランの現在の評価を読み、新しい評価を計算します
  • サブコレクションに評価を追加します
  • レストランの平均評価と評価数を更新します

オープンRestaurantDetailActivity.javaと実装addRating機能を:

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

addRating()関数が返すTaskトランザクション全体を表しています。でonRating()関数リスナーがトランザクションの結果に対応するためにタスクに追加されます。

ここでもう一度アプリを実行し、レストランの詳細画面を立ち上げなければならないレストラン、のいずれかをクリックしてください。レビューを追加を開始する+ボタンをクリックします。星の数を選び、テキストを入力してレビューを追加します。

78fa16cdf8ef435a.png

トランザクションをキックオフしますSubmitを叩きます。トランザクションが完了すると、レビューが下に表示され、レストランのレビュー数が更新されます。

f9e670f40bd615b0.png

おめでとうございます!これで、CloudFirestore上に構築されたソーシャルのローカルモバイルレストランレビューアプリができました。最近はとても人気があるそうです。

10.データを保護します

これまでのところ、このアプリケーションのセキュリティについては考慮していません。ユーザーが正しい自分のデータしか読み書きできないことをどうやって知ることができますか? Firestore datbasesは、という設定ファイルで固定されているセキュリティルール

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

レッツ・チェンジこれらのルールは、不要なデータacesssまたは変更を防ぐ開くために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;
      }
    }
  }
}

これらのルールは、クライアントが安全な変更のみを行うことを保証するためにアクセスを制限します。たとえば、レストランのドキュメントを更新すると、評価のみが変更され、名前やその他の不変のデータは変更されません。レーティングは、ユーザーIDがサインインしたユーザーと一致する場合にのみ作成できます。これにより、なりすましが防止されます。

セキュリティルールの詳細を読むには、訪問のドキュメントを

11.結論

これで、Firestore上にフル機能のアプリが作成されました。次のような最も重要なFirestoreの機能について学びました。

  • ドキュメントとコレクション
  • データの読み取りと書き込み
  • クエリによる並べ替えとフィルタリング
  • サブコレクション
  • トランザクション

もっと詳しく知る

Firestoreについて学び続けるために、ここに始めるためのいくつかの良い場所があります:

このコードラボのレストランアプリは、「FriendlyEats」サンプルアプリケーションに基づいています。あなたはそのアプリのソースコードを閲覧することができ、ここで

オプション:本番環境にデプロイする

これまでのところ、このアプリはFirebase EmulatorSuiteのみを使用しています。このアプリを実際のFirebaseプロジェクトにデプロイする方法を学びたい場合は、次のステップに進んでください。

12.(オプション)アプリをデプロイします

これまでのところ、このアプリは完全にローカルであり、すべてのデータはFirebase EmulatorSuiteに含まれています。このセクションでは、このアプリが本番環境で機能するようにFirebaseプロジェクトを構成する方法を学習します。

Firebase認証

Firebase consleでの認証]セクションとナビゲートするために行くサインインプロバイダーでのタブ

電子メールのサインイン方法を有効にします。

334ef7f6ff4da4ce.png

ファイヤーストア

データベースを作成する

コンソールのFirestoreセクションに移動し、データベースを作成]をクリックします:

  1. セキュリティルールはロックモードで起動するかを選択についてプロンプトが表示されたら、私たちはすぐそれらのルールを更新します。
  2. アプリに使用するデータベースの場所を選択します。選択データベースの場所は、あなたが新しいプロジェクトを作成する必要があります恒久的な意思決定とそれを変更することであることに注意してください。プロジェクトの場所の選択の詳細については、マニュアルを参照して

ルールの展開

以前に作成したセキュリティルールを展開するには、codelabディレクトリで次のコマンドを実行します。

$ firebase deploy --only firestore:rules

これは、内容展開するfirestore.rules使用すると、コンソールの[ルール]タブに移動して確認することができ、あなたのプロジェクトに。

インデックスをデプロイする

FriendlyEatsアプリには複雑な並べ替えとフィルタリングがあり、多数のカスタム複合インデックスが必要です。これらはFirebaseコンソールで手動で作成することができますが、中にそれらの定義を記述することが簡単ですfirestore.indexes.jsonファイルとFirebase CLIを使用してそれらを展開します。

あなたが開いている場合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

インデックスの作成は瞬時ではないことに注意してください。Firebaseコンソールで進行状況を監視できます。

アプリを構成する

FirebaseUtilクラス我々は、デバッグモードでエミュレータに接続するためにFirebase SDKを設定しました。

public class FirebaseUtil {

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

    // ...
}

実際のFirebaseプロジェクトでアプリをテストする場合は、次のいずれかを実行できます。

  1. リリースモードでアプリをビルドし、デバイスで実行します。
  2. 一時的に変更sUseEmulatorsするfalseして、再度アプリを実行します。

あなたがアプリからログアウトし、適切に生産に接続するために再度サインインする必要があるかもしれないことに注意してください。