Типы индексов в 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 возвращает сообщение об ошибке со ссылкой, по которой можно перейти для создания отсутствующего индекса.

Вы также можете определять и управлять составными индексами вручную с помощью консоли или Firebase CLI . Подробнее о создании и управлении составными индексами см. в разделе Управление индексами .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. Определяет индекс, соответствующий коллекции запроса, свойствам фильтра, операторам фильтра и порядкам сортировки.
  2. Определяет позицию индекса, с которой начинается сканирование. Начальная позиция предваряется фильтрами равенства запроса и заканчивается фильтрами диапазона и неравенства по первому полю orderBy .
  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
  • city_name DESC
  • температуры.лето ASC
  • температуры.лето DESC
  • температуры.зима ASC
  • температура.зима DESC
  • Массив содержит окрестности (ASC и DESC)

Композитные индексы

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

Записи индекса

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

Индекс Индексированные данные
Записи индекса с одним полем
название_города ASC city_name: "Сан-Франциско"
city_name DESC city_name: "Сан-Франциско"
температуры.лето ASC температура.лето: 67
температуры.лето DESC температура.лето: 67
температуры.зима ASC температура зимой: 55
температура.зима DESC температура зимой: 55
Массив окрестностей содержит ASC кварталы: «Миссия»
Массив окрестностей содержит DESC кварталы: «Миссия»
Массив окрестностей содержит ASC кварталы: «Центр города»
Массив окрестностей содержит DESC кварталы: «Центр города»
Массив окрестностей содержит ASC микрорайоны: «Марина»
Массив окрестностей содержит DESC микрорайоны: «Марина»
Составные записи индекса
название_города ASC, районы ARRAY city_name: "Сан-Франциско", neighborhood_name: "Mission"
название_города ASC, районы ARRAY city_name: "Сан-Франциско", neighborhood_name: "Downtown"
название_города ASC, районы ARRAY city_name: "Сан-Франциско", neighborhood_name: "Марина"
city_name DESC, neighborhoods ARRAY city_name: "Сан-Франциско", neighborhood_name: "Mission"
city_name DESC, neighborhoods ARRAY city_name: "Сан-Франциско", neighborhood_name: "Downtown"
city_name DESC, neighborhoods ARRAY city_name: "Сан-Франциско", neighborhood_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 объединять индексы для условий равенства:

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

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

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

Лимиты индексации

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

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

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

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

40,000

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

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

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

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

7,5 КБ

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

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

8 МБ

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

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

    1500 байт

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

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

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

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

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

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

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

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

    TTL-поля

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

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

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

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

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