Справочные материалы по выполнению запросов

В этой статье рассказывается о результатах запроса, выполненного с помощью Query Explain. Чтобы узнать, как выполнить запрос с помощью Query Explain, ознакомьтесь со статьей Анализ выполнения запросов с помощью Query Explain.

Общие понятия

В дереве выполнения используются следующие понятия и термины:

Строки и записи

Термины строка и запись используются для общего обозначения документа или записи индекса.

Переменные

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

В узлах выполнения могут появляться следующие внутренние переменные:

  • $__key__ – ключ, который является внутренним идентификатором документа. Это абсолютный уникальный идентификатор, содержащий проект, базу данных и полный путь к документу.
  • $__id__ – уникальный идентификатор документа в коллекции. Это значение уникально в пределах одной коллекции.
  • $rid – идентификатор строки, который является внутренним идентификатором документа в хранилище. Это значение уникально в пределах одной коллекции.

Рассмотрим пример, в котором узел Compute используется для вычисления __id__ из документа __key__:

Compute
    |  $__id__1: _id($__key__)
    |  records returned: 1

Ограничения и диапазоны

Некоторые узлы сканирования используют атрибуты constraints и ranges для описания диапазона сканируемых значений. Эти атрибуты используют формат дерева диапазонов, который содержит список значений. Эти значения соответствуют упорядоченному списку ключей, которые появляются в определении индекса. Например, первый диапазон, который появляется в дереве, здесь (1..5], соответствует ограничениям для первого ключа, здесь a, в упорядоченном списке ключей:

| index: type=CollectionGroupIndex, id=CICAgOjXh#EK, keys=[a ASC, b ASC, __key__ ASC]
| constraints: /
               |----(1..5]
                    |----[1L]

Каждый уровень отступа указывает на ограничение, применяемое к следующему ключу в списке. Квадратные скобки означают "включительно", а круглые – "исключительно". В этом случае ограничение преобразуется в 1 < "a" <= 5 и "b" = 1.

В следующем примере с несколькими ветвями для a ограничение соответствует 1 < a <= 5 OR a = 10:

| constraints: /
               |----(1L, 5L]
               |----[10L]

Ключевые переменные

В некоторых узлах сканирования (например, SequentialScan) есть как список ключей в составе атрибута index, так и отдельный атрибут keys в узле Scan. Атрибут keys в узле Scan обозначает имя переменной каждого ключа в определении индекса по порядку. Переменные можно использовать для ссылки на значения времени выполнения сканируемого поля, расположенного выше в дереве выполнения.

В следующем примере значение поля user для текущего документа сопоставляется с переменной $user_1, а значение date_placed – с $date_placed_1.

index: type=CollectionGroupIndex, id=CICAgOjXh4EK, keys=[user ASC, date_placed ASC, __key__ ASC]
keys: [user ASC, date_placed ASC, __key__ ASC]

Узлы выполнения

Дерево выполнения запроса может содержать следующие узлы:

SeekingScan

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

Например, запрос, в котором a существует, а b равно 1, работающий с индексом ["a" ASC, "b" ASC], должен будет просканировать и вернуть отдельный, потенциально непоследовательный диапазон для каждого отдельного значения a. Это эффективнее, чем полный TableScan, но менее эффективно, чем один SequentialScan в составном индексе ["b" ASC, "a" ASC].

• SeekingScan
| constraints: /
               |----(-∞..+∞)
                    |----[1L]
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, quantity ASC, __key__ ASC]
| keys: [user ASC, quantity ASC, __key__ ASC]
| properties: Selection { user }
| records returned: 1
| records scanned: 1

SequentialScan

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

key ordering length – это количество ключей, которые должны быть сохранены и возвращены в исходном порядке. Для схемы [k1, k2, k3] длина упорядочения ключей, равная 0, означает, что сканирование может возвращать документы в любом порядке, 1 – упорядочение по k1, но строки с одинаковым значением k1 могут возвращаться в любом порядке, 3 – документы возвращаются в точном порядке сортировки.

• SequentialScan
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, date_placed ASC, __key__ ASC]
| key ordering length: 3
| keys: [user ASC, date_placed ASC, __key__ ASC]
| limit: 10
| properties: Selection { a }
| ranges: /
| records returned: 1
| records scanned: 1

UniqueScan

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

