1. Обзор
Цели
В этой лабораторной работе вы создадите приложение для рекомендации ресторанов на Android с поддержкой Cloud Firestore. Вы узнаете, как:
- Чтение и запись данных в Firestore из приложения Android
- Отслеживайте изменения данных Firestore в режиме реального времени
- Используйте аутентификацию Firebase и правила безопасности для защиты данных Firestore
- Написание сложных запросов Firestore
Предпосылки
Перед началом работы над этой лабораторной работой убедитесь, что у вас есть:
- Android Studio Flamingo или новее
- Эмулятор Android с API 19 или выше
- Node.js версии 16 или выше
- Java версии 17 или выше
2. Создайте проект Firebase
- Войдите в консоль Firebase, используя свою учетную запись Google.
- Нажмите кнопку, чтобы создать новый проект, а затем введите название проекта (например,
FriendlyEats
). - Нажмите «Продолжить» .
- При появлении соответствующего запроса ознакомьтесь с условиями Firebase и примите их, а затем нажмите кнопку «Продолжить» .
- (Необязательно) Включите помощь ИИ в консоли Firebase (так называемая «Gemini в Firebase»).
- Для этой лабораторной работы вам не понадобится Google Analytics, поэтому отключите опцию Google Analytics.
- Нажмите «Создать проект» , дождитесь завершения подготовки проекта, а затем нажмите «Продолжить» .
3. Настройте пример проекта
Загрузить код
Выполните следующую команду, чтобы клонировать пример кода для этой лабораторной работы. Это создаст на вашем компьютере папку friendlyeats-android
:
$ git clone https://github.com/firebase/friendlyeats-android
Если на вашем компьютере нет git, вы также можете загрузить код непосредственно с GitHub.
Добавить конфигурацию Firebase
- В консоли Firebase выберите «Обзор проекта» в левой панели навигации. Нажмите кнопку Android , чтобы выбрать платформу. При запросе имени пакета используйте
com.google.firebase.example.fireeats
- Нажмите «Зарегистрировать приложение» и следуйте инструкциям, чтобы загрузить файл
google-services.json
и переместить его в папкуapp/
только что загруженного вами кода. Затем нажмите «Далее» .
Импортировать проект
Откройте Android Studio. Нажмите «Файл» > «Создать» > «Импортировать проект» и выберите папку friendlyeats-android .
4. Настройте эмуляторы Firebase
В этой лабораторной работе вы будете использовать Firebase Emulator Suite для локальной эмуляции Cloud Firestore и других сервисов Firebase. Это обеспечит безопасную, быструю и бесплатную локальную среду разработки для создания вашего приложения.
Установите Firebase CLI
Сначала вам необходимо установить Firebase CLI . Если вы используете macOS или Linux, выполните следующую команду cURL:
curl -sL https://firebase.tools | bash
Если вы используете Windows, прочтите инструкции по установке , чтобы получить автономный двоичный файл или установить его через npm
.
После установки CLI запуск firebase --version
должен выдать версию 9.0.0
или выше:
$ firebase --version 9.0.0
Авторизоваться
Выполните firebase login
, чтобы подключить CLI к вашей учётной записи Google. Откроется новое окно браузера для завершения процесса входа. Убедитесь, что вы выбрали ту же учётную запись, которую использовали при создании проекта Firebase ранее.
Свяжите свой проект
В папке friendlyeats-android
выполните firebase use --add
, чтобы подключить локальный проект к проекту Firebase. Следуя подсказкам, выберите созданный ранее проект и, если потребуется выбрать псевдоним, введите default
.
5. Запустите приложение.
Теперь пришло время впервые запустить Firebase Emulator Suite и приложение FriendlyEats для Android.
Запустите эмуляторы
В терминале из каталога 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-приложению потребуется подключиться к эмуляторам.
Подключите приложение к эмуляторам
Откройте файлы util/FirestoreInitializer.kt
и util/AuthInitializer.kt
в Android Studio. Эти файлы содержат логику подключения Firebase SDK к локальным эмуляторам, работающим на вашем компьютере, при запуске приложения.
В методе create()
класса FirestoreInitializer
изучите следующий фрагмент кода:
// Use emulators only in debug builds
if (BuildConfig.DEBUG) {
firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
}
Мы используем BuildConfig
, чтобы гарантировать подключение к эмуляторам только тогда, когда приложение работает в режиме debug
. При компиляции приложения в режиме release
это условие будет ложным.
Мы видим, что он использует метод useEmulator(host, port)
для подключения Firebase SDK к локальному эмулятору Firestore. В ходе работы приложения мы будем использовать FirebaseUtil.getFirestore()
для доступа к этому экземпляру FirebaseFirestore
, чтобы гарантировать постоянное подключение к эмулятору Firestore при работе в режиме debug
.
Запустите приложение
Если вы правильно добавили файл google-services.json
, проект должен скомпилироваться. В Android Studio нажмите «Сборка» > «Пересобрать проект» и убедитесь, что ошибок больше нет.
Запустите приложение в Android Studio на эмуляторе Android. Сначала появится экран «Вход». Для входа в приложение можно использовать любой адрес электронной почты и пароль. Этот процесс входа подключается к эмулятору аутентификации Firebase, поэтому реальные учётные данные не передаются.
Теперь откройте интерфейс эмулятора, перейдя по адресу http://localhost:4000 в веб-браузере. Затем перейдите на вкладку «Аутентификация» , и вы увидите только что созданную учётную запись:
После завершения процесса входа вы увидите главный экран приложения:
Скоро мы добавим некоторые данные для заполнения главного экрана.
6. Записать данные в Firestore
В этом разделе мы запишем некоторые данные в Firestore, чтобы иметь возможность заполнить в данный момент пустой домашний экран.
Основным объектом модели в нашем приложении является ресторан (см. model/Restaurant.kt
). Данные Firestore разделены на документы, коллекции и подколлекции. Мы будем хранить каждый ресторан как документ в коллекции верхнего уровня под названием "restaurants"
. Чтобы узнать больше о модели данных Firestore, ознакомьтесь с документацией о документах и коллекциях.
Для демонстрации мы добавим в приложение функцию создания десяти случайных ресторанов при нажатии кнопки «Добавить случайные пункты» в дополнительном меню. Откройте файл 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"
. Коллекции создаются автоматически при добавлении документов, поэтому не было необходимости создавать коллекцию перед записью данных. - Документы можно создавать с использованием классов данных Kotlin, которые мы используем для создания каждого документа Restaurant.
- Метод
add()
добавляет документ в коллекцию с автоматически сгенерированным идентификатором, поэтому нам не нужно было указывать уникальный идентификатор для каждого ресторана.
Теперь снова запустите приложение и нажмите кнопку «Добавить случайные элементы» в дополнительном меню (в правом верхнем углу), чтобы вызвать код, который вы только что написали:
Теперь откройте интерфейс эмулятора, перейдя по адресу http://localhost:4000 в веб-браузере. Затем перейдите на вкладку Firestore , и вы увидите только что добавленные данные:
Эти данные полностью локальны для вашего компьютера. Более того, ваш реальный проект пока даже не содержит базу данных Firestore! Это означает, что вы можете безопасно экспериментировать с изменением и удалением этих данных без каких-либо последствий.
Поздравляем, вы только что записали данные в Firestore! На следующем шаге мы узнаем, как отобразить эти данные в приложении.
7. Отображение данных из Firestore
На этом этапе мы научимся извлекать данные из Firestore и отображать их в нашем приложении. Первым шагом для чтения данных из Firestore является создание 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
, чтобы он мог получать обновления запроса Firestore:
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)
}
}
Теперь приложение полностью настроено для чтения данных из Firestore. Запустите приложение ещё раз, и вы увидите рестораны, добавленные на предыдущем шаге:
Теперь вернитесь в интерфейс эмулятора в браузере и отредактируйте название одного из ресторанов. Вы увидите, как оно изменится в приложении практически мгновенно!
8. Сортировка и фильтрация данных
В настоящее время приложение отображает самые рейтинговые рестораны из всей коллекции, но в реальном приложении для ресторанов пользователю потребуется сортировать и фильтровать данные. Например, приложение должно отображать «Лучшие рестораны морепродуктов в Филадельфии» или «Самую дешёвую пиццу».
Нажатие на белую панель в верхней части приложения открывает диалоговое окно фильтров. В этом разделе мы воспользуемся запросами Firestore для реализации этого диалогового окна:
Давайте отредактируем метод onFilter()
в MainFragment.kt
. Этот метод принимает объект 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
}
В приведенном выше фрагменте мы создаем объект Query
, присоединяя предложения where
и orderBy
для соответствия заданным фильтрам.
Запустите приложение еще раз и выберите следующий фильтр, чтобы отобразить самые популярные недорогие рестораны:
Теперь вы должны увидеть отфильтрованный список ресторанов, содержащий только недорогие варианты:
Если вы дошли до этого момента, значит, вы создали полноценное приложение для просмотра рекомендаций ресторанов на Firestore! Теперь вы можете сортировать и фильтровать рестораны в режиме реального времени. В следующих разделах мы добавим отзывы к ресторанам и правила безопасности в приложение.
9. Организуйте данные в подколлекциях
В этом разделе мы добавим в приложение рейтинги, чтобы пользователи могли оставлять отзывы о своих любимых (или нелюбимых) ресторанах.
Коллекции и подколлекции
До сих пор мы хранили все данные о ресторанах в коллекции верхнего уровня под названием «Рестораны». Когда пользователь оценивает ресторан, мы хотим добавить новый объект Rating
к ресторанам. Для этой задачи мы будем использовать подколлекцию. Подколлекцию можно представить как коллекцию, прикреплённую к документу. Таким образом, каждый документ о ресторане будет иметь подколлекцию рейтингов, полную документов с рейтингами. Подколлекции помогают организовать данные, не перегружая документы и не требуя сложных запросов.
Чтобы получить доступ к подколлекции, вызовите .collection()
в родительском документе:
val subRef = firestore.collection("restaurants")
.document("abc123")
.collection("ratings")
Вы можете получать доступ к подколлекции и выполнять запросы к ней так же, как и к коллекции верхнего уровня, без ограничений по размеру и без снижения производительности. Подробнее о модели данных Firestore можно узнать здесь .
Запись данных в транзакции
Для добавления 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()
к задаче добавляются слушатели, которые реагируют на результат транзакции.
Теперь снова запустите приложение и нажмите на один из ресторанов, чтобы открыть экран с информацией о нём. Нажмите кнопку «+» , чтобы начать добавлять отзыв. Чтобы добавить отзыв, выберите количество звёзд и введите текст.
Нажатие кнопки «Отправить» запустит транзакцию. После завершения транзакции вы увидите свой отзыв ниже и обновлённое количество отзывов о ресторане:
Поздравляю! Теперь у вас есть социальное, локальное и мобильное приложение для отзывов о ресторанах, созданное на Cloud Firestore. Говорят, сейчас такие приложения очень популярны.
10. Защитите свои данные
До сих пор мы не рассматривали безопасность этого приложения. Откуда нам знать, что пользователи могут читать и записывать только свои собственные данные? Базы данных Firestore защищены конфигурационным файлом Security Rules .
Откройте файл 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 перейдите в раздел «Аутентификация» и нажмите « Начать» . Перейдите на вкладку «Способ входа» и выберите « Электронная почта/Пароль» в разделе «Нативные поставщики» .
Включите метод входа «Электронная почта/Пароль» и нажмите «Сохранить» .
Пожарный склад
Создать базу данных
Перейдите в раздел «База данных Firestore» на консоли и нажмите «Создать базу данных» :
- При появлении запроса на правила безопасности выберите запуск в производственном режиме . Мы скоро обновим эти правила.
- Выберите расположение базы данных, которое вы хотите использовать для своего приложения. Обратите внимание, что выбор расположения базы данных — это решение, которое не будет действовать , и для его изменения вам потребуется создать новый проект. Подробнее о выборе расположения проекта см. в документации .
Правила развертывания
Чтобы развернуть написанные ранее правила безопасности, выполните следующую команду в каталоге 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.
Настройте приложение
В файлах 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
}
Если вы хотите протестировать свое приложение с реальным проектом Firebase, вы можете сделать следующее:
- Создайте приложение в режиме релиза и запустите его на устройстве.
- Временно замените
BuildConfig.DEBUG
наfalse
и запустите приложение еще раз.
Обратите внимание, что для корректного подключения к производству вам может потребоваться выйти из приложения и войти в него снова.