Informacje o wykonywaniu zapytań

Na tej stronie opisujemy dane wyjściowe zapytania wykonanego za pomocą funkcji Query Explain. Aby dowiedzieć się, jak wykonać zapytanie za pomocą funkcji Query Explain, przeczytaj artykuł Analizowanie wykonywania zapytań za pomocą funkcji Query Explain.

Podstawowe pojęcia

W drzewie wykonania używamy tych samych pojęć i terminów.

Wiersze i rekordy

Terminy wiersz i rekord są używane do ogólnego określania dokumentu lub wpisu w indeksie.

Zmienne

$ oznacza zmienną, która jest tworzona lub do której odwołuje się drzewo wykonania. Na przykład: $foo_1. Te zmienne są zwykle używane do odwoływania się do zawartości dokumentu lub wartości wyrażenia obliczonego podczas wykonywania zapytania.

W węzłach wykonania mogą pojawić się te zmienne wewnętrzne:

  • $__key__ – klucz jest wewnętrznym identyfikatorem dokumentu. Jest to bezwzględny, unikalny identyfikator zawierający projekt, bazę danych i pełną ścieżkę dokumentu.
  • $__id__ – identyfikator jest unikalnym identyfikatorem dokumentu w jego kolekcji. Jest on unikalny w obrębie jednej kolekcji.
  • $rid – identyfikator wiersza jest wewnętrznym identyfikatorem dokumentu w pamięci. Jest on unikalny w obrębie jednej kolekcji.

Rozważmy przykład, w którym węzeł Compute służy do obliczania __id__ na podstawie dokumentu __key__:

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

Ograniczenia i zakresy

Niektóre węzły skanowania używają atrybutów constraints i ranges do opisywania zakresu skanowanych wartości. Te atrybuty używają formatu drzewa zakresów, który zawiera listę wartości. Te wartości odpowiadają uporządkowanej liście kluczy, które pojawiają się w definicji indeksu. Na przykład pierwszy zakres, który pojawia się w drzewie, w tym przypadku (1..5], odpowiada ograniczeniom dotyczącym pierwszego klucza, w tym przypadku a, na uporządkowanej liście kluczy:

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

Każdy poziom wcięcia wskazuje ograniczenie stosowane do następnego klucza na liście. Nawiasy kwadratowe oznaczają zakres włącznie, a nawiasy okrągłe – zakres wyłączając. W tym przypadku ograniczenie przekłada się na 1 < "a" <= 5 i "b" = 1.

W tym przykładzie z wieloma gałęziami dla a, ograniczenie odpowiada 1 < a <= 5 OR a = 10:

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

Zmienne klucza

W niektórych węzłach skanowania (np. SequentialScan) występuje zarówno lista kluczy jako część atrybutu index, jak i osobny atrybut keys w węźle Scan. Atrybut keys w węźle Scan oznacza nazwę zmiennej każdego klucza w definicji indeksu w odpowiedniej kolejności. Zmienne mogą służyć do odwoływania się do wartości w czasie wykonywania skanowanego pola w dalszej części drzewa wykonania.

W tym przykładzie wartość pola user w bieżącym dokumencie jest mapowana na zmienną $user_1, a wartość date_placed na $date_placed_1.

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

Węzły wykonania

Drzewo wykonania zapytania może zawierać te węzły.

SeekingScan

Reprezentuje skanowanie dynamiczne, w którym zwracane wiersze mogą nie znajdować się w jednym sekwencyjnym zakresie indeksu, a do spełnienia zapytania trzeba wykonać kilka odrębnych skanowań.

Na przykład zapytanie, w którym a istnieje, a b jest równe 1, działające na indeksie ["a" ASC, "b" ASC], musiałoby skanować i zwracać osobny, potencjalnie niesekwencyjny zakres dla każdej odrębnej wartości a. Jest to bardziej wydajne niż pełne TableScan, ale mniej wydajne niż pojedyncze SequentialScan na indeksie złożonym ["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

Reprezentuje skanowanie statycznego, sekwencyjnego zakresu wierszy w pamięci, które można wykonać w ramach jednej operacji odczytu.

key ordering length odnosi się do liczby kluczy, które muszą zostać zachowane i zwrócone w oryginalnej kolejności kluczy. W przypadku schematu [k1, k2, k3] długość porządkowania kluczy równa 0 oznacza, że skanowanie może zwracać wyniki w dowolnej kolejności, 1 oznacza porządkowanie według k1, ale wiersze o tej samej wartości k1 mogą występować w dowolnej kolejności, a 3 zwraca dokumenty w dokładnej kolejności posortowanej.

• 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

Reprezentuje skanowanie statycznego, sekwencyjnego zakresu wierszy w pamięci z deduplikacją wierszy w pamięci.

• 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

Reprezentuje skanowanie dynamiczne, w którym zwracane wiersze mogą być sparametryzowane przez dane w czasie wykonywania i mogą nie znajdować się w jednym sekwencyjnym zakresie indeksu, a do spełnienia zapytania trzeba wykonać kilka odrębnych skanowań.

Na przykład zapytanie, w którym user jest równe $user_id, a date_placed jest równe "2025-08-10", działające na indeksie ["user" ASC, "date_placed" ASC], użyje wartości zmiennej $user_id w czasie wykonywania i ograniczenia "2025-08-10" w przypadku date_placed, aby ograniczyć zakresy skanowania.

• 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

Łączy identyfikator podanego wiersza z rzeczywistą zawartością wiersza z pamięci głównej. Węzeł Fetch jest wymagany, jeśli węzeł nadrzędny (lub ostateczny wynik zapytania) wymaga podzbioru pól z dokumentów.

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

LookupById

Wykonuje łączenie przez wyszukiwanie dokumentów w obcej kolekcji według ich identyfikatora. Identyfikatory do wyszukania pochodzą z pola w dokumentach wejściowych. Wyniki wyszukiwania są dodawane jako nowe pole do dokumentów wejściowych.

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

TableScan

Pełne, nieuporządkowane skanowanie kolekcji. Używane, gdy zapytanie jest wykonywane bez powiązanego indeksu.

Kolejność może być STABLE lub UNDEFINED, przy czym STABLE oznacza kolejność deterministyczną.

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

NestedLoopJoin

Wykonuje łączenie między 2 zbiorami danych (lewym i prawym) przez iterowanie po każdym wierszu danych wejściowych po lewej stronie i, w przypadku każdego wiersza po lewej stronie, skanowanie danych wejściowych po prawej stronie w poszukiwaniu pasujących wierszy na podstawie join_condition.

join_type wskazuje typ łączenia. Na przykład LEFT_OUTER oznacza, że wszystkie wiersze z danych wejściowych po lewej stronie są uwzględniane w danych wyjściowych co najmniej raz. Jeśli wiersz po lewej stronie nie pasuje do żadnego wiersza w danych wejściowych po prawej stronie na podstawie join_condition, nadal będzie uwzględniany z wartościami null w kolumnach z danych wejściowych po prawej stronie.

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

HashAggregate

Implementacja operacji agregujących oparta na haszowaniu. Wymaga zmaterializowania całej grupy w pamięci przed zwróceniem wyniku i nie może przekraczać limitu pamięci zapytania.

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

StreamAggregate

Specjalistyczny węzeł agregujący, który utrzymuje stan tylko dla jednej grupy naraz, co zmniejsza szczytowe zużycie pamięci. Używany, gdy węzeł podrzędny będzie zwracać grupy sekwencyjnie. Na przykład podczas grupowania według odrębnych wartości pola przy użyciu indeksu w tym polu.

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

MajorSort

Wykonuje operację sortowania na stałym zbiorze właściwości. Materializuje wszystkie rekordy w pamięci naraz i zwraca posortowane wartości w kolejności. Rozmiar zbioru sortowania jest ograniczony przez limit pamięci zapytania.

Gdy podany jest kolejny limit, do zmniejszenia zużycia pamięci używany jest algorytm sortowania top-k. Dzięki niemu sortowanie można wykonywać na dowolnie dużym zbiorze rekordów, o ile pamięć używana do przechowywania k rozważanych elementów nie przekracza limitu.

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

Concat

Łączy wyniki wielu węzłów podrzędnych i zwraca wynik do węzła nadrzędnego. Ten węzeł nie deduplikuje wyników, które pojawiają się w wielu węzłach podrzędnych, a kolejność zwracanych wyników jest niedeterministyczna.

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

Compute

Oblicza zbiór wyrażeń, przypisując wyniki do zbioru zmiennych.

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

Filter

Selektywnie zwraca wiersze tylko wtedy, gdy pasują one do podanego wyrażenia.

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

RecordCount

Zlicza liczbę wierszy wygenerowanych przez węzeł podrzędny i emituje bieżącą liczbę do zmiennej określonej w atrybucie count.

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

Values

Tworzy sekwencję wartości literałów, na których można pracować. Używany głównie wtedy, gdy jako dane wejściowe zapytania podawany jest ustalony zbiór dokumentów.

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

Unnest

Rozpakowuje wartość wygenerowaną przez węzeł podrzędny.

• Unnest
|  expression: foo AS unnested_foo

Limit

Ogranicza liczbę wierszy zwracanych do węzła nadrzędnego.

• Limit
|  limit: 10
|  records returned: 1

Offset

Pomija określoną liczbę wierszy wygenerowanych przez węzeł podrzędny.

• Offset
|  offset: 10
|  records returned: 1