Cloud Firestore Android Codelab

1. Обзор

Цели

В этой кодовой лаборатории вы создадите приложение для рекомендаций ресторанов на Android, поддерживаемое Cloud Firestore. Вы узнаете, как:

  • Чтение и запись данных в Firestore из приложения для Android
  • Слушайте изменения в данных Firestore в режиме реального времени
  • Используйте Firebase Authentication и правила безопасности для защиты данных Firestore
  • Написание сложных запросов Firestore

Предпосылки

Перед запуском этой кодлабы убедитесь, что у вас есть:

  • Android Studio 4.0 или выше
  • Эмулятор Android
  • Node.js версии 10 или выше
  • Java версия 8 или выше

2. Создайте проект Firebase

  1. Войдите в консоль Firebase с вашей учетной записью Google.
  2. В Firebase консоли выберите Добавить проект.
  3. Как показано на скриншоте ниже, введите имя для вашего проекта Firebase (например, «Дружественные Ест»), и нажмите кнопку Продолжить.

9d2f625aebcab6af.png

  1. Вас могут попросить включить Google Analytics, для целей этой кодовой лаборатории ваш выбор не имеет значения.
  2. Примерно через минуту ваш проект Firebase будет готов. Нажмите кнопку Продолжить.

3. Настройте пример проекта

Скачать код

Выполните следующую команду, чтобы клонировать пример кода для этой лаборатории кода. Это позволит создать папку с именем friendlyeats-android на вашей машине:

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

Если на вашем компьютере нет git, вы также можете скачать код напрямую с GitHub.

Импорт проекта в Android Studio. Вы, вероятно , увидите некоторые ошибки компиляции или , может быть предупреждение о пропавшем google-services.json файл. Мы исправим это в следующем разделе.

Добавить конфигурацию Firebase

  1. В Firebase консоли выберите Обзор проекта в левой навигационной панели. Нажмите кнопку для Android , чтобы выбрать платформу. При запросе на имя пакета использования com.google.firebase.example.fireeats

73d151ed16016421.png

  1. Выберите Зарегистрировать приложение и следуйте инструкциям , чтобы загрузить google-services.json файл и переместить его в app/ папку образца кода. Затем нажмите кнопку Далее.

4. Настройте эмуляторы Firebase

В этом codelab вы будете использовать Firebase Emulator Suite , локально эмулировать Cloud Firestore и другие Firebase услуги. Это обеспечивает безопасную, быструю и бесплатную локальную среду разработки для создания вашего приложения.

Установите интерфейс командной строки Firebase

Сначала вам нужно будет установить Firebase CLI . Самый простой способ сделать это состоит в использовании npm :

npm install -g firebase-tools

Если у вас нет npm или вы испытываете ошибку, прочитайте инструкции по установке , чтобы получить автономный исполняемый файл для вашей платформы.

После того, как вы установили CLI, работает firebase --version должна сообщить о версии 9.0.0 или выше:

$ firebase --version
9.0.0

Авторизоваться

Запуск firebase login для подключения консоли к вашей учетной записи Google. Это откроет новое окно браузера для завершения процесса входа в систему. Обязательно выберите ту же учетную запись, которую вы использовали при создании проекта Firebase ранее.

В рамках friendlyeats-android папку запуска firebase use --add для подключения локального проекта к вашему проекту Firebase. Следуйте инструкциям на экране , чтобы выбрать проект , который вы создали ранее , и если попросят выбрать псевдоним , введите значение по default .

5. Запустите приложение

Теперь пришло время запустить Firebase Emulator Suite и Android-приложение FriendlyEats в первый раз.

Запустите эмуляторы

В вашем терминале внутри 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. Этот файл содержит логику для подключения Firebase SDK к локальным эмуляторам, работающим на вашем компьютере.

В верхней части файла проверьте эту строку:

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

Мы используем BuildConfig , чтобы убедиться , что мы подключаем только эмуляторы , когда наше приложение работает в debug режиме. Когда мы скомпилировать приложение в release режиме это условие будет ложным.

Теперь посмотрим на 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) - useEmulator(host, port) метод для подключения Firebase SDK для локального эмулятора Firestore. На протяжении всего приложения мы будем использовать FirebaseUtil.getFirestore() , чтобы получить доступ к этому экземпляру FirebaseFirestore поэтому мы уверены , что мы всегда подключения к эмулятору Firestore при работе в debug режиме.

Запустите приложение

Если вы добавили google-services.json файл должным образом, проект должен компилировать. В Android студии выберите Построить> Rebuild Project и убедитесь , что не осталось ошибок.

В Android Studio Запустите приложение на Android эмулятора. Сначала вам будет представлен экран «Войти». Вы можете использовать любой адрес электронной почты и пароль для входа в приложение. Этот процесс входа подключается к эмулятору аутентификации Firebase, поэтому настоящие учетные данные не передаются.

Теперь откройте эмуляторы интерфейс, перейдя к HTTP: // локальный: 4000 в веб - браузере. Затем щелкните на вкладке Проверка подлинности и вы должны увидеть только что созданную учетную запись:

Эмулятор аутентификации Firebase

После того, как вы завершили процесс входа в систему, вы должны увидеть главный экран приложения:

de06424023ffb4b9.png

Вскоре мы добавим некоторые данные для заполнения главного экрана.

6. Запишите данные в Firestore

В этом разделе мы запишем некоторые данные в Firestore, чтобы мы могли заполнить пустой домашний экран.

Основная модель объект в нашем приложении есть ресторан (см model/Restaurant.java ). Данные Firestore разделены на документы, коллекции и подколлекции. Мы будем хранить каждый ресторан в качестве документа в коллекции верхнего уровня под названием "restaurants" . Чтобы узнать больше о модели данных Firestore, читать о документах и коллекциях в документации .

В демонстрационных целях мы добавим в приложение функцию создания десяти случайных ресторанов при нажатии кнопки «Добавить случайные элементы» в дополнительном меню. Откройте файл 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, так что нам не нужно указать уникальный идентификатор для каждого ресторана.

Теперь снова запустите приложение и нажмите кнопку «Добавить случайные элементы» в дополнительном меню, чтобы вызвать код, который вы только что написали:

95691e9b71ba55e3.png

Теперь откройте эмуляторы интерфейс, перейдя к HTTP: // локальный: 4000 в веб - браузере. Затем нажмите на вкладку Firestore и вы должны увидеть данные , которые вы только что добавили:

Эмулятор аутентификации Firebase

Эти данные на 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();
    }

  // ...
}

На начальной загрузки слушатель получит один ADDED событие для каждого нового документа. Поскольку результирующий набор запроса изменяется с течением времени, прослушиватель будет получать больше событий, содержащих изменения. Теперь давайте закончим реализацию слушателя. Сначала нужно добавить три новых метода: onDocumentAdded , onDocumentModified , и на 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

Теперь вернитесь в пользовательский интерфейс эмулятора в браузере и отредактируйте одно из названий ресторанов. Вы должны увидеть, как это изменится в приложении почти мгновенно!

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 объект, прикрепив where это и orderBy положения , чтобы соответствовать заданным фильтрам.

Запустите приложение еще раз и выберите следующий фильтр , чтобы показать самые популярные рестораны по низким ценам:

7a67a8a400c80c50.png

Теперь вы должны увидеть отфильтрованный список ресторанов, содержащий только варианты с низкими ценами:

а670188398c3c59.png

Если вы дошли до этого места, то теперь у вас есть полнофункциональное приложение для просмотра рекомендаций ресторанов в Firestore! Теперь вы можете сортировать и фильтровать рестораны в режиме реального времени. В следующих нескольких разделах мы публикуем обзоры и информацию о безопасности приложения.

9. Организуйте данные в подколлекциях

В этом разделе мы добавим рейтинги в приложение, чтобы пользователи могли оставить отзыв о своих любимых (или наименее любимых) ресторанах.

Коллекции и подколлекции

До сих пор мы хранили все данные о ресторанах в коллекции верхнего уровня под названием «рестораны». Когда ставки пользователей ресторана мы хотим , чтобы добавить новый Rating объект в рестораны. Для этой задачи мы будем использовать подколлекцию. Вы можете думать о подколлекции как о коллекции, прикрепленной к документу. Таким образом, каждый документ ресторана будет иметь подколлекцию рейтингов, полную рейтинговых документов. Подколлекции помогают организовать данные, не раздувая наши документы и не требуя сложных запросов.

Чтобы получить доступ к подколлекции, вызов .collection() для родительского документа:

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

Вы можете получить доступ к подколлекции и запросить ее так же, как и к коллекции верхнего уровня, ограничений по размеру или производительности нет. Вы можете прочитать больше о модели данных Firestore здесь .

Запись данных в транзакции

Добавление Rating для правильной подколлекции требует только вызов .add() , но мы также должны обновить Restaurant среднего рейтинга и количество оценок объекта , чтобы отразить новые данные. Если мы используем отдельные операции для внесения этих двух изменений, существует ряд условий гонки, которые могут привести к устаревшим или неверным данным.

Чтобы обеспечить правильное добавление оценок, мы будем использовать транзакцию для добавления оценок ресторану. Эта транзакция выполнит несколько действий:

  • Прочитать текущий рейтинг ресторана и рассчитать новый
  • Добавить рейтинг в подколлекцию
  • Обновить средний рейтинг ресторана и количество оценок

Открыть 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

Поздравляю! Теперь у вас есть социальное локальное мобильное приложение для обзора ресторанов, созданное на базе Cloud Firestore. Я слышал, что они очень популярны в наши дни.

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

Эти правила ограничивают доступ, чтобы клиенты могли вносить только безопасные изменения. Например, обновления документа ресторана могут изменить только рейтинги, но не название или любые другие неизменяемые данные. Рейтинги могут быть созданы только в том случае, если идентификатор пользователя совпадает с вошедшим в систему пользователем, что предотвращает спуфинг.

Чтобы узнать больше о правилах безопасности, посетите документацию .

11. Заключение

Теперь вы создали полнофункциональное приложение поверх Firestore. Вы узнали о наиболее важных функциях Firestore, включая:

  • Документы и коллекции
  • Чтение и запись данных
  • Сортировка и фильтрация с помощью запросов
  • Подколлекции
  • Транзакции

Выучить больше

Чтобы продолжить изучение Firestore, вот несколько хороших мест для начала:

Приложение ресторана в этой кодовой лаборатории было основано на примере приложения «Friendly Eats». Вы можете просмотреть исходный код для этого приложения здесь .

Необязательно: развертывание в рабочей среде

До сих пор это приложение использовало только Firebase Emulator Suite. Если вы хотите узнать, как развернуть это приложение в реальном проекте Firebase, перейдите к следующему шагу.

12. (Необязательно) Разверните приложение

До сих пор это приложение было полностью локальным, все данные содержались в Firebase Emulator Suite. В этом разделе вы узнаете, как настроить проект 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 и запустить приложение снова.

Обратите внимание , что вам может понадобиться выйти из приложения и снова войти в систему для того , чтобы правильно подключить к производству.