MyLab.Search.SearcherClient
1.10.9
dotnet add package MyLab.Search.SearcherClient --version 1.10.9
NuGet\Install-Package MyLab.Search.SearcherClient -Version 1.10.9
<PackageReference Include="MyLab.Search.SearcherClient" Version="1.10.9" />
paket add MyLab.Search.SearcherClient --version 1.10.9
#r "nuget: MyLab.Search.SearcherClient, 1.10.9"
// Install MyLab.Search.SearcherClient as a Cake Addin #addin nuget:?package=MyLab.Search.SearcherClient&version=1.10.9 // Install MyLab.Search.SearcherClient as a Cake Tool #tool nuget:?package=MyLab.Search.SearcherClient&version=1.10.9
MyLab.Search.Searcher
Ознакомьтесь с последними изменениями в журнале изменений.
Обзор
Как это работает?
MyLab.Search.Searcher
(далее Searcher
) - сервис поиска по предварительно сконфигурированным условиям.
Обобщённый и упрощённый алгоритм работы поиска с применением Searcher
выглядит следующим образом:
- клиент делает запрос, используя высокоуровневые термины для описания требований к поиску: свободный текстовый запрос, литеральный идентификатор фильтра, сортировки, параметры пейджинга и т.д.;
Searcher
по литеральным идентификаторам находит предустановленные json-файлы с фильтрами и сортировками;- из полученных данных строится запрос в
Elasticsearch
; - с небольшими модификациями, результат передаётся клиенту.
Searcher
позволяет осуществлять поиск по разным сущностям. Разделение достигается за счёт абстракции индекс
. Index
указывается в адресе при поиске. Для каждого индекса могут быть свои настройки и ресурсы (фильтры и сортировки).
Строка поиска
Для поиска Searcher
получает в том числе и строку поиска. Эта строка может содержать несколько текстовых конструкций, используя которые Searcher
дополняет запрос в Elasticsearch
необходимыми условиями.
Особенности:
- чем ближе слово к началу строки поиска, тем больший приоритет у найденных по нему записей;
- поддерживаются числовые условия:
123
- значение любого из числовых полей эквивалентно значению123
;<123
- значение любого из числовых полей меньше123
;>123
- значение любого из числовых полей больше123
;123-321
- значение любого из числовых полей больше или равно123
и меньше или равно321
;
- поддерживаются условия по дате:
- как дата определяется литерал в формате:
MM/dd/yyyy
dd.MM.yyyy
ddMMyy
, гдеyy
- двузначный год текущего столетия
01.02.2003
- значение любого из полей даты и времени в диапазоне больше или равно01.02.2003 00:00:00
и меньше02.02.2003 00:00:00
<01.02.2003
- значение любого из полей даты и времени меньше01.02.2003 00:00:00
>01.02.2003
- значение любого из полей даты и времени больше01.02.2003 00:00:00
01.02.2003-03.02.2003
- значение любого из полей даты и времени в диапазоне больше или равно01.02.2003 00:00:00
и меньше03.02.2003 00:00:00
- как дата определяется литерал в формате:
Токен поиска
Применение токена поиска
Возможно применение Searcher
в открытом виде, когда конечная точка для поиска доступна публично для доступа клиента напрямую без авторизации. В этом случае следует использовать токен поиска.
Токен поиска предназначен для того, чтобы ограничить объём данных результатов поиска для клиента. Токен содержит информацию о том, к каким index
пользователь имеет доступ и какие фильтры с какими аргументами при этом необходимо обязательно.
Например, отфильтровать для пользователя только его заявки, чтобы поиск происходил только по ним. Для этого при запросе токена поиска, необходимо будет указать идентификатор фильтра поиска заявок и указать идентификатор пользователя, как аргумент (при этом фильтр поиска самописный для конкретного случая и должен содержать аргумент, подразумевающий указание идентификатора пользователя).
Работа с токеном поиска выглядит следующим образом:
- авторизованный клиент запрашивает токен поиска у сервера;
- сервер определяет пользователя и принимает решение об ограничении области данных при поиске. Для этого формируется запрос токена поиска со списком применяемых фильтров и аргументов для них;
- сервер запрашивает токен поиска с указанными параметрами у
Searcher
по приватному каналу; Searcher
формирует и возвращает токен поиска серверу;- сервер выдаёт токен поиска клиенту;
- клиент обращается к
Searcher
по публичному каналу, передавая интересующие параметры поиска и токен поиска; Searcher
осуществляет поиск с применением, в том числе, указанных в токене фильтров, модифицированных соответствующими аргументами.
Особенности при работе с токеном поиска:
- при создании токена:
- следует указать все
index
, к которым пользователь должен иметь доступ; - не проверяется наличие указанных фильтров и аргументов в этих фильтрах;
- дата+время экспирации добавляется только если в конфигурации указано время жизни токена поиска;
- будет ошибка
423 (Locked)
, еслиSearcher
не был сконфигурирован для работы с токенами;
- следует указать все
- при поиске с токеном:
- будет ошибка
400 (BadRequest)
, еслиSearcher
не был сконфигурирован для работы с токенами; - будет ошибка
400 (BadRequest)
, если токен не указан или не прошёл проверку; - будет осуществлена проверка актуальности токена только если в конфигурации указано время жизни выдаваемого токена (т.е. если токен создаётся без указания даты+времени экспирации, то она и не проверяется);
- токен передаётся в заголовке
X-Search-Token
.
- будет ошибка
Запрос токена доступа
Запрос токена доступа перечисляет индексы, к которым должен иметь доступ клиент и ограничения по выборкам для этих индексов.
Структура запроса:
indexes
[] - настройки индексов:id
- литеральный идентификатор индекса;filters[]
(опционально) - фильтры, применяющиеся для текущего индекса;id
- идентификатор фильтра;args
(опционально) - аргументы фильтра:[any]
- именованные параметры - аргументы для фильтра.
Wildcard
(*
) - идентификатор индекса:
- доступ клиента к любому индексу;
- фильтры из узла применяются к индексам, для которых нет точно указанного по имени узла настроек в токене.
Пример запроса:
{
"indexes": [
{
"id": "orders",
"filters": [
{
"id": "id-filter",
"args": {
"from": "6",
"to": "8"
}
}
]
},
{
"id": "users",
"filters": [
{
"id": "my-region",
"args": {
"region": "1022"
}
},
{
"id": "enabled-only"
}
]
}
]
}
В этом примере:
indexes
- настройки индексов;[0]
- настройки индексаorders
id
- идентификатор индексаfilters
- настройки фильтров:[0]
- настройки фильтра с идентификаторомid-filter
:id
- идентификатор фильтра;args
- аргументы фильтра:from
- значение аргументаfrom
;to
- значение аргументаto
;
[1]
- настройки индексаusers
:id
- идентификатор индексаfilters
- настройки фильтров:[0]
- настройки фильтра с идентификаторомmy-region
:id
- идентификатор фильтраmy-region
:args
- аргументы фильтра:region
- значение аргументаregion
;
[1]
настройки фильтра с идентификаторомenabled-only
:id
- идентификатор фильтраenabled-only
.
Токен доступа
Токен поиска - это JWT токен, содержащий следующие поля:
exp
- дата+время экспирации (опционально);aud
- индексы, к которым имеет доступ клиент;mylab:searcher:indexes
- список настроек доступа к индексам.
Пример содержательной части токена в json
:
{
"exp": 1628711837,
"aud": [
"users",
"orders"
],
"mylab:searcher:indexes": [
{
"id": "users"
},
{
"id": "orders",
"filters": [
{
"id": "only_my_orders",
"args": {
"user": "user@domain.com"
}
}
]
}
]
}
В этом примере:
aud
- список индексов, к которым разрешает доступ токен;exp
- дата и время истечения срока действия токена в секундах от начала эпохи (1970-01-01);mylab:searcher:indexes
- настройки сервисаSearcher
:[0]
- настройки доступа к индексуusers
(нет фильтров, т.е. без дополнительных ограничений):id
- идентификатор индексаusers
;
[1]
- настройки доступа к индексуorders
:id
- идентификатор индексаorders
;filters
- список фильтров, которые необходимо дополнительно применять при поиске в индексеorders
:[0]
:id
- литеральный идентификатор фильтраonly_my_orders
args
- аргументы фильтра:user
- значение аргументаuser
, которое будет добавлено в фильтр.
Запрос поиска
Запрос клиента
Запрос клиента осуществляется следующим запросом:
POST /v4/indezes/[indexId]/searcher
X-Search-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.....sagf0qhKM7TAxtuYcSGygZe7pls5nsO8khWl6zHOnY4
{
"query": "something I want to search",
"sort": {
"id": "early_first"
},
"offset": 0,
"limit": 10,
"queryMode": "should",
"filters": [
{
"id": "only_my",
"args": {
"user": "user@host.ru",
"less_then": 10
}
},
{
"id": "filter_without_args"
}
]
}
, где
indexId
(обязательный параметр) - идентификатор индекса. Характеризует целевую сущность для поиска . Например,orders
;filters[]
- список фильтров:id
- идентификатор фильтра;args
- аргументы фильтра в виде ключ-значение
sort
- описание сотрировки:id
- идентификатор сотрировки;args
- аргументы сортировки в виде ключ-значение
offset
- сдвиг выборки;limit
- ограничение размера выборки;query-mode
-should
/must
стратегия поиска по строке поиска;X-Search-Token
- токен поиска.
Все параметры из query
части url
- опциональны.
Результат поиска - список объектов, описывающих найденные сущности.
[
{
"content": { ... },
"score": 1.2
},
{
"content": { ... },
"score": 1.1
}
]
, где:
content
- содержательная часть найденного объекта;score
- оценка релевантности отElasticsearch
.
Запрос на сервере
Содержание запроса для ES
После получения запроса от клиента, Searcher
формирует на его основе запрос в Elasticsearch
. Условный шаблон запроса выглядит на примере ниже:
{
"from": 0,
"size": 10,
"query": {
"bool": {
"minimum_should_match": 1,
"should": [ ... query from request ... ],
"must": [ ... query from request ... ],
"filter": [ ... filters from requests and token ... ]
}
},
"sort": { ... sort from request ... }
}
}
Запрос в ES
формируется по следующему алгоритму:
offset
- если указан, берётся из запроса клиента;size
- если указан, берётся из запроса клиента (limit
) или лимит по умолчанию из конфигурации или10
;should
- список условий поиска, если выбрана стратегия поискаShould
формируются на основе параметраquery
из запроса;must
- список условий поиска, если выбрана стратегия поискаMust
формируются на основе параметраquery
из запроса;filter
- загружается из локального файла из места, в соответствии с конфигурацией. Идентификатор фильтра берётся из запроса, если указан, или из конфигурации, как фильтр по умолчанию. Если в запросе передан токен поиска, то применяются и фильтры, указанные в нём. Если ни один фильтр не удалось определить, то этот узел в запросе отсутствует;sort
- загружается из локального файла из места, в соответствии с конфигурацией. Идентификатор сортировки берётся из запроса, если указан, или из конфигурации, как сортировка по умолчанию. Если ни одну сортировку не удалось определить, то этот узел в запросе отсутствует;minimum_should_match
- указывается если естьshould
. Фиксированное значение -1.
Формирование запроса для ES
Запрос формируется следующим образом:
- определяются параметры пейджинга;
- определяется и загружается сортировка из запроса клиента или по умолчанию;
- определяются, загружаются и инициализируются аргументами фильтры из токена поиска;
- определяется и загружается фильтр из запроса клиента или по умолчанию;
- применяется строка поиска:
- разбирается по условиям;
- формируются выражения поиска из условий, применённых к полям мэппинга индекса;
- выражения для каждого литерала строки поиска объединяются по условию bool->should с минимальным количеством совпадений -
1
; - консолидированные выражения литералов объединяются в соответствии с выбранной стратегией поиска по строке поиска:
- bool->should - если стратегия
Should
с минимальным количеством совпадений -1
; - bool->must - если стратегия
Must
;
- bool->should - если стратегия
В качестве имени целевого индекса в Elasticsearch
используется переданный идентификатор индекса в запросе или имя индекса из соответствующего узла конфигурации EsIndex
.
Фильтры запроса для ES
Загрузка фильтра
Фильтры загружаются из файлов json
с содержанием, соответствующим узлам условий поиска ES
Путь к файлу фильтра формируется следующим образом:
[filter-dir]/[index]/[filter].json
, где:
filter-dir
- полный путь к директории. где находятся файлы с фильтрами;index
- идентификатор индекса из запроса;filter
- литеральный идентификатор фильтра.
Если не удалось найти файл в этой директории, то будет попытка обнаружить его в общей директории для всех индексов:
[filter-dir]/[filter].json
Если файл не будет найден и по этому адресу, то клиент получит ошибку 400 (BadRequest)
.
Список применяемых фильтров формируется следующим образом:
- фильтры из запроса
- фильтры из токена поиска
- фильтр по умолчанию из настроек индекса - поле
DefaultFilter
Пример файла фильтра:
{
"range": {
"Id": {
"gte": 2,
"lt": 5
}
}
}
В этом примере, фильтр выбирает записи где поле Id
имеет значение, удовлетворяющее условиям >=2
и <5
;
Инициализация фильтра
После загрузки, фильтр инициализируется аргументами. Аргументы - список именованных значений.
Инициализация фильтра заключается в том, что в json
фильтра заменяются тэги с именами аргументов на их значения.
Пример фильтра:
{
"range": {
"Id": {
"gte": {from},
"lt": {to}
}
}
}
В этом фильтре обозначены два аргумента: from
и to
.
Применяемые фильтры
Следующие фильтры применяются для формирования запроса в Elasticsearch
:
- фильтр, указанный в запросе клиента;
- если в запросе не указан фильтр, то используется фильтр по умолчанию, указанный в конфигурации текущего индекса;
- фильтры, указанные в токене поиска для текущего индекса.
Сортировка запроса для ES
Загрузка сортировки
Сортировки загружаются из файлов json
с содержанием, соответствующим структуре узла сортировки в запросе ES
Путь к файлу сортировки формируется следующим образом:
[sort-dir]/[index]/[sort].json
, где:
sort-dir
- полный путь к директории. где находятся файлы с сортировками;index
- идентификатор индекса из запроса;sort
- литеральный идентификатор сортировки.
Если не удалось найти файл в этой директории, то будет попытка обнаружить его в общей директории для всех индесов:
[sort-dir]/[sort].json
Если файл не будет найден и по этому адресу, то клиент получит ошибку 400 (BadRequest)
.
Приложение пытается получить литеральный идентификатор сортировки в следующей последовательности:
- из запроса поиска (обязателен, если указан)
- из настроек индекса - поле
DefaultSort
(обязателен, если указан) default
(только если файл существует)
Пример файла сортировки:
{
"Id": {
"order": "desc"
}
}
В этом примере сортировка описывает очерёдность по полю Id
в обратном порядке.
Инициализация сортировки
После загрузки, сортировка инициализируется аргументами. Аргументы - список именованных значений.
Инициализация сортировки заключается в том, что в json
сотрировки заменяются тэги с именами аргументов на их значения.
Пример фильтра:
{
"Id": {
"order": "{direction}"
}
}
В этом фильтре обозначен аргумент direction
, который можно указать как asc
, так и desc
.
Порядок сортировок
Следующие сортировки применяются для формирования запроса в Elasticsearch
:
- если не указана сортировка в запросе пользователя:
- сортировка по релевантности, если осуществляется поиск по строке поиска, переданный в запросе клиента;
- сортировка по умолчанию, если указана;
- в противном случае - сортировка из запроса пользователя.
Разбор строки поиска
Общий алгоритм разбора строки поиска
Ниже приведён алгоритм формирования условий поиска в Elasticsearch
на основании строки поиска, переданной клиентом:
текст разбивается на литералы по пробелам и табуляциям;
по каждому литералу:
назначается вес литерала в соответствии с его порядком в строке поиска. Это значит, что найденная запись по условию с этим литералом будет иметь значение релевантности, равное весу этого литерала. Вес вычисляется как количество литералов в строке поиска минус порядковый номер литерала, начинающийся с
0
:boost = literal_count - 0_index;
Это означает, что совпадения по литералам ближе к началу строки поиска будут иметь большую релевантность, чем по литералом ближе к концу строки поиска;
определяется, как условие по регулярному выражению regexp для полей типа keyword. Регулярное выражение формируется по правилу:
norm_literal.*
, где
norm_literal
- нормализованная строка, где служебные символы экранированы;определяется, как условие по регулярному выражению regexp для полей типа text. Регулярное выражение формируется по правилу:
norm_literal.*
, где
norm_literal
- нормализованная строка, переведённая в нижний регистр, где служебные символы экранированы;определяется как выражение для полнотекстового поиска match по полям типа text;
попытка определить числовое условие. Применяется с применением условия range;
попытка определить условие по датам. Применяется с применением условия range;
Определение числового диапазона
число, если успешно определяется методом int.TryParse();
123
диапазон чисел с включением границ, если есть дефис, разделяющий наборы символов, успешно определяемые как целочисленное значение методом int.TryParse();
123-321
диапазон чисел до указанного числа, исключая его - если начинается с символа
<
и остальной набор символов определяется как целочисленное значение методом int.TryParse();<321
диапазон чисел от указанного числа, исключая его - если начинается с символа
>
и остальной набор символов определяется как целочисленное значение методом int.TryParse();>321
Определение диапазона даты времени
диапазон даты и времени от начала до окончания суток указанной даты, если литерал соответствует:
короткому формату даты для
CultureInfo.InvariantCulture
. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
и меньше11.04.2008 00:00
:04/10/2008
формату даты
dd.MM.yyyy
. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
и меньше11.04.2008 00:00
:10.04.2008
формату даты
ddMMyy
, гдеyy
- двухзначный год текущего столетия. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
и меньше11.04.2008 00:00
:100408
диапазон даты и времени от начала суток указанной даты начала диапазона до начала суток указанной даты окончания диапазона, если литерал содержит дефис, который разделяет последовательности символов, соответствующих:
короткому формату даты для
CultureInfo.InvariantCulture
. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
и меньше12.04.2008 00:00
:04/10/2008-04/12/2008
формату даты
dd.MM.yyyy
. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
и меньше12.04.2008 00:00
:10.04.2008-12.04.2008
формату даты
ddMMyy
, гдеyy
- двухзначный год текущего столетия. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
и меньше12.04.2008 00:00
:100408-120408
диапазон даты и времени до начала суток указанной даты, если литерал начинается с символа
<
и содержит последовательность символов, соответствующих:короткому формату даты для
CultureInfo.InvariantCulture
. Пример ниже соответствует диапазону меньше12.04.2008 00:00
:<04/12/2008
формату даты
dd.MM.yyyy
. Пример ниже соответствует диапазону меньше12.04.2008 00:00
:<12.04.2008
формату даты
ddMMyy
, гдеyy
- двухзначный год текущего столетия. Пример ниже соответствует диапазону меньше12.04.2008 00:00
:<120408
диапазон даты и времени от начала суток указанной даты, если литерал начинается с символа
>
и содержит последовательность символов, соответствующих:короткому формату даты для
CultureInfo.InvariantCulture
. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
:>04/10/2008
формату даты
dd.MM.yyyy
. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
:>12.04.2008
формату даты
ddMMyy
, гдеyy
- двухзначный год текущего столетия. Пример ниже соответствует диапазону больше или равно10.04.2008 00:00
:>120408
Конфигурация
Основная конфигурация состоит из следующих узлов:
ES
- настройки подключения кElasticsearch
:Url
-url
подключения
Searcher
- настройки логики сервиса:SortPath
- путь по умолчанию к директории для хранения сортировок. Значение по умолчанию -/etc/mylab-search-searcher/sort/
;FilterPath
- путь по умолчанию к директории для хранения фильтров. Значение по умолчанию -/etc/mylab-search-searcher/filter/
;QueryStrategy
- стратегия поиска по строке поискаshould
/must
. Определяет общую стратегию для всех индексов. Значение по умолчанию -should
;EsIndexNamePrefix
- префикс, который будет добавляться к имени индексаElasticsearch
всех индесов поисковика;EsIndexNamePostfix
- постфикс, который будет добавляться к имени индексаElasticsearch
всех индексов поисковика;Token
- настройки использования токенов:ExpirySec
- (опционально) время жизни токена в секундах;SignKey
- текстовый ключ подписи токена. Должен быть не меньше 16 байт.
Indexes[]
- настройки индексов:Id
- идентификатор индекса;EsIndex
- если указан, определяет целевой индекс вElasticsearch
;DefaultFilter
- (опционально) литеральный идентификатор фильтра по умолчанию;DefaultSort
- (опционально) литеральный идентификатор сортировки по умолчанию;DefaultLimit
- (опционально) лимиты выборки по умолчанию;QueryStrategy
- стратегия поиска по строке поискаshould
/must
. Если указано, переопределяет стратегию для индекса. Значение по умолчанию -Undefined
;
Debug
- флаг, определяющий добавление отладочной информации о поиске вElasticsearch
(см. Отладка запросов поиска)
Отсутствие узла Searcher/Token
означает отключение функции использования токенов. Это приведёт к ошибкам при попытке запросить токен или осуществить поиск с запросом, снабжённым токеном.
Отсутствие узла Searcher/Token/ExpirySec
приведёт к тому, что:
- в токен не будет добавляться поле с датой и временем экспирации
exp
; - при проверке токена не будет проверяться время его жизни.
Отладка
Отладка запросов поиска
Для включения отладки запросов поиска, необходимо в настройках указать Searcher__Debug: true
(на примере конфигурирования через переменные окружения).
При этом в ответ поиска будут включены:
- поле
esRequest
- объект запроса вElasticsearch
. Подробнее про объект запроса поиска. - поле
explanation
- для каждой найденной сущности, объект описания причины включения сущности в результат поиска. Подробнее про объекты Explanation.
Ниже приведён пример ответа с данными отладки:
{
"entities": [
{
"content": {
"Id": 1,
"Value": "val_1",
"Date": "0001-01-01T00:00:00"
},
"score": 1,
"explanation": {
"description": "*:*",
"details": [],
"value": 1
}
}
],
"total": 20,
"esRequest": {
"trackScores": true,
"from": 0,
"size": 1
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.1 is compatible. |
-
.NETCoreApp 3.1
- MyLab.ApiClient (>= 3.7.21)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on MyLab.Search.SearcherClient:
Package | Downloads |
---|---|
MyLab.ProtocolStorage.Client
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.