Поиск с профилированием

Как интерпретируется запрос

Рассмотрим этот пример сложного запроса:

"hello world" @title "example program"~5 @body python -(php|perl) @* code

Полное значение этого поиска:

  • Найти слова 'hello' и 'world' рядом друг с другом в любом поле документа;
  • Кроме того, в том же документе должны содержаться слова 'example' и 'program' в поле заголовка, с не более чем 5 словами между ними (не включая 5); (например, "example PHP program" подойдет, а "example script to introduce outside data into the correct context for your program" — нет, так как между двумя терминами 5 или более слов)
  • Более того, в том же документе должно быть слово 'python' в поле body, при этом исключая 'php' или 'perl';
  • Наконец, в том же документе должно содержаться слово 'code' в любом поле.

Оператор OR имеет приоритет над AND, поэтому "looking for cat | dog | mouse" означает "looking for (cat | dog | mouse)", а не "(looking for cat) | dog | mouse".

Чтобы понять, как будет выполняться запрос, Manticore Search предоставляет инструменты профилирования запросов для изучения дерева запроса, сгенерированного выражением запроса.

Профилирование дерева запроса в SQL

Чтобы включить профилирование полнотекстового запроса с помощью SQL-запроса, необходимо активировать его перед выполнением нужного запроса:

SET profiling =1;
SELECT * FROM test WHERE MATCH('@title abc* @body hey');

Чтобы просмотреть дерево запроса, выполните команду SHOW PLAN сразу после выполнения запроса:

SHOW PLAN;

Эта команда вернет структуру выполненного запроса. Имейте в виду, что 3 оператора — SET profiling, сам запрос и SHOW — должны выполняться в одной сессии.

Профилирование запроса в HTTP JSON

При использовании протокола HTTP JSON можно просто включить "profile":true, чтобы получить в ответе структуру дерева полнотекстового запроса.

{
  "table":"test",
  "profile":true,
  "query":
  {
    "match_phrase": { "_all" : "had grown quite" }
  }
}

В ответе будет объект profile, содержащий член query.

Свойство query содержит преобразованное дерево полнотекстового запроса. Каждый узел состоит из:

  • type: тип узла, который может быть AND, OR, PHRASE, KEYWORD и т.д.
  • description: поддерево запроса для этого узла, представленное в виде строки (в формате SHOW PLAN)
  • children: дочерние узлы, если есть
  • max_field_pos: максимальная позиция в поле

У узла ключевого слова дополнительно будут:

  • word: преобразованное ключевое слово.
  • querypos: позиция этого ключевого слова в запросе.
  • excluded: ключевое слово исключено из запроса.
  • expanded: ключевое слово добавлено расширением префикса.
  • field_start: ключевое слово должно появиться в начале поля.
  • field_end: ключевое слово должно появиться в конце поля.
  • boost: IDF ключевого слова будет умножен на это значение.
‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
SET profiling=1;
SELECT * FROM test WHERE MATCH('@title abc* @body hey');
SHOW PLAN \G
‹›
Response
*************************** 1\. row ***************************
Variable: transformed_tree
   Value: AND(
  OR(fields=(title), KEYWORD(abcx, querypos=1, expanded), KEYWORD(abcm, querypos=1, expanded)),
  AND(fields=(body), KEYWORD(hey, querypos=2)))
1 row in set (0.00 sec)

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

‹›
  • SQL
  • JSON
  • PHP
  • Python
  • Python-asyncio
  • javascript
  • Java
  • C#
  • Rust
  • TypeScript
  • Go
📋
SET profiling=1;
SELECT id FROM forum WHERE MATCH('@title way* @content hey') LIMIT 1;
SHOW PLAN;
‹›
Response
Query OK, 0 rows affected (0.00 sec)
+--------+
| id     |
+--------+
| 711651 |
+--------+
1 row in set (0.04 sec)
+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variable         | Value                                                                                                                                                                                                                                                                                                                                                                                                                   |
+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| transformed_tree | AND(
  OR(
    OR(
      AND(fields=(title), KEYWORD(wayne, querypos=1, expanded)),
      OR(
        AND(fields=(title), KEYWORD(ways, querypos=1, expanded)),
        AND(fields=(title), KEYWORD(wayyy, querypos=1, expanded)))),
    AND(fields=(title), KEYWORD(way, querypos=1, expanded)),
    OR(fields=(title), KEYWORD(way*, querypos=1, expanded))),
  AND(fields=(content), KEYWORD(hey, querypos=2))) |
+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Профилирование без выполнения запроса

SQL-оператор EXPLAIN QUERY позволяет отобразить дерево выполнения для заданного полнотекстового запроса без фактического выполнения поискового запроса по таблице.

‹›
  • SQL
SQL
📋
EXPLAIN QUERY index_base '@title running @body dog'\G
‹›
Response
 EXPLAIN QUERY index_base '@title running @body dog'\G
*************************** 1\. row ***************************
Variable: transformed_tree
   Value: AND(
      OR(
            AND(fields=(title), KEYWORD(run, querypos=1, morphed)),
            AND(fields=(title), KEYWORD(running, querypos=1, morphed))))
  AND(fields=(body), KEYWORD(dog, querypos=2, morphed)))

EXPLAIN QUERY ... option format=dot позволяет отобразить дерево выполнения заданного полнотекстового запроса в иерархическом формате, подходящем для визуализации с помощью существующих инструментов, таких как https://dreampuf.github.io/GraphvizOnline:

EXPLAIN QUERY graphviz example

‹›
  • SQL
SQL
📋
EXPLAIN QUERY tbl 'i me' option format=dot\G
‹›
Response
EXPLAIN QUERY tbl 'i me' option format=dot\G
*************************** 1. row ***************************
Variable: transformed_tree
   Value: digraph "transformed_tree"
{
0 [shape=record,style=filled,bgcolor="lightgrey" label="AND"]
0 -> 1
1 [shape=record,style=filled,bgcolor="lightgrey" label="AND"]
1 -> 2
2 [shape=record label="i | { querypos=1 }"]
0 -> 3
3 [shape=record,style=filled,bgcolor="lightgrey" label="AND"]
3 -> 4
4 [shape=record label="me | { querypos=2 }"]
}

Просмотр значений факторов совпадения

При использовании ранжировщика выражений можно вывести значения вычисленных факторов с помощью функции PACKEDFACTORS().

Функция возвращает:

  • Значения факторов на уровне документа (таких как bm25, field_mask, doc_word_count)
  • Список каждого поля, которое сгенерировало совпадение (включая lcs, hit_count, word_count, sum_idf, min_hit_pos и т.д.)
  • Список каждого ключевого слова из запроса вместе с их значениями tf и idf

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

‹›
  • SQL
SQL
📋
SELECT id, PACKEDFACTORS() FROM test1 WHERE MATCH('test one') OPTION ranker=expr('1')\G
‹›
Response
             id: 1
packedfactors(): bm25=569, bm25a=0.617197, field_mask=2, doc_word_count=2,
    field1=(lcs=1, hit_count=2, word_count=2, tf_idf=0.152356,
        min_idf=-0.062982, max_idf=0.215338, sum_idf=0.152356, min_hit_pos=4,
        min_best_span_pos=4, exact_hit=0, max_window_hits=1, min_gaps=2,
        exact_order=1, lccs=1, wlccs=0.215338, atc=-0.003974),
    word0=(tf=1, idf=-0.062982),
    word1=(tf=1, idf=0.215338)
1 row in set (0.00 sec)
Last modified: August 28, 2025