Не так давно Google зарелизил Cloud Firestore. Cloud Firestore — это облачная NoSQL база данных, которую Google позиционирует как замену Realtime Database. В этой статье я хочу рассказать как начать ей пользоваться.
Возможности
Cloud Firestore позволяет хранить данные на удаленном сервере, легко получать к ним доступ и следить за изменениями в режиме реального времени. В документации есть отличное сравнение Cloud Firestore и Realtime Database.
Создание и подключение к проекту
В консоли Firebase выбираем Database и нажимаем на Create database. Дальше выбираем настройки доступа. Для ознакомления нам будет достаточно тестового режима, но на проде лучше подойти к этому вопросу серьезнее. Подробнее про режимы доступа можно почитать здесь.

Для настройки проекта проделываем следующие шаги:
- Добавить Firebase к проекту по инструкции от сюда
- Добавить зависимость в app/build.gradle
implementation 'com.google.firebase:firebase-firestore:18.1.0'
Теперь все готово.
Для ознакомления с базовыми приемами работы с Cloud Firestore я написал простенькое приложение. Для его работы необходимо создать проект в консоли Firebase и добавить файлик google-services.json в проект в Android Studio.
Структура хранения данных
В Firestore для хранения данных используются коллекции и документы. Документ — это запись, которая содержит какие-либо поля. Документы объединяются в коллекции. Также документ может содержать вложенные коллекции, но на андроиде это не поддерживается. Если проводить аналогию с SQL-базой, то коллекция — это таблица, а документ — это запись в этой таблице. Одна коллекция может содержать документы с разным набором полей.
Получение и запись данных
Для того чтобы получить все документы какой-либо коллекции достаточно следующего кода
remoteDB.collection(“Tasks”)
.get()
.addOnSuccessListener { querySnapshot ->
// Успешно получили данные. Список в querySnapshot.documents
}
.addOnFailureListener { exception ->
// Произошла ошибка при получении данных
}
}
Здесь мы запрашиваем все документы из коллекции Tasks.
Библиотека позволяет формировать запросы с параметрами. Следующий код показывает как получить документы из коллекции по условию
remoteDB.collection(“Tasks”)
.whereEqualTo("title", "Task1")
.get()
.addOnSuccessListener { querySnapshot ->
// Успешно получили данные. Список в querySnapshot.documents
}
.addOnFailureListener { exception ->
// Произошла ошибка при получении данных
}
}
Здесь мы запрашиваем все документы из коллекции Tasks, у которых поле title соответсвует значению Task1.
При получении документов, их можно сразу конвертировать в наши data-классы
remoteDB.collection(“Tasks”)
.get()
.addOnSuccessListener { querySnapshot ->
// Успешно получили данные. Список в querySnapshot.documents
val taskList: List<RemoteTask> = querySnapshot.toObjects(RemoteTask::class.java)
}
.addOnFailureListener { exception ->
// Произошла ошибка при получении данных
}
}
Для записи необходимо сформировать Hashmap с данными (где в качестве ключа выступает название поля, а в качестве значения — значение этого поля) и передать библиотеке. Следующий код это демонстрирует
val taskData = HashMap<String, Any>()
taskData["title"] = task.title
taskData["created"] = Timestamp(task.created.time / 1000, 0)
remoteDB.collection("Tasks")
.add(taskData)
.addOnSuccessListener {
// Успешная запись
}
.addOnFailureListener {
// Произошла ошибка при записи
}
В данном примере будет создан новый документ и Firestore сгенерирует ему id. Чтобы задать собственный id необходимо сделать следующее
val taskData = HashMap<String, Any>()
taskData["title"] = task.title
taskData["created"] = Timestamp(task.created.time / 1000, 0)
remoteDB.collection("Tasks")
.document("New task")
.set(taskData)
.addOnSuccessListener {
// Успешная запись
}
.addOnFailureListener {
// Произошла ошибка при записи
}
В этом случае если нет документа с id равном New task, то он будет создан, а если есть, то указанные поля будут обновлены.
Еще один вариант создания/обновления документа
remoteDB.collection("Tasks")
.document("New task")
.set(mapToRemoteTask(task))
.addOnSuccessListener {
// Успешная запись
}
.addOnFailureListener {
// Произошла ошибка при записи
}
Подписываемся на изменения
Firestore позволяет подписаться на изменения данных. Подписаться можно как на изменения коллекции, так и на изменения конкретного документа
remoteDB.collection("Tasks")
.addSnapshotListener { querySnapshot, error ->
// querySnapshot - список изменений
// error - ошибка
}
querySnapshot.documents — содержит обновленный список всех документов
querySnapshot.documentChanges — содержит список изменений. Каждый объект содержит измененный документ и тип изменения. Возможны 3 типа изменений
ADDED — документ добавлен,
MODIFIED — документ изменен,
REMOVED — документ удален
Загрузка большого количества данных
Realtime Database предоставляет более менее удобный механизм загрузки большого количества данных, который заключается в ручном редактировании json-файла и его загрузки. Firestore из коробки ничего такого не предоставляет. Было очень неудобно добавлять новые документы, пока я не нашел способ как можно легко загрузить большой объем информации. Чтобы у вас не было таких проблем как у меня, ниже приложу инструкцию как быстро и легко загрузить большой объем данных. Инструкция была найдена на просторах интернета.
- Установить Node.js и npm
- Установить пакет firebase-admin выполнив команду
npm install firebase-admin --save
- Сформировать json-файл с данными коллекции. Пример можно посмотреть в файле Tasks.json
- Для загрузки нам понадобиться ключ доступа. Как его получить хорошо описано в этой статье
- В файле export.js проставить свои данные
require('./firestore_key.json') — файл с ключом доступа. У меня лежал в папке со скриптом
<YOU_DATABASE> — название вашей firestore-базы
"./json/Tasks.json" — путь до файла в котором лежат данные
['created'] — список имен полей с типом Timestamp - Выполнить скрипт
node export.js
В скрипте используются наработки dalenguyen
Заключение
Cloud Firestore я использовал в одном из своих проектов и не испытал никаких серьезных проблем. Одна из моих коллекций содержит около 15000 документов и запросы по ней проходят довольно быстро и это без использования индексов. Используя Cloud Firestore совместно с Room и Remote Config можно существенно сократить количество обращений к базе и не выходить за бесплатные лимиты. На бесплатном тарифе в день можно прочитать 50000 документов, записать 20000 и 20000 удалить.