Полнотекстовый поиск
Простой поиск
Для простого поиска в тексте можно использовать оператор LIKE
.
Он ищет записи, в которых встречается слово, соответствующее заданному шаблону (pattern
).
В pattern
можно использовать _
— для обозначения любого одиночного символа и %
— для обозначения любой последовательности символов.
Например, шаблон me_t
будет соответствовать meet
, meat
, melt
и так далее, а шаблон %tion
будет соответствовать tion
, condition
, creation
и так далее (вместо %
может быть любая последовательность символов, в том числе и их отсутствие).
Примеры команд для простого поиска в Reindexer:
query := db.Query("items").
Where("field", reindexer.LIKE, "pattern")
SELECT * FROM items WHERE fields LIKE 'pattern'
Чтобы с помощью LIKE
искать информацию в нескольких полях, нужно использовать несколько операторов, например:
SELECT * FROM items WHERE fields LIKE 'pattern' OR titles LIKE 'second_pattern'
Это ведет к увеличению нагрузки на сервер. Кроме того, LIKE
использует метод полного сканирования поля. С учетом этого его рекомендуется использовать для отладки или внутри запросов с другими хорошими выборочными условиями.
Оператор
LIKE
поддерживается в запросах к текстовым полям с типом значенияstring
с типами индексовhash
иtree
. Для индексов типаtext
он неприменим.
Для полнотекстового поиска с высокой скоростью рекомендуется использовать полнотекстовые индексы.
Полнотекстовый поиск
В Reindexer имеется встроенный движок fast
для полнотекстового поиска на базе суффиксного массива (suffixarray
) и алгоритма инвертированных индексов.
Он характеризуется минимальными требованиями по памяти, а также поддержкой морфологии, опечаток, транслита и поиска с неправильной раскладкой клавиатуры.
Размер индекса обычно равен 30-80% от исходного текста, но может выходить за пределы этого диапазона в некоторых случаях.
Полнотекстовый поиск реализован с поддержкой стеммеров для расширения поисковых запросов за счет естественного поиска слова по его основе. Например, запросу users
также будет соответствовать значение user
.
Стеммеры специфичны для каждого языка, поэтому при их использовании необходимо указывать язык. По умолчанию в Reindexer используются русский и английский языки (ru
, en
).
Полнотекстовый поиск возможен по text
-индексам для одиночных полей типа string
и по композитным (composite
) text
-индексам, объединяющим несколько string-полей.
Примеры объявления структур с индексами на Go:
type Item struct {
ID int64 `reindex:"id,,pk"`
Description string `reindex:"description,text"` // Индекс `description` с поддержкой полнотекстового поиска
}
type Item struct {
ID int64 `reindex:"id,,pk"`
Name string `reindex:"name,-"`
Description string `reindex:"description,-"`
_ struct{} `reindex:"name+description=text_search,text,composite` // Композитный (составной) индекс с псевдонимом`text_search` для полнотекстового поиска по полям `name` и `description`
}
Также в Reindexer имеется встроенный триграммный движок для полнотекстового поиска (fuzzy
), работающий с индексами типа fuzzytext
.
Он дает лучшее качество поиска, но при этом работает медленнее и требует больше памяти.
На данный момент он находится в экспериментальном статусе. В большинстве случаев для организации полнотекстового поиска лучше использовать движок fast
Особенности реализации полнотекстового поиска в Reindexer
- Полнотекстовый поиск в Reindexer нечувствителен к регистру.
- Исходный текст при поиске токенизируется до набора слов.
- Под словом понимается последовательность любых букв в кодировке UTF-8, цифр или символов:
+
,-
,/
. - Первый символ в слове — буква или цифра.
- Результаты выполнения запросов для полнотекстового поиска сортируются по релевантности, если не задан другой критерий сортировки.
Запросы к полнотекстовым индексам
Запросы к полнотекстовым индексам строятся через обычный интерфейс запросов по имени индекса. Примеры:
query := db.Query ("items").
Match ("name+description","text query","<stemmers>") // Запрос для поиска по индексам name+description
SELECT * FROM items WHERE description = '*tor'
Или с использованием псевдонимов (пример на Go):
query := db.Query ("items").
Match ("text_search","text query","<stemmers>") // Запрос для поиска по композитному (составному) индексу с псевдонимом`text_search` для полнотекстового поиска по индексам `name` и `description`
Здесь:
text query
— запрос, по которому будет происходить полнотекстовый поиск,<stemmers>
— список стеммеров, с использованием которых будет выполняться поиск по основе слова. Значение по умолчанию —"en","ru"
.
Кроме того, запросы к полнотекстовым индексам можно комбинировать с условиями выбора по другим полям неймспейса.
В таком случае все условия выбора по другим полям (where-фильтры) выполнятся после поиска по полнотекстовым индексам.
Примеры:
query := db.Query ("items").
Match ("description","text query"). // Запрос для поиска по индексу `description`
WhereInt("year",reindexer.GT,2010) // Условие выбора по полю `year`
SELECT * FROM items WHERE description = '*tor' AND id > 3
Результат каждого запроса при полнотекстовом поиске имеет степень (ранг) соответствия (совпадения).
Это — целое число, диапазон значений которого от 0
(минимальное соответствие запросу) до 255
(максимальное соответствие).
При взаимодействии с Reindexer через Go-коннектор для получения ранга полученного результата можно воспользоваться методом итератора запроса Rank()
. Пример:
query := db.Query("items").
Match("description", "*text query*")
iterator := query.Exec()
defer iterator.Close()
if err := iterator.Error(); err != nil {
panic(err)
}
for iterator.Next() {
rank := iterator.Rank() // Получение степени соответствия соответствия результата запросу
fmt.Println(rank)
}
В SQL также есть функция rank()
, которая позволяет получить ранг в ответ на запрос или может быть использована в сортировке. Пример:
SELECT rank(), * FROM items WHERE description = '*tor' ORDER BY 'rank()'
Формат запросов для полнотекстового поиска в Reindexer
В Reindexer используется следующий формат запросов для полнотекстового поиска:
query := [@[+]field[^boost][,field2[^boost]]] [=][*]term1[*][~][^boost] [+|-][*]term2[*][~][@][^boost] ...
Выбор полей при поиске по составным индексам
В фрагменте [@[+]field[^boost][,field2[^boost]]]
указываются параметры для полнотекстового поиска по композитным (составным) индексам. Здесь используются следующие специальные символы:
Символ | Значение | Пояснение, особенности использования |
---|---|---|
@ |
Указывает на список индексов для поиска. | Индексы перечисляются через запятую. |
* |
Поиск будет происходить по всем индексам неймспейса. | |
^boost |
Буст (увеличение) значения ранга совпадения для индекса, участвующего в поиске (по умолчанию равно 1 ). |
При его указании (например field^x ) совпадения, найденные для индекса field , будут вносить в x раз больший вклад в итоговый ранг документа в выдаче, чем совпадения для других индексов, без ^x . Параметр используется при полнотекстовом поиске по композитным текстовым индексам. |
+ |
Совпадения для индекса обязательно используются при вычислении итогового ранга совпадения слова. | Если индекс не помечен символом + , его использование в вычислении итогового ранга зависит от степени совпадения для других индексов: при наилучшем совпадении в этом индексном поле оно используется как наилучшее и при этом итоговый ранг совпадения слова вычисляется с его использованием и с использованием рангов совпадений для других индексов, помеченных + . Результаты для индексов, не помеченных + (если они не наилучшее) не используется при вычислении итогового ранга совпадения слова. Например, для паттерна text=‘@+field1,field2 word’ , если наилучшее совпадение будет для field1 , для вычисления итогового ранга будет использовано только оно (field2 не учитывается, т.к. индекс не помечен + ). Если же наилучшее совпадение будет для field2 , при вычислении общего ранга будут будут использованы совпадения слова из обоих индексных полей. Если оба индекса пометить + , совпадения для них будут учитываться при вычислении итогового ранга в любом случае. |
Вычисление итогового ранга слова при полнотекстовом поиске по нескольким индексам
Итоговый ранг совпадения слова при полнотекстовом поиске по нескольким индексным полям вычисляется следующим образом:
- Значения рангов совпадения слова из нескольких индексных полей сортируются от большего к меньшему.
- Каждое из этих значений умножается на коэффициент
SumRanksByFieldsRatio
, возведенный в степеньномер значения минус 1
(отсчет значений ведется от1
). КоэффициентSumRanksByFieldsRatio
задается в конфигурации для каждого полнотекстового индекса. Таким образом, чем меньше ранг совпадения слова для индекса, тем меньше его вклад в итоговый ранг.
Пример расчета итогового ранга слова
Дано:
Коэффициент SumRanksByFieldsRatio
для полнотекстовых индексов равен 0.5 (в примере обозначим его как K
).
Совпадения для слова найдены в 3 индексных полях с рангами:
- R1=20,
- R2=90,
- R3=40.
Суммарный ранг будет следующим:
R=(K^0)*R2 + (K^1)*R3 + (K^2)*R1 = 1*90 + 0.5*40 + 0.5*0.5*20 = 115
Минимальное значение этого коэффициента SumRanksByFieldsRatio
равно 0.0
, что означает использовать только одно максимальное совпадение. Максимальное значение — 1
(простая сумма рангов совпадений из всех полей).
По умолчанию значение SumRanksByFieldsRatio
для полнотекстового индекса равно 0
.
Шаблоны (паттерны) для слов при полнотекстовом поиске
В фрагменте запроса [=][*]term1[*][~][^boost] [+|-][*]term2[*][~][@][^boost]
указываются шаблоны (паттерны) слов для полнотекстового поиска.
Здесь используются следующие специальные символы:
Символ | Значение | Пояснение, особенности использования |
---|---|---|
* |
Любая последовательность символов. | Например шаблону termina* будут соответствовать значения terminator , terminal и так далее. Символ может использоваться только в начале или в конце слова (term*na , например, недопустимый вариант). Минимальная длина последовательности, соответствующей * — 2 символа. |
~ |
Нечеткое слово с ошибкой согласно словарю опечаток. | Словарь опечаток включает слова с одной возможной ошибкой (опечаткой). Например, шаблону black~ будут соответствовать слова block , blck , blask и т.д. (одна ошибка или опечатка). |
^boost |
Буст (увеличение) значения ранга совпадения для слова при формировании результатов поиска. | Значение по умолчанию равно 1 . |
= |
В результат включаются только точные совпадения для слова, следующего за этим символом. | При поиске не используется транслит, исправления при неверной раскладке, стеммеры. |
Бинарные операторы в запросах для полнотекстового поиска в Reindexer
С помощью бинарных операторов в запросах для полнотекстового поиска указываются паттерны, которые должны обязательно присутствовать или отсутствовать в результатах. В Reindexer используются следующие бинарные операторы:
+
- следующий за оператором паттерн должен присутствовать в найденных документах для их включения в результат поиска,-
- следующий за оператором паттерн не должен присутствовать в найденных документах для их включения в результат поиска.
Экранирование символов в запросах для полнотекстового поиска
Экранирование с помощью \
позволяет добавлять специальные символы +
,-
,@
,*
,^
и ~
в запросы для полнотекстового поиска. Например при наличии в запросе паттерна \*crisis
будет искаться слово *crisis
, а не все слова, заканчивающиеся на crisis
.
Поиск фразы
Операндом может быть фраза. Фраза задается в форме
“word1 word2 …"~N word1, word2, … - слова фразы в заданной последовательности (последовательность “word2 word1” не найдется если ищется “word1 word2”) ~N - максимальное расстояние между словами соседними словами фразы (если не задано, то расстояние принимается за 1)
Синонимы из нескольких слов не поддерживаются во фразе.
Примеры паттернов для полнотекстового поиска в Reindexer
Ниже представлены примеры паттернов для полнотекстового поиска, построенных по рассмотренному формату:
-
termina* -genesis
— поиск документов, включающих слово, начинающееся сtermina
(terminator
,terminal
и т.д.). Документы содержащие словоgenesis
, в результаты поиска включены не будут. -
black~
— поиск документов со словами, соответствующими шаблонуblack
с одной ошибкой или опечаткой. В результаты попадутblock
,blck
,blask
и т.д. -
tom jerry cruz^2
— поиск документов, включающих хотя бы одно из словtom
jerry
илиcruz
. При этом результаты, содержаниеcruz
, будут иметь больший ранг:tom cruz
будет ранжироваться выше, чемtom jerry
. -
fox +fast
— в результаты поиска будут включены варианты, содержание оба слова (fast
должно обязательно присутствовать в документе для включения в результат поиска). -
"one two"
— поиск документов, содержащих фразу"one two"
. -
"one two"~5
— поиск документов, содержащих словаone
иtwo
, которые могут быть расположены на расстоянии до 5 (<5) слов друг от друга. -
@name rush
— поиск документов со словомrush
будет вестись только по индексуname
. -
@name^1.5,* rush
— поиск документов, содержащих словоrush
с бустом ранга совпадения для индексаname
, равным 1.5. -
=windows
— поиск документов, содержащих точное вхождение словаwindows
. -
one -"phrase example"
- поиск документов содержащих словоone
и не содержащих фразу “phrase example”. -
one "phrase example"
- поиск документов содержащих словоone
или фразу “phrase example” или оба операнда. -
+one +"phrase example"
- поиск документов содержащих словоone
и фразу “phrase example”.
Объединение результатов полнотекстового поиска
Reindexer поддерживает объединение результатов нескольких запросов и их сортировку.
Пример на Go:
query := db.Query ("items").
Match ("description","text query1")
q2 := db.Query ("another_items").
Match ("description","text query2")
query.Merge (q2)
iterator = query.Exec ()
// Проверка на наличие ошибки
if err := iterator.Error(); err != nil {
panic(err)
}
// Перебор результатов поиска
for iterator.Next() {
// Получение результатов, их добавление в Iterator, вывод результатов
switch elem := iterator.Object().(type) {
case Item:
fmt.Printf ("%v,rank=%d\n",*elem,iterator.Rank())
case AnotherItem:
fmt.Printf ("%v,rank=%d\n",*elem,iterator.Rank())
}
}
В Reindexer не поддерживается использование более одного метода
Match()
в одном запросе. Для поиска по нескольким индексным полям используйте композитные индексы или объединяйте результаты нескольких запросов с помощьюMerge()
.
Использование функций для модификации результатов поиска
В Reindexer возможно использование функций выбора для обработки и модификации результатов поиска. С их помощью вы сможете добавлять теги для подсветки результатов и удалять из них лишние символы, адаптируя под введенный запрос.
На данный момент такая возможность доступна только для простых текстовых индексов. Функции не поддерживаются при полнотекстовом поиске по композитным индексам.
Функция highlight
Пример на Go:
highlight(first,second)
Используется для выделения текстовой области из состава поискового запроса.
Принимает 2 обязательных аргумента:
first
— строка, которая будет вставлена перед найденной текстовой областью,second
— строка, которая будет вставлена после найденной текстовой области.
Примеры использования:
b.Query("items").Match("field", "some").Limit(limit).Offset(offset).Functions("field.highlight(<b>,</b>)")
При использовании dsl-query функции highlight
должна передаваться в теле запроса в поле select_functions
:
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/fulltext/query' \
--header 'Content-Type: application/json' \
--data-raw '{
"namespace": "items",
"offset": 0,
"limit": 3,
"sort": {
"field": "",
"desc": true
},
"filters": [
{
"Field": "field",
"Cond": "EQ",
"Value": [
"some"
]
}
],
"select_functions": [
"field.highlight(<b>,</b>)"
]
}'
При выполнении запросов из приведенных выше примеров в результатах поиска по запросу “some” слово будет обрамлено html-тегами <b> </b>
.
Результат выполнения запроса: “some”.
Функция snippet
Формат функции:
snippet(first,second,third,fourth,fifth,sixth)
Используется для выделения текстовой области из состава поискового запроса и удаления окружающего ее текста.
Функция принимает 6 аргументов:
first
— строка, которая будет вставлена перед найденной текстовой областью;second
— строка, которая будут вставлена после найденной текстовой области;third
— количество символов окружающего текста, которые будут размещены в результатах поиска перед найденной текстовой областью из запроса;fourth
— количество символов окружающего текста, которые будут размещены в результатах поиска после найденной текстовой области из запроса;fifth
(необязательный, по умолчанию какой-либо символ отсутствует) — разделитель перед найденной текстовой областью;sixth
(необязательный, значение по умолчанию — пробел) — разделитель после найденной текстовой области.
Пример использования:
b.Query("items").Match("field", "text").Limit(limit).Offset(offset).Functions("field.snippet(<b>,</b>,2,0)")
При использовании dsl-query функции snippet
должна передаваться в теле запроса в поле select_functions
:
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/fulltext/query' \
--header 'Content-Type: application/json' \
--data-raw '{
"namespace": "items",
"offset": 0,
"limit": 3,
"sort": {
"field": "",
"desc": true
},
"filters": [
{
"Field": "field",
"Cond": "EQ",
"Value": [
"text"
]
}
],
"select_functions": [
"field.snippet(<b>,</b>,2,0)"
]
}'
При выполнении запросов из приведенных выше примеров слово “text” будет обрамлено html-тегами <b> </b>
и перед ним будет добавлено 2 символа из окружающего текста.
Например, если для индекса field
будет найдено сочетание some text
, результат выполнения запроса получится: “e text”.
Функция snippet_n
Формат функции:
snippet_n(first,second,third,fourth[,pre_delim='value1'][,post_delim='value2'][,with_area=0][,left_bound='value3'][,right_bound='value4'])
Используется для выделения текстовой области из состава поискового запроса и удаления окружающего ее текста. Отличается от snippet наличием именованных аргументов. Функция принимает 9 аргументов: 4 позиционных (обязательные) и 5 именованных (необязательные). Именованные аргументы следуют в любом порядке после позиционных:
first
— строка, которая будет вставлена перед найденной текстовой областью;second
— строка, которая будут вставлена после найденной текстовой области;third
— количество символов окружающего текста, которые будут размещены в результатах поиска перед найденной текстовой областью из запроса;fourth
— количество символов окружающего текста, которые будут размещены в результатах поиска после найденной текстовой области из запроса;pre_delim
— (по умолчанию какой-либо символ отсутствует) — разделитель перед найденной текстовой областью;post_delim
— (значение по умолчанию — пробел) — разделитель после найденной текстовой области;with_area
— (значение по умолчанию — 0, не выводить) Выводить начало и конец фрагмент относительно начала документа в символах (UTF-8) в формате [B,E] послеpre_delim
;left_bound
— (значение по умолчанию — пустая строка, параметр не используется в вычислениях) Набор символов UTF-8. Началом фрагмента выступает символ из строкиleft_bound
если он встретится раньшеthird
;right_bound
— (значение по умолчанию — пустая строка, параметр не используется в вычислениях) Набор символов UTF-8. Концом фрагмента выступает символ из строкиright_bound
если он встретится раньшеfourth
.
Строковые параметры задаются в одинарных кавычках. Имена параметров задаются либо без кавычек либо в двойных кавычках. Числовые параметры задаются либо без кавычек либо в одинарных кавычках.
Пример использования:
text: “some text string”
b.Query("items").Match("text", query).Limit(limit).Offset(offset).Functions("text.snippet_n('<b>','</b>',2,2,pre_delim='{',post_delim='}',with_area=1)")
result: “{[3,11]e text s}”
b.Query("items").Match("text", query).Limit(limit).Offset(offset).Functions("text.snippet_n('<b>','</b>',5,5,pre_delim='{',post_delim='}',left_bound='o',right_bound='i')")
result: “{me text str}”
При использовании dsl-query функции snippet_n
должна передаваться в теле запроса в поле select_functions
:
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/fulltext/query' \
--header 'Content-Type: application/json' \
--data-raw '{
"namespace": "items",
"offset": 0,
"limit": 3,
"sort": {
"field": "",
"desc": true
},
"filters": [
{
"Field": "field",
"Cond": "EQ",
"Value": [
"text"
]
}
],
"select_functions": [
"field.snippet_n(<b>,</b>,2,0,pre_delim=!,post_delim=!)"
]
}'
При выполнении запросов из приведенных выше примеров слово “text” будет обрамлено html-тегами <b> </b>
и перед ним будет добавлено 2 символа из окружающего текста.
Например, если для индекса field
будет найден документ some text
, результат выполнения запроса получится: “!e text!”.
Поиск с учетом ошибок и опечаток
В Reindexer реализован алгоритм поиска совпадений при сравнении поискового запроса и результатов поиска с удаленными из них символами.
Алгоритм является языконезависимым и работает со словами просто как с наборами символов.
Общее количество символов, которые могут быть удалены из обоих слов (запрос и результат поиска), настраивается с помощью параметра полнотекстового индекса MaxTypos
.
Из каждого слова при сравнении может быть удалено до MaxTypos/2
символов с округлением до большего значения.
Не рекомендуется задавать значение
MaxTypos
больше 2, т.к. это существенно увеличивает потребление оперативной памяти и снижает скорость поиска.
Для более точной настройки работы алгоритма можно использовать TyposDetailedConfig
.
Он содержит следующий набор параметров:
-
MaxMissingLetters
. Максимальное количество символов, которое может быть удалено из первоначального поискового терма для получения результирующего слова. Например, задавMaxMissingLetters = 0
иMaxTypos = 2
можно получить поведение, при котором допустима либо только замена одного символа, либо только добавления одной буквы к исходному терму. Заданное значение будет учитывать ограничения, наложенныеMaxTypos
. Диапазон значений: [-1,2]. -
MaxExtraLetters
. Максимальное количество символов, которое может быть добавлено к первоначальному поискового терма для получения результирующего слова. Например, задавMaxMissingLetters = 0
,MaxExtraLetters = 0
иMaxTypos = 2
можно получить поведение, при котором допустима только замена одного символа в терме, но не разрешено добавление/удаление символов. Заданное значение будет учитывать ограничения, наложенныеMaxTypos
. Диапазон значений: [-1,2]. -
MaxTypoDistance
. Максимальная дистанция между заменяемыми символами при обработке опечатки (актуально только приMaxTypos >= 2
). Например, приMaxTypoDistance = -1
(обзначает отсутвие ограничений по этому параметру) иMaxTypos = 2
запросdword~
сможет найти словаsword
иwords
(в первом случае символ ’d’ был заменён на ’s’, а во втором не только заменён, но и сдвинут в конец итогового слова). В то же время при приMaxTypoDistance = 0
(значение по умолчанию) иMaxTypos = 2
позиции первоначального и итогового символа должны будут совпадать. В результате запросdword~
вернёт только словоsword
, но не вернёт словоwords
. Диапазон возможных значений: [-1,100]. -
MaxSymbolPermutationDistance
. Максимальное расстояние, на которое может быть перемещён один и тот же символ при обработке опечатки. Этот параметр используется в паре сMaxTypoDistance
для ослабления накладываемых им ограничений в случаях перестановки одного и того же символа. Например, сMaxSymbolPermutationDistance = 0
,MaxTypoDistance = 0
иMaxTypos = 2
запросwsord~
не найдёт словоsword
, потому что для такого преобразования требуется заменить 2 буквы в исходном терме, либо же просто поменять их местами. ЗадавMaxSymbolPermutationDistance = 1
(значение по умолчанию), можно как раз получить требуюмую перестановку двух соседних букв, и запросwsord~
вернёт словоsword
. При этомMaxTypoDistance = 0
всё ещё не позволит при такой перестановке заменить одну из этих букв на какую-то другую и, например, по тому же запросу~wsord
не будет возвращено словоdword
.
Принципы работы поиска с учетом ошибок и опечаток при различных значениях MaxTypos
Ниже рассмотрены принципы работы поиска с учетом ошибок и опечаток при различных параметра текстового индекса MaxTypos
:
Значение MaxTypos |
Принципы сравнения | Примеры |
---|---|---|
1 |
Один символ будет удален из поискового запроса или из результата поиска. | black будет соответствовать blaack , если удалить a во втором слове. А black уже не будет соответствовать block . |
2 |
В обоих словах (в поисковом запросе и в результате) может быть удалено по одному символу (2 символа в сумме). | black будет соответствовать blaack , если удалить a во втором слове;black будет соответствовать block , если удалить a в первом слове и o — во втором;black не будет соответствовать blok . |
3 |
Один символ будет удален в одном слове (в поисковом запросе или в результате поиска), и два символа — в другом (3 символа в сумме). | black будет соответствовать block , если удалить ac в первом слове и o — во втором. |
Управление параметрами индексов
В Reindexer реализована возможность управления параметрами индексов. Это возможно при подключении к БД различными способами. Задать конфигурацию индекса можно при его добавлении или изменении.
Ниже представлены примеры конфигурирования индексов для полнотекстового поиска разными способами:
При взаимодействии с БД через Go-коннектор настроить индекс можно при его добавлении (метод db.AddIndex
) или с помощью метода db.UpdateIndex
.
Пример:
...
ftconfig := reindexer.DefaultFtFastConfig()
// Настройка конфигурации
ftconfig.LogLevel = reindexer.TRACE
// Настройка других параметров конфигурации
// ...
// Создание определения индекса
indexDef := reindexer.IndexDef {
Name: "info",
JSONPaths: []string{"description"},
IndexType: "text",
FieldType: "string",
Config: ftconfig,
}
// Добавление индекса с заданной конфигурацией
return db.AddIndex ("items",indexDef)
Пример конфигурирования индекса для полнотекстового поиска по HTTP с помощью UPDATE-метода:
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/namespaces/items/indexes' \
--header 'Content-Type: text/plain' \
--data-raw '{
"name": "info",
"field_type": "string",
"index_type": "text",
"is_pk": false,
"is_array": false,
"is_dense": false,
"is_sparse": false,
"collate_mode": "none",
"sort_order_letters": "",
"expire_after": 0,
"config": {
"bm25_boost": 1,
"bm25_weight": 0.5,
"position_boost": 1,
"position_weight": 0.1,
"distance_boost": 1,
"distance_weight": 0.5,
"enable_kb_layout": true,
"enable_preselect_before_ft": false,
"enable_numbers_search": false,
"enable_translit": true,
"enable_warmup_on_ns_copy": false,
"min_relevancy": 0.05,
"term_len_boost": 1,
"term_len_weight": 0.3,
"max_typos": 2,
"max_typo_len": 15,
"typos_detailed_config": {
"max_typo_distance": 0,
"max_symbol_permutation_distance": 1,
"max_missing_letters": 2,
"max_extra_letters": 2
},
"base_ranking": {
"full_match_proc": 100,
"prefix_min_proc": 50,
"suffix_min_proc": 10,
"base_typo_proc": 85,
"typo_proc_penalty": 15,
"stemmer_proc_penalty": 15,
"kblayout_proc": 90,
"translit_proc": 90,
"synonyms_proc": 95
},
"bm25_config": {
"bm25_k1": 2,
"bm25_b": 0.75,
"bm25_type": "rx_bm25"
},
"max_rebuild_steps": 50,
"max_step_size": 4000,
"merge_limit": 20000,
"stemmers": [
"en",
"ru"
],
"log_level": 0,
"extra_word_symbols": "-/+",
"full_match_boost": 1,
"partial_match_dercrease": 15,
"sum_ranks_by_fields_ratio": 0,
"fields": [],
"synonyms": [
{
"tokens": [],
"alternatives": []
}
]
},
"json_paths": [
"info"
]
}'
Список параметров индекса полнотекстового поиска:
Параметр | Описание | Тип значения | Значение по умолчанию | Допустимые значения/диапазон значений | json-paths параметра |
---|---|---|---|---|---|
Bm25Boost |
Ранг bm25. Определяет степень релевантности документа поисковому запросу | float | 1.0 | 0.0 - 10.0 | bm25_boost |
Bm25Weight |
Вес ранга bm25 индекса в суммарном ранге. При значении 0 bm25 ранг индекса не влияет на суммарный ранг, а при значении 1 — влияет на 100%. |
float | 0.1 | 0.0 - 1.0 | bm25_weight |
DistanceBoost |
Коэффициент влияния на ранг расстояния между словами запроса в найденном документе | float | 1 | 0.0 - 10.0 | distance_boost |
DistanceWeight |
Вес коэффициента DistanceBoost в суммарном ранге. При значении 0 DistanceWeight не влияет на суммарный ранг, при значении 1 — влияет на 100% |
float | 0.5 | 0.0 - 1.0 | distance_weight |
TermLenBoost |
Коэффициент влияния на ранг длины поискового запроса | float | 1 | 0.0 - 10.0 | term_len_boost |
TermLenWeight |
Вес коэффициента TermLenBoost в суммарном ранге. Диапазон значений: от 0 до 1 . При значении 0 параметр не влияет на суммарный ранг, при значении 1 — влияет на 100% |
float | 0.3 | 0.0 - 1.0 | term_len_weight |
PositionBoost |
Коэффициент влияния на ранг близости расположения слов из поискового запроса к началу документа | float | 1.0 | 0.0 - 10.0 | position_boost |
PositionWeight |
Вес коэффициента PositionBoost в суммарном ранге. При значении 0 параметр не влияет на суммарный ранг, при значении 1 — влияет на 100% |
float | 0.1 | 0.0 - 1.0 | position_weight |
FullMatchBoost |
Коэффициент влияния на ранг полного совпадения поискового запроса с документом | float | 1.1 | 0.0 - 10.0 | full_match_boost |
PartialMatchDecrease |
Коэффициент влияния длины слова на релевантность. При расчете релевантности используется формула PartialMatchDecrease * (количество несовпадающих символов) / (количество совпадающих символов) |
int | 15 | 0 - 100 | partial_match_decrease |
MinRelevancy |
Минимальная релевантность, при которой документ попадет в поисковую выдачу. При значении параметра 0 в результат поиска будут добавлены все найденные документы, при значении 1 — только документы с релевантностью >=100% |
float | 0.05 | 0.0 - 1.0 | min_relevancy |
MaxTypos |
Максимальное количество ошибок (опечаток) в слове. Подробнее — в разделе «Поиск с учетом ошибок и опечаток» (#typos_search) | int | 2, если не задан параметр MaxTyposInWord , в противном случае — 2*MaxTyposInWord |
0 - 4 | max_typos |
MaxTyposInWord |
Устаревший параметр. Вместо него следует использовать MaxTypos. Максимальное количество опечаток в каждом слове: в поисковом запросе и в результате поиска. Не может использоваться совместно с параметром MaxTypos . При значении N из обоих слов (из запроса и результата) будет удалено по N символов. Не рекомендуется задавать значение параметра больше 2 , т.к. это существенно увеличивает использование оперативной памяти и снижает скорость поиска |
int | 0 | 0 - 2 | max_typos_in_word |
FtTyposDetailedConfig |
Расширенные параметры настройки алгоритма поиска с опечатками | []Struct | typos_detailed_config Описание полей структуры |
||
MaxTypoLen |
Максимальная длина слова с опечаткой (ошибкой) в поисковом запросе, для которого будет выполняться поиск вариантов. | int | 15 | 0 - 100 | max_typo_len |
MaxRebuildSteps |
Максимальное количество шагов (операций вставки, модификации) без перестроения индекса. Чем меньше этот параметр, тем медленнее происходит фиксация изменений. Оптимальное значение параметра — 15 |
int | 50 | 1 - 500 | max_rebuild_steps |
MaxStepSize |
Максимальное количество уникальных слов в каждом шаге. Чем больше шагов, тем медленнее работает поиск, но быстрее происходит динамическое перестроение индекса и наоборот. | int | 4000 | max_step_size | |
MergeLimit |
Максимальное количество релевантных документов, которое будет учтено движком при выполнении запроса. Увеличение этого параметра позволяет улучшить результаты поиска по высокочастотным словам, но снижает скорость поиска. | int | 20000 | 1 - 0x1FFFFFFF | merge_limit |
Stemmers |
Список стеммеров, с использованием которых будет происходить полнотекстовый поиск. | []string | “en”, “ru” | “en”, “ru”, “nl”, “fin”, “de”, “da”, “fr”, “it”, “hu”, “no”, “pt”, “ro”, “es”, “sv”, “tr” | stemmers |
EnableTranslit |
Включение/отключение использования транслитерации русских слов при поиске. Например слово luntik будет соответствовать слову лунтик |
bool | true | true, false | enable_translit |
EnableKbLayout |
Включение/отключение поддержки неправильной раскладки клавиатуры. Например слово keynbr будет соответствовать слову лунтик |
bool | true | true, false | enable_kb_layout |
EnableNumbersSearch |
Включение/отключение поиска чисел по строке. Например, по слову «пятьсот» при включенном поиске должны находиться документы, содержащие «500». Работает только в одну сторону и только для русского языка: по запросу «500» слово «пятьсот» найдено не будет | bool | false | true, false | enable_numbers_search |
EnablePreselectBeforeFt |
Флаг изменения порядка выборки для планировщика. По умолчанию (enable_preselect_before_ft=false ) в первую очередь происходит выборка из полнотекстового индекса, а дальше фильтрация по компараторам (если в запросе есть ещё какие-то условия). Если этот флаг включен, то в первую очередь выполняется поиск по всем неполнотекстовым индексам из запроса, а дальше полученная выборка будет использована в полнотекстовом индексе. Это может быть полезно в ситуациях, когда есть много документов, удовлетворяющих полнотекстовому DSL (больше, чем merge limit ), но при этом среди всех этих документов нет ни одного, который бы удовлетворял остальным условиям запроса по другим полям. |
bool | false | true, false | enable_preselect_before_ft |
StopWords |
Список стоп-слов, которые будут игнорироваться в запросах и результатах при полнотекстовом поиске | []string | *Список по умолчанию для русского языка *Список по умолчанию для английского языка | stop_words | |
SumRanksByFieldsRatio |
Коэффициент для вычисления вклада индекса в итоговый ранг при полнотекстовом поиске по нескольким индексам. | float | 0.0 | sum_ranks_by_fields_ratio | |
LogLevel |
Уровень логирования для движка полнотекстового поиска. | int | 0 | 0 - 5 | log_level |
ExtraWordSymbols |
Список символов, которые будут восприниматься как часть слова. Все остальные символы будут оцениваться как разделители. | string | “-/+” | extra_word_symbols | |
FieldsCfg |
Конфигурация для отдельного поля. Перекрывает общие параметры конфигурации индекса. Содержит параметры: FieldName , Bm25Boost , Bm25Weight , TermLenBoost , TermLenWeight , PositionBoost , PositionWeight . |
[]struct | пустая структура | fields | |
EnableWarmupOnNsCopy |
Включить автоматическое прогревание (перестроение) индекса после транзакции, выполнившей копирование неймспейса. Может использоваться для ускорения транзакций и перестроения индекса. | bool | false | true, false | enable_warmup_on_ns_copy |
Synonyms |
Список синонимов, которые будут использоваться при полнотекстовом поиске. | []string | пустая структура | synonyms | |
MaxAreasInDoc |
Максимальное количество областей на документ, «подсвеченных» с помощью в функции snippet() или highlight() . |
int | 5 | -1 - 1000 Значение -1 означает отсутствие ограничений по количеству «подсвеченных» областей |
max_areas_in_doc |
MaxTotalAreasToCache |
Максимальное число «подсвеченных» с помощью snippet() или highlight() областей, при котором результаты запроса к полнотекстовому индексу могут быть закешированы. |
int | -1 | -1 - 1000 Значение -1 означает отсутствие ограничений |
max_total_areas_to_cache |
Optimization |
Способ оптимизации индекса: по памяти или CPU. | string | “Memory” | “Memory” “CPU” |
optimization |
FtBaseRanking |
Структура с настройками параметров релевантности слова в разных формах | []Struct | base_ranking Описание полей структуры |
||
Bm25Config |
Структура с настройками функции оценки релевантности документа | []Struct | bm25_config Описание полей структуры |
Структура с расширенными настройками алгоритма поиска с опечатками (FtTyposDetailedConfig
)
Параметр | Описание | Тип значения | Значение по умолчанию | Допустимые значения/диапазон значений | json-paths параметра |
---|---|---|---|---|---|
MaxMissingLetters |
Максимальное количество символов, которое может быть удалено из первоначального поискового терма для получения результирующего слова. Подробнее о поиске с ошибками и опечатками. | int | 2 | -1 - 2 | typos_detailed_config.max_missing_letters |
MaxExtraLetters |
Максимальное количество символов, которое может быть добавлено к первоначальному поисковому терму для получения результирующего слова. Подробнее о поиске с ошибками и опечатками. | int | 2 | -1 - 2 | typos_detailed_config.max_extra_letters |
MaxTypoDistance |
Максимальная дистанция между заменяемыми символами при обработке опечатки (актуально только при MaxTypos >= 2 ). Подробнее о поиске с ошибками и опечатками. |
int | 0 | -1 - 100 | typos_detailed_config.max_typo_distance |
MaxSymbolPermutationDistance |
Максимальное расстояние, на которое может быть перемещён один и тот же символ при обработке опечатки. Подробнее о поиске с ошибками и опечатками. | int | 1 | -1 - 100 | typos_detailed_config.max_symbol_permutation_distance |
Структура с настройками параметров базовой релевантности слова в разных формах (FtBaseRanking
)
Параметр | Описание | Тип значения | Значение по умолчанию | Допустимые значения/диапазон значений | json-paths параметра |
---|---|---|---|---|---|
FullMatch |
Релевантность полного совпадения | int | 100 | 0 - 500 | full_match_proc |
PrefixMin |
Минимальная релевантность при применении поиска по префиксам (см. PartialMatchDecrease ) |
int | 50 | 0 - 500 | prefix_min_proc |
SuffixMin |
Минимальная релевантность при применении поиска по суффиксам (см. PartialMatchDecrease ) |
int | 10 | 0 - 500 | suffix_min_proc |
Typo |
Базовая релевантность при поиске с опечатками | int | 85 | 0 - 500 | base_typo_proc |
TypoPenalty |
Дополнительный штраф за перестановку каждого слова в алгоритме опечаток. Минимальная релевантность после применения штрафов будет не меньше 1. | int | 15 | 0 - 500 | typo_proc_penalty |
StemmerPenalty |
Штраф за варианты, созданные стеммером. Минимальная релевантность после применения штрафов будет не меньше 1. | int | 15 | 0 - 500 | stemmer_proc_penalty |
Kblayout |
Релевантность слова в неправильной раскладке клавиатуры | int | 90 | 0 - 500 | kblayout_proc |
Translit |
Релевантность слова, написанного транслитом | int | 90 | 0 - 500 | translit_proc |
Synonyms |
Релевантность синонимов | int | 95 | 0 - 500 | synonyms_proc |
Базовая оценка релевантности документа
Для базовой оценки релевантности документа используются следующие алгоритмы:
bm25
rx_bm25
word_count
Формула для вычисления bm25
:
R = (log(totalDocCount / (matchedDocCount + 1)) + 1) * termCountInDoc / wordsInDoc * (k1 + 1.0) / (termCountInDoc / wordsInDoc + k1 * (1.0 - b_ + b_ * wordsInDoc / avgDocLen))
Формула для вычисления rx_bm25
:
R = (log(totalDocCount / (matchedDocCount + 1)) + 1) * termCountInDoc * (k1 + 1.0) / (termCountInDoc + k1 * (1.0 - b_ + b_ * wordsInDoc / avgDocLen))
Формула для вычисления word_count
:
R = termCountInDoc
totalDocCount
- общее колличество документовmatchedDocCount
- число документов в которых встретилась подформа исходного слова запросаtermCountInDoc
- число слов в документе, которые соответствуют подформе исходного слова запросаwordsInDoc
- число слов в документеk1
- Свободный коэффициент. Данный параметр задает порог насыщения по частоте терма. Чем больше коэффициент, тем выше порог и меньше скорость насыщения.b
- Свободный коэффициент. Данный параметр усиливает отношение длины документа к средней длине документа.
Структура с настройками функции оценки релевантности документа (Bm25Config
)
Параметр | Описание | Тип значения | Значение по умолчанию | Допустимые значения/диапазон значений | json-paths параметра |
---|---|---|---|---|---|
Bm25k1 |
Коэффициент k1 в формуле для расчета bm25 (Используется только если выбран тип rx_bm25, bm25). | float | 2.0 | >= 0 | bm25_k1 |
Bm25b |
Коэффициент b в формуле для расчета bm25 (Используется только если выбран тип rx_bm25, bm25). | float | 0.75 | 0-1.0 | bm25_b |
Bm25Type |
Идентификатор формулы по которой происходит расчет релевантности Описание | string | “rx_bm25” | “rx_bm25” “bm25” “word_count” |
bm25_type |
Производительность и использование памяти при полнотекстовом поиске
В Reindexer по умолчанию реализована «ленивая» индексация: операция Upsert
не перестраивает индекс.
Он перестраивается при первом запросе к text
полю.
При «ленивой» индексации используется несколько потоков, что обеспечивает эффективное расходование ресурсов современного многоядерного процессора.
В результате удается добиться очень высокой скорости индексации. На современном оборудовании она равна ~50Мб/сек.
Но при слишком больших размерах текста «ленивая» индексация может серьезно замедлить первый запрос к текстовому индексу.
Избежать этого эффекта поможет прогревание индекса с помощью «фиктивного» запроса после последней операции Upsert
.
Пример такого запроса:
query := db.Query("items").
Match("description","*text").
Limit(1) // Запрос для поиска по индексу `description` с минимальной выдачей
SELECT * FROM items WHERE descripton ='*text' LIMIT 1