• UniqueScan
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, date_placed ASC, __key__ ASC]
| keys: [user ASC, date_placed ASC, __key__ ASC]
| properties: Selection { a }
| ranges: /
          |----(-∞..+∞)
| records returned: 1
| records scanned: 1

IndexSeek

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

Например, запрос, в котором user равно $user_id, а date_placed равно "2025-08-10", выполняемый по индексу ["user" ASC, "date_placed" ASC], будет использовать значение переменной $user_id во время выполнения и ограничение "2025-08-10" для date_placed, чтобы ограничить диапазоны сканирования.

• IndexSeek
| index: type=CollectionGroupIndex, id=CAE, keys=[user ASC, date_placed ASC, __key__ ASC]
| fields: [$user_1 ASC, $date_placed_1 ASC, $rid ASC]
| key: $key_1
| filter: equal($user_1, $user_id) AND equal($date_placed_1, "2025-08-10")
| records returned: 1
| records scanned: 1

Получить

Присоединяет идентификатор указанной строки к фактическому содержимому строки из основного хранилища. Fetch требуется, если родительскому узлу (или конечному результату запроса) требуется подмножество полей из документов.

• Fetch
|  order: PRESERVE_INPUT_ORDER
|  peak memory usage: 4.00 KiB (4,096 B)
|  properties: *
|  records returned: 1

LookupById

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

• LookupById
|  local_field: $localField_1
|  foreign_datasource: (default)#/**/foreign
|  output: $output_1

TableScan

Полное неупорядоченное сканирование коллекции. Используется, когда запрос выполняется без связанного индекса.

Порядок может быть STABLE или UNDEFINED, где STABLE обозначает детерминированный порядок.

• TableScan
|  order: STABLE
|  properties: *
|  records returned: 1
|  records scanned: 1
|  source: (default)#/**/collection

NestedLoopJoin

Объединяет два набора данных (левый и правый), перебирая каждую строку левого набора и сканируя правый набор на наличие совпадающих строк на основе join_condition.

join_type указывает тип объединения. Например, LEFT_OUTER означает, что все строки из левого набора входных данных включаются в выходные данные хотя бы один раз. Если строка из левой таблицы не соответствует ни одной строке из правой таблицы на основе join_condition, она все равно будет включена, а в столбцах из правой таблицы будут указаны нулевые значения.

• NestedLoopJoin
|  join_type: LEFT_OUTER
|  join_condition: equal($left, $right)
|
└── • left tree
|     ...
└── • right tree
      ...

HashAggregate

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

• HashAggregate
|  aggregations: [sum($b_1) AS total]
|  groups: [$a_1]
|  peak memory usage: 4.00 KiB (4,096 B)
|  records returned: 0

StreamAggregate

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

• StreamAggregate
|  keys: [foo ASC, bar ASC]
|  properties: Selection { baz }
|  aggregations: [$sum($foo_1) AS baz]

MajorSort

Выполняет сортировку по фиксированному набору свойств. Материализует все записи в памяти одновременно и возвращает отсортированные значения по порядку. Размер набора сортировки ограничен лимитом памяти запроса.

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

• MajorSort
|  fields: [a ASC, b DESC]
|  limit: 10
|  peak memory usage: 4.00 KiB (4,096 B)
|  records returned: 1

Concat

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

• Concat
├── • Fetch
...
├── • Fetch

Compute

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

• Compute
|  $user_1: user
|  $full_name_1: str_concat($first_name_1, " ", $last_name_1)
|  $address_1: UNSET
|  records returned: 1

Фильтр

Возвращает строки, соответствующие заданному выражению.

• Filter
|  expression: $eq(foo, "bar")
|  records returned: 1

RecordCount

Подсчитывает количество строк, созданных дочерним узлом, и передает текущее количество переменной, указанной в атрибуте count.

• RecordCount
|  count: $row_number_1
|  records returned: 1

Значения

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

• Values
| expression: [{__key__=/col/1}, {__key__=/col/2}]

UNNEST

Разворачивает значение, созданное дочерним узлом.

• Unnest
|  expression: foo AS unnested_foo

Ограничение

Ограничивает количество строк, возвращаемых родительскому узлу.

• Limit
|  limit: 10
|  records returned: 1

Смещение

Пропускает заданное количество строк, созданных дочерним узлом.

• Offset
|  offset: 10
|  records returned: 1