Типы индексов в Cloud Firestore

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

На этой странице описаны два типа индексов, которые использует Cloud Firestore: индексы одного поля и составные индексы .

Определение и структура индекса

Индекс определяется в списке полей данного документа с соответствующим режимом индексирования для каждого поля.

Индекс содержит запись для каждого поля, указанного в определении индекса. Индекс включает в себя все документы, которые являются потенциальными результатами запросов на основе индекса. Документ включается в индекс только в том случае, если для каждого поля, используемого в индексе, установлено индексированное значение; если определение индекса ссылается на поле, для которого в документе не установлено значение, этот документ не будет отображаться в индексе и, следовательно, никогда не будет возвращен в результате любого запроса на основе индекса.

Составной индекс сортируется по значениям полей в порядке, указанном в определении индекса.

Индекс каждого запроса

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

Меньше управления индексами, больше разработки приложений

Cloud Firestore включает в себя функции, которые сокращают время, необходимое для управления индексами. Индексы, необходимые для большинства основных запросов, создаются автоматически. Когда вы используете и тестируете свое приложение, Cloud Firestore помогает вам определить и создать дополнительные индексы, необходимые вашему приложению.

Типы индексов

Cloud Firestore использует два типа индексов: однополевые и составные . Помимо количества проиндексированных полей, индексы с одним полем и составные индексы различаются по способу управления ими.

Индексы с одним полем

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

Автоматическое индексирование

По умолчанию Cloud Firestore автоматически поддерживает индексы отдельных полей для каждого поля в документе и каждого подполя на карте. Cloud Firestore использует следующие настройки по умолчанию для индексов с одним полем:

  • Для каждого поля, не являющегося массивом и не являющегося картой, Cloud Firestore определяет два индекса с одним полем области коллекции : один в восходящем режиме и один в нисходящем режиме.

  • Для каждого поля карты Cloud Firestore создает следующее:

    • Один возрастающий индекс области коллекции для каждого подполя, не являющегося массивом или картой.
    • Один нисходящий индекс области коллекции для каждого подполя, не являющегося массивом или картой.
    • Один массив области коллекции содержит индекс для каждого подполя массива.
    • Cloud Firestore рекурсивно индексирует каждое подполе карты.
  • Для каждого поля массива в документе Cloud Firestore создает и поддерживает индекс, содержащий массив.

  • Индексы с одним полем в области группы сбора не поддерживаются по умолчанию.

Исключения для индекса для одного поля

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

Используйте значение пути к полю * , чтобы добавить исключения индекса на уровне коллекции для всех полей в группе коллекций. Например, для comments группы сбора задайте путь к полю * , чтобы он соответствовал всем полям в группе сбора comments , и отключите индексацию всех полей в группе сбора. Затем вы можете добавить исключения для индексации только тех полей, которые необходимы для ваших запросов. Уменьшение количества индексированных полей снижает затраты на хранение и может повысить производительность записи.

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

Чтобы создавать исключения для индексов для одного поля и управлять ими, см. раздел «Управление индексами в Cloud Firestore» .

Составные индексы

Составной индекс хранит отсортированное сопоставление всех документов в коллекции на основе упорядоченного списка индексируемых полей.

Cloud Firestore использует составные индексы для поддержки запросов, которые еще не поддерживаются индексами с одним полем.

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

Каждый раз, когда вы пытаетесь выполнить запрос, не поддерживаемый индексом, Cloud Firestore возвращает сообщение об ошибке со ссылкой, по которой вы можете перейти, чтобы создать отсутствующий индекс.

Вы также можете определять составные индексы и управлять ими вручную с помощью консоли или интерфейса командной строки Firebase . Дополнительные сведения о создании составных индексов и управлении ими см. в разделе «Управление индексами» .

Режимы индексирования и области запросов

Вы настраиваете однополевые и составные индексы по-разному, но оба требуют настройки режимов индексирования и областей запросов для ваших индексов.

Индексные режимы

При определении индекса вы выбираете режим индексирования для каждого индексируемого поля. Режим индексирования каждого поля поддерживает определенные условия запроса для этого поля. Вы можете выбрать один из следующих режимов индексации:

Индексный режим Описание
Восходящая Поддерживает < , <= , == , >= , > , != , in и not-in , предложения запроса в поле и поддерживает сортировку результатов в порядке возрастания на основе значения этого поля.
Нисходящая Поддерживает < , <= , == , >= , > , != , in и not-in предложения запроса в поле и поддерживает сортировку результатов в порядке убывания на основе значения этого поля.
Массив содержит Поддерживает предложения запроса array-contains и array-contains-any в поле.
Вектор Поддерживает предложения запроса FindNearest в поле.

Области запроса

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

Объем коллекции
Cloud Firestore по умолчанию создает индексы с областью сбора. Эти индексы поддерживают запросы, возвращающие результаты из одной коллекции.

Область действия группы сбора
Группа коллекций включает в себя все коллекции с одинаковым идентификатором коллекции. Чтобы запустить запрос группы сбора , который возвращает отфильтрованные или упорядоченные результаты из группы сбора, необходимо создать соответствующий индекс с областью действия группы сбора.

Порядок по умолчанию и поле __name__

Помимо сортировки документов по режимам индексации, заданным для каждого поля (по возрастанию или убыванию), индексы применяют окончательную сортировку по полю __name__ каждого документа. В качестве значения поля __name__ устанавливается полный путь к документу. Это означает, что документы в наборе результатов с одинаковыми значениями полей сортируются по пути к документу.

По умолчанию поле __name__ сортируется в том же направлении, что и последнее отсортированное поле в определении индекса. Например:

Коллекция Поля проиндексированы Область запроса
города имя, __name__ Коллекция
города Состояние , __name__ Коллекция
города страна, население, __name__ Коллекция

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

Идеальный индекс

Идеальный индекс для запроса, который позволяет выполнить запрос наиболее эффективно, определяется по следующим свойствам (по порядку):

  1. Поля, используемые в фильтрах равенства
  2. Поля, используемые в порядках сортировки
  3. Поля, используемые в фильтрах диапазона и неравенства (которые еще не включены в порядок сортировки)
  4. Поля, используемые в агрегатах (которые еще не включены в порядок сортировки и фильтры диапазона и неравенства)

Firestore вычисляет результаты запросов следующим образом:

  1. Идентифицирует индекс, соответствующий коллекции запроса, свойствам фильтра, операторам фильтра и порядкам сортировки.
  2. Определяет позицию индекса для начала сканирования с использованием фильтров равенства запроса и фильтров диапазона и неравенства в первом порядке по полю.
  3. Начинает сканирование индекса, возвращая каждый документ, удовлетворяющий всем фильтрам, пока:
    1. обнаруживает документ, который не соответствует условиям фильтра, и подтверждает, что любой последующий документ никогда не будет полностью соответствовать условиям фильтра, или
    2. достигает конца индекса, или
    3. собрал максимальное количество результатов, запрошенное запросом

Пример индексирования

Автоматически создавая для вас индексы с одним полем, Cloud Firestore позволяет вашему приложению быстро поддерживать самые основные запросы к базе данных. Индексы с одним полем позволяют выполнять простые запросы на основе значений полей и компараторов < , <= , == , >= , > и in . Для полей массива они позволяют выполнять запросы array-contains и array-contains-any .

Для иллюстрации рассмотрим следующие примеры с точки зрения создания индекса. Следующий фрагмент кода создает несколько документов city в коллекции cities и задает поля name , state , country , capital , population и tags для каждого документа:

Интернет
var citiesRef = db.collection("cities");

citiesRef.doc("SF").set({
    name: "San Francisco", state: "CA", country: "USA",
    capital: false, population: 860000,
    regions: ["west_coast", "norcal"] });
citiesRef.doc("LA").set({
    name: "Los Angeles", state: "CA", country: "USA",
    capital: false, population: 3900000,
    regions: ["west_coast", "socal"] });
citiesRef.doc("DC").set({
    name: "Washington, D.C.", state: null, country: "USA",
    capital: true, population: 680000,
    regions: ["east_coast"] });
citiesRef.doc("TOK").set({
    name: "Tokyo", state: null, country: "Japan",
    capital: true, population: 9000000,
    regions: ["kanto", "honshu"] });
citiesRef.doc("BJ").set({
    name: "Beijing", state: null, country: "China",
    capital: true, population: 21500000,
    regions: ["jingjinji", "hebei"] });

Предполагая настройки автоматического индексирования по умолчанию, Cloud Firestore обновляет один восходящий индекс одного поля для каждого поля, не являющегося массивом, один нисходящий индекс одного поля для каждого поля, не являющегося массивом, и один массив, содержащий индекс одного поля для поля массива. Каждая строка в следующей таблице представляет запись в индексе с одним полем:

Коллекция Поле проиндексировано Область запроса
города имя Коллекция
города состояние Коллекция
города страна Коллекция
города столица Коллекция
города население Коллекция
города имя Коллекция
города состояние Коллекция
города страна Коллекция
города столица Коллекция
города население Коллекция
города array-contains регионы Коллекция

Запросы, поддерживаемые индексами с одним полем

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

Интернет
const stateQuery = citiesRef.where("state", "==", "CA");
const populationQuery = citiesRef.where("population", "<", 100000);
const nameQuery = citiesRef.where("name", ">=", "San Francisco");

Вы также можете создавать запросы in равенство ( == ):

Интернет
citiesRef.where('country', 'in', ["USA", "Japan", "China"])

// Compound equality queries
citiesRef.where("state", "==", "CO").where("name", "==", "Denver")
citiesRef.where("country", "==", "USA")
         .where("capital", "==", false)
         .where("state", "==", "CA")
         .where("population", "==", 860000)

Если вам нужно запустить составной запрос, использующий сравнение диапазонов ( < , <= , > или >= ) или если вам нужно выполнить сортировку по другому полю, вы должны создать составной индекс для этого запроса.

Индекс array-contains позволяет запрашивать поле массива regions :

Интернет
citiesRef.where("regions", "array-contains", "west_coast")
// array-contains-any and array-contains use the same indexes
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])

Запросы, поддерживаемые составными индексами

Cloud Firestore использует составные индексы для поддержки составных запросов, которые еще не поддерживаются индексами с одним полем. Например, вам понадобится составной индекс для следующих запросов:

Интернет
citiesRef.where("country", "==", "USA").orderBy("population", "asc")
citiesRef.where("country", "==", "USA").where("population", "<", 3800000)
citiesRef.where("country", "==", "USA").where("population", ">", 690000)
// in and == clauses use the same index
citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)

Для этих запросов требуется составной индекс, указанный ниже. Поскольку запрос использует равенство ( == или in ) для поля country , вы можете использовать режим индекса по возрастанию или убыванию для этого поля. По умолчанию в предложениях о неравенстве применяется порядок сортировки по возрастанию на основе поля в предложении о неравенстве.

Коллекция Поля проиндексированы Область запроса
города (или ) страна, население Коллекция

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

Интернет
citiesRef.where("country", "==", "USA").orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", "<", 3800000)
         .orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", ">", 690000)
         .orderBy("population", "desc")

citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)
         .orderBy("population", "desc")
Коллекция Поля проиндексированы Область запроса
города страна, население Коллекция
города страна , население Коллекция

Вам также необходимо создать составной индекс для объединения array-contains или array-contains-any с дополнительными предложениями.

Интернет
citiesRef.where("regions", "array-contains", "east_coast")
         .where("capital", "==", true)

// array-contains-any and array-contains use the same index
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])
         .where("capital", "==", true)
Коллекция Поля проиндексированы Область запроса
города массив содержит теги, (или ) Коллекция

Запросы, поддерживаемые индексами групп сбора

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

Интернет
var citiesRef = db.collection("cities");

citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Bridge",
    category : "bridge" });
citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Park",
    category : "park" });

citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Gallery of Art",
    category : "museum" });
citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Mall",
    category : "park" });

Используя следующий индекс с одним полем и областью коллекции, вы можете запросить коллекцию landmarks одного города на основе поля category :

Коллекция Поля проиндексированы Область запроса
достопримечательности Категория (или ) Коллекция
Интернет
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")
citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])

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

Коллекция Поля проиндексированы Область запроса
достопримечательности Категория (или ) Группа сбора

Если этот индекс включен, вы можете запросить группу сбора landmarks :

Интернет
var landmarksGroupRef = db.collectionGroup("landmarks");

landmarksGroupRef.where("category", "==", "park")
landmarksGroupRef.where("category", "in", ["park", "museum"])

Чтобы выполнить запрос группы сбора, который возвращает отфильтрованные или упорядоченные результаты, необходимо включить соответствующий индекс с одним полем или составной индекс с областью группы сбора. Однако запросы группы коллекций, которые не фильтруют и не упорядочивают результаты, не требуют каких-либо дополнительных определений индексов.

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

Интернет
db.collectionGroup("landmarks").get()

Записи указателя

Настроенные индексы вашего проекта и структура документа определяют количество записей индекса для документа. Записи индекса учитываются в пределах лимита количества записей индекса .

В следующем примере демонстрируются записи индекса документа.

Документ

/cities/SF

city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]

Индексы с одним полем

  • название города ASC
  • название города DESC
  • температура.лето ASC
  • температура.лето DESC
  • температура.зима ASC
  • температура.зима DESC
  • Районы Массив содержит (ASC и DESC)

Составные индексы

  • название города ASC, районы ARRAY
  • название города DESC, районы ARRAY

Записи указателя

Эта конфигурация индексирования приводит к появлению следующих 18 записей индекса для документа:

Индекс Индексированные данные
Записи индекса с одним полем
название города ASC city_name: "Сан-Франциско"
название города DESC city_name: "Сан-Франциско"
температура.лето ASC температура.лето: 67
температура.лето DESC температура.лето: 67
температура.зима ASC температура.зима: 55
температура.зима DESC температура.зима: 55
Массив соседей Содержит ASC кварталы: «Миссия»
Районы Массив Содержит DESC кварталы: «Миссия»
Массив соседей Содержит ASC кварталы: «Даунтаун»
Районы Массив Содержит DESC кварталы: «Даунтаун»
Массив соседей Содержит ASC кварталы: «Марина»
Районы Массив Содержит DESC кварталы: «Марина»
Записи составного индекса
название города ASC, районы ARRAY city_name: "Сан-Франциско", районы: "Миссия"
название города ASC, районы ARRAY city_name: "Сан-Франциско", районы: "Даунтаун"
название города ASC, районы ARRAY city_name: "Сан-Франциско", районы: "Марина"
название города DESC, районы ARRAY city_name: "Сан-Франциско", районы: "Миссия"
название города DESC, районы ARRAY city_name: "Сан-Франциско", районы: "Даунтаун"
название города DESC, районы ARRAY city_name: "Сан-Франциско", районы: "Марина"

Индексы и цены

Индексы увеличивают затраты на хранение вашего приложения. Дополнительные сведения о том, как рассчитывается размер хранилища для индексов, см. в разделе Размер записи индекса .

Использование преимущества слияния индексов

Хотя Cloud Firestore использует индекс для каждого запроса, он не обязательно требует наличия одного индекса для каждого запроса. Для запросов с несколькими предложениями равенства ( == ) и, при необходимости, предложением orderBy , Cloud Firestore может повторно использовать существующие индексы. Cloud Firestore может объединять индексы для простых фильтров равенства для создания составных индексов, необходимых для более крупных запросов на равенство.

Вы можете снизить затраты на индексацию, определив ситуации, в которых можно воспользоваться преимуществами объединения индексов. Например, представьте себе коллекцию restaurants для приложения для оценки ресторанов:

  • рестораны

    • бургертимейм

      name : "Burger Thyme"
      category : "burgers"
      city : "San Francisco"
      editors_pick : true
      star_rating : 4

Теперь представьте, что это приложение использует запросы, подобные приведенным ниже. Обратите внимание, что приложение использует комбинации условий равенства для category , city и editors_pick , при этом всегда сортируя по возрастанию star_rating :

Интернет
db.collection("restaurants").where("category", "==", "burgers")
                            .orderBy("star_rating")

db.collection("restaurants").where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==" "San Francisco")
                            .where("editors_pick", "==", true )
                            .orderBy("star_rating")

Вы можете создать индекс для каждого запроса:

Коллекция Поля проиндексированы Область запроса
рестораны , звездный_рейтинг Коллекция
рестораны город, звездный_рейтинг Коллекция
рестораны Категория , город , звездный_рейтинг Коллекция
рестораны категория, город, выбор редактора, звездный_рейтинг Коллекция

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

Коллекция Поля проиндексированы Область запроса
рестораны Категория , звездный_рейтинг Коллекция
рестораны город, звездный_рейтинг Коллекция
рестораны editors_pick, star_rating Коллекция

Этот набор индексов не только меньше, но и поддерживает дополнительный запрос:

Интернет
db.collection("restaurants").where("editors_pick", "==", true)
                            .orderBy("star_rating")

Ограничения индексации

К индексам применяются следующие ограничения. Все квоты и лимиты см. в разделе Квоты и лимиты .

Лимит Подробности
Максимальное количество составных индексов для базы данных
Максимальное количество конфигураций с одним полем для базы данных

Одна конфигурация уровня поля может содержать несколько конфигураций для одного и того же поля. Например, освобождение от индексации одного поля и политика TTL для одного и того же поля считаются одной конфигурацией поля в рамках ограничения.

Максимальное количество записей указателя для каждого документа

40 000

Количество записей индекса представляет собой сумму следующих значений для документа:

  • Количество записей индекса с одним полем
  • Количество записей составного индекса

Чтобы увидеть, как Cloud Firestore превращает документ и набор индексов в записи индекса, посмотрите этот пример подсчета записей индекса .

Максимальное количество полей в составном индексе 100
Максимальный размер записи индекса

7,5 КиБ

Чтобы узнать, как Cloud Firestore вычисляет размер записи индекса, см. Размер записи индекса .

Максимальная сумма размеров записей указателя документа

8 МБ

Общий размер документа представляет собой сумму следующих величин:

  • Сумма размеров записей индекса с одним полем документа.
  • Сумма размеров записей составного индекса документа.
  • Максимальный размер значения индексированного поля

    1500 байт

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

    Лучшие практики индексирования

    Для большинства приложений вы можете полагаться на автоматическое индексирование и ссылки на сообщения об ошибках для управления индексами. Однако вы можете добавить исключения для одного поля в следующих случаях:

    Случай Описание
    Большие строковые поля

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

    Высокая скорость записи в коллекцию, содержащую документы с последовательными значениями.

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

    Например, в случае использования Интернета вещей с высокой скоростью записи коллекция, содержащая документы с полем метки времени, может приближаться к пределу в 500 операций записи в секунду.

    Поля TTL

    Если вы используете политики TTL (время жизни) , обратите внимание, что поле TTL должно быть меткой времени. Индексирование полей TTL включено по умолчанию и может повлиять на производительность при более высоких скоростях трафика. Рекомендуется добавить исключения для отдельных полей для полей TTL.

    Большой массив или поля карты

    Большие поля массива или карты могут приближаться к пределу в 40 000 записей индекса на документ. Если вы не выполняете запрос на основе большого массива или поля карты, вам следует освободить его от индексации.

    Если вы используете запросы с операторами диапазона и неравенства в нескольких полях, ознакомьтесь с рекомендациями по индексированию , которые следует учитывать, чтобы оптимизировать производительность и стоимость запросов Cloud Firestore.

    Для получения дополнительной информации о том, как решить проблемы индексации (разветвление индекса, ошибки INVALID_ARGUMENT ), посетите страницу устранения неполадок .