Выделение
Выделители позволяют получить выделенные фрагменты из одного или нескольких полей в результатах поиска, чтобы показать пользователям, где совпадает запрос. При запросе выделения ответ содержит дополнительный элемент highlight для каждого результата поиска, который включает выделенные поля и выделенные фрагменты.
Выделители не отражают булеву логику запроса при извлечении терминов для выделения. Таким образом, для некоторых сложных булевых запросов (например, вложенных булевых запросов, запросов с использованием minimum_should_match и т. д.), части документов могут быть выделены, что не соответствует совпадениям с запросом.
Выделение требует фактического содержимого поля. Если поле не сохраняется (сопоставление не устанавливает store в true), фактическое значение _source загружается, а соответствующее поле извлекается из _source.
Например, чтобы получить выделения для поля content в каждом результате поиска с помощью стандартного выделителя, включите объект highlight в тело запроса, который указывает поле content:
resp = client.search(
query={
"match": {
"content": "kimchy"
}
},
highlight={
"fields": {
"content": {}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
content: 'kimchy'
}
},
highlight: {
fields: {
content: {}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
content: "kimchy",
},
},
highlight: {
fields: {
content: {},
},
},
});
console.log(response); GET /_search
{
"query": {
"match": { "content": "kimchy" }
},
"highlight": {
"fields": {
"content": {}
}
}
} Elasticsearch поддерживает три выделителя: unified, plain и fvh (быстрый векторный выделитель). Вы можете указать желаемый выделитель type для каждого поля.
Унифицированный выделитель
Выделитель unified использует унифицированный выделитель Lucene. Этот выделитель разбивает текст на предложения и использует алгоритм BM25 для оценки отдельных предложений как документов в корпусе. Он также поддерживает точное выделение фраз и выделение по нескольким терминам (нечеткое, префикс, регулярное выражение). Выделитель unified может объединять совпадения из нескольких полей в один результат (см. matched_fields). Это стандартный выделитель.
Простой выделитель
Выделитель plain использует стандартный выделитель Lucene. Он пытается отразить логику совпадения запроса в терминах понимания важности слов и любых критериев позиционирования слов в запросах на фразы.
Выделитель plain лучше всего подходит для выделения совпадений простых запросов в одном поле. Чтобы точно отразить логику запроса, он создает крошечный индекс в памяти и повторно выполняет исходные критерии запроса через планировщик выполнения запросов Lucene, чтобы получить доступ к информации о совпадениях на низком уровне для текущего документа. Это повторяется для каждого поля и каждого документа, которые необходимо выделить. Если вы хотите выделить много полей во многих документах со сложными запросами, рекомендуется использовать выделитель unified на полях postings или term_vector.
Быстрый векторный выделитель
Выделитель fvh использует быстрый векторный выделитель Lucene. Этот выделитель может использоваться для полей с term_vector, установленным на with_positions_offsets в сопоставлении. Быстрый векторный выделитель:
- Может быть настроен с помощью
boundary_scanner. - Требует установки
term_vectorнаwith_positions_offsets, что увеличивает размер индекса - Может объединять совпадения из нескольких полей в один результат. См.
matched_fields - Может назначать разные веса совпадениям в разных позициях, что позволяет, например, сортировать совпадения фраз выше совпадений терминов при выделении запроса Boosting, который повышает совпадения фраз по сравнению с совпадениями терминов
Выделитель fvh не поддерживает запросы span. Если вам нужна поддержка запросов span, попробуйте альтернативный выделитель, такой как выделитель unified.
Стратегия смещений
Чтобы создать осмысленные фрагменты поиска из запрошенных терминов, выделителю нужно знать начальное и конечное смещения символов каждого слова в исходном тексте. Эти смещения можно получить из:
- Списка результатов. Если
index_optionsустановлено наoffsetsв сопоставлении, выделительunifiedиспользует эту информацию для выделения документов без повторного анализа текста. Он повторно выполняет исходный запрос непосредственно на списке результатов и извлекает соответствующие смещения из индекса, ограничивая сбор выделенными документами. Это важно, если у вас большие поля, поскольку не требуется повторный анализ выделяемого текста. Это также требует меньше места на диске, чем использованиеterm_vectors. - Векторов терминов. Если информация о векторах терминов предоставляется путем установки
term_vectorнаterm_vectorв сопоставлении, выделительunifiedавтоматически используетterm_vectorдля выделения поля. Он быстрый, особенно для больших полей (>1MB) и для выделения запросов по нескольким терминам, таких какprefixилиwildcard, потому что может получить доступ к словарю терминов для каждого документа. Выделительfvhвсегда использует векторы терминов. - Простое выделение. Этот режим используется выделителем
unified, когда нет других вариантов. Он создает крошечный индекс в памяти и повторно выполняет исходные критерии запроса через планировщик выполнения запросов Lucene, чтобы получить доступ к информации о совпадениях на низком уровне для текущего документа. Это повторяется для каждого поля и каждого документа, которые необходимо выделить. Выделительplainвсегда использует простое выделение.
Простое выделение для больших текстов может потребовать значительного времени и памяти. Для защиты от этого максимальное количество символов текста, которые будут анализироваться, ограничено 1000000. Это значение по умолчанию можно изменить для конкретного индекса с помощью параметра индекса index.highlight.max_analyzed_offset.
Настройки выделения
Настройки выделения могут быть заданы на глобальном уровне и переопределены на уровне поля.
- boundary_chars
- Строка, содержащая каждый разделительный символ. По умолчанию
.,!? \t\n. - boundary_max_scan
- Насколько далеко просматривать разделительные символы. По умолчанию
20.
- boundary_scanner
-
Указывает, как разбивать выделенные фрагменты:
chars,sentenceилиword. Действует только для выделенийunifiedиfvh. По умолчанию используетсяsentenceдля выделенияunified. По умолчанию используетсяcharsдля выделенияfvh.-
chars - Используйте символы, указанные в
boundary_chars, как границы выделения. Параметрboundary_max_scanуправляет тем, как далеко искать символы границы. Действует только для выделенияfvh. -
sentence -
Разбивать выделенные фрагменты по границам предложений, определяемым с помощью BreakIterator Java’s BreakIterator. Вы можете указать используемый язык с помощью
boundary_scanner_locale.При использовании с выделением
unified, сканерsentenceразбивает предложения, превышающиеfragment_size, по границе первого слова, следующего заfragment_size. Вы можете установитьfragment_sizeв 0, чтобы никогда не разбивать предложение. -
word - Разбивать выделенные фрагменты по границам слов, определяемым с помощью BreakIterator Java’s BreakIterator. Вы можете указать используемый язык с помощью
boundary_scanner_locale.
-
- boundary_scanner_locale
- Управляет языковым регионом, используемым для поиска границ предложений и слов. Этот параметр принимает тег языка, например,
"en-US","fr-FR","ja-JP". Дополнительную информацию можно найти в документации Locale Language Tag. Значение по умолчанию — Locale.ROOT. - encoder
- Указывает, должен ли фрагмент кодироваться в HTML:
default(без кодирования) илиhtml(HTML-экранировать текст фрагмента и затем вставить теги выделения). - fields
-
Указывает поля, для которых нужно получить выделения. Вы можете использовать шаблоны для указания полей. Например, вы можете указать
comment_*, чтобы получить выделения для всех полей text, match_only_text и keyword, которые начинаются сcomment_.При использовании шаблонов выделения применяются только к полям типа text, match_only_text и keyword. Если вы используете пользовательский маппер и хотите выделить поле, вы должны явно указать имя этого поля.
- fragmenter
- Указывает, как текст должен быть разделен на фрагменты выделения:
simpleилиspan. Действует только для выделенияplain. По умолчанию используетсяspan. - force_source
-
устаревший; этот параметр не оказывает никакого влияния
-
simple - Разбивает текст на фрагменты одинаковой длины.
-
span - Разбивает текст на фрагменты одинаковой длины, но старается не разделять текст между выделенными терминами. Это полезно, когда вы ищете фразы. Значение по умолчанию.
-
- fragment_offset
- Управляет отступом, с которого необходимо начать выделение. Действует только при использовании выделения
fvh. - fragment_size
- Размер фрагмента выделения в символах. По умолчанию 100.
- highlight_query
-
Выделить совпадения для запроса, отличного от запроса поиска. Это особенно полезно, если вы используете запрос rescore, так как они по умолчанию не учитываются выделением.
Elasticsearch не проверяет, что
highlight_queryсодержит запрос поиска, поэтому возможно определить его так, что результаты поиска не будут выделены. Обычно вы должны включать запрос поиска вhighlight_query. - matched_fields
- Объединять совпадения по нескольким полям для выделения одного поля. Это наиболее удобно для многопольных структур, которые анализируют одну и ту же строку разными способами. Действительно для выделений
unifiedи fvh`, но поведение этого параметра отличается для каждого выделения.
Для выделения unified:
- Массив
matched_fieldsне должен содержать исходное поле, которое вы хотите выделить. Исходное поле будет автоматически добавлено вmatched_fields, и нет способа исключить его совпадения при выделении. -
matched_fieldsи исходное поле могут быть проиндексированы с помощью разных стратегий (сoffsetsили без него, сterm_vectorsили без него). - Загружается только исходное поле, к которому комбинируются совпадения, поэтому только это поле получает выгоду от установки
storeнаyes
Для выделения fvh:
- Массив
matched_fieldsможет или не может содержать исходное поле в зависимости от ваших потребностей. Если вы хотите включить совпадения исходного поля в выделение, добавьте его в массивmatched_fields. - Все поля
matched_fieldsдолжны иметьterm_vectorустановленным вwith_positions_offsets -
Загружается только исходное поле, к которому комбинируются совпадения, поэтому только это поле получает выгоду от установки
storeнаyes.- no_match_size
- Количество символов текста, которые необходимо вернуть с начала поля, если нет совпадений для выделения. По умолчанию 0 (ничего не возвращается).
- number_of_fragments
- Максимальное количество фрагментов для возврата. Если число фрагментов равно 0, фрагменты не возвращаются. Вместо этого содержимое всего поля выделяется и возвращается. Это может быть полезно при выделении коротких текстов, таких как заголовок или адрес, но фрагментация не требуется. Если
number_of_fragmentsравно 0,fragment_sizeигнорируется. По умолчанию 5. - order
- Сортирует выделенные фрагменты по оценке, если установлено в
score. По умолчанию фрагменты выводятся в порядке их появления в поле (order:none). Установка этого параметра вscoreвыведет наиболее релевантные фрагменты в первую очередь. Каждое выделение применяет свою логику для вычисления оценок релевантности. См. документ Как работают выделения в ES для получения более подробной информации о том, как различные выделения находят лучшие фрагменты. - phrase_limit
- Управляет количеством совпадающих фраз в документе, которые учитываются. Предотвращает анализ выделением
fvhслишком большого количества фраз и избыточное потребление памяти. При использованииmatched_fields, учитываютсяphrase_limitфраз на поле совпадений. Увеличение предела увеличивает время запроса и потребление памяти. Поддерживается только выделениемfvh. По умолчанию 256. - pre_tags
- Используется совместно с
post_tagsдля определения HTML-тегов, используемых для выделенного текста. По умолчанию выделенный текст заключён в<em>и</em>теги. Укажите в виде массива строк. - post_tags
- Используется совместно с
pre_tagsдля определения HTML-тегов, используемых для выделенного текста. По умолчанию выделенный текст заключён в<em>и</em>теги. Укажите в виде массива строк. - require_field_match
- По умолчанию выделяются только поля, содержащие совпадение с запросом. Установите
require_field_matchвfalse, чтобы выделить все поля. По умолчаниюtrue.
- max_analyzed_offset
- По умолчанию максимальное количество анализируемых символов для запроса выделения ограничено значением, определенным в настройке
index.highlight.max_analyzed_offset, и при превышении этого предела возвращается ошибка. Если это значение установлено на ненулевое значение, выделение останавливается на этом максимальном пределе, а остальной текст не обрабатывается, следовательно, не выделяется и не возвращается ошибка. Параметр запросаmax_analyzed_offsetне переопределяет настройкуindex.highlight.max_analyzed_offset, которая имеет приоритет, если она установлена значением меньше, чем значение параметра запроса. - tags_schema
-
Установите в
styledдля использования встроенной схемы тегов. Схема теговstyledопределяет следующиеpre_tagsи определяетpost_tagsкак</em>.<em class="hlt1">, <em class="hlt2">, <em class="hlt3">, <em class="hlt4">, <em class="hlt5">, <em class="hlt6">, <em class="hlt7">, <em class="hlt8">, <em class="hlt9">, <em class="hlt10">
- type
- Используемое выделение:
unified,plainилиfvh. По умолчанию используетсяunified.
Примеры выделения
- Изменить глобальные настройки
- Указать запрос выделения
- Установить тип выделения
- Настроить теги выделения
- Выделить все поля
- Объединить совпадения по нескольким полям
- Явно указать порядок выделенных полей
- Управлять выделенными фрагментами
- Выделить с помощью списка извлеченных данных
- Указать фрагментировщик для простого выделения
Изменить глобальные настройки
Вы можете указать настройки выделения глобально и выборочно изменять их для отдельных полей.
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"number_of_fragments": 3,
"fragment_size": 150,
"fields": {
"body": {
"pre_tags": [
"<em>"
],
"post_tags": [
"</em>"
]
},
"blog.title": {
"number_of_fragments": 0
},
"blog.author": {
"number_of_fragments": 0
},
"blog.comment": {
"number_of_fragments": 5,
"order": "score"
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
number_of_fragments: 3,
fragment_size: 150,
fields: {
body: {
pre_tags: [
'<em>'
],
post_tags: [
'</em>'
]
},
'blog.title' => {
number_of_fragments: 0
},
'blog.author' => {
number_of_fragments: 0
},
'blog.comment' => {
number_of_fragments: 5,
order: 'score'
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
number_of_fragments: 3,
fragment_size: 150,
fields: {
body: {
pre_tags: ["<em>"],
post_tags: ["</em>"],
},
"blog.title": {
number_of_fragments: 0,
},
"blog.author": {
number_of_fragments: 0,
},
"blog.comment": {
number_of_fragments: 5,
order: "score",
},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"number_of_fragments" : 3,
"fragment_size" : 150,
"fields" : {
"body" : { "pre_tags" : ["<em>"], "post_tags" : ["</em>"] },
"blog.title" : { "number_of_fragments" : 0 },
"blog.author" : { "number_of_fragments" : 0 },
"blog.comment" : { "number_of_fragments" : 5, "order" : "score" }
}
}
} Указать запрос выделения
Вы можете указать highlight_query, чтобы учесть дополнительную информацию при выделении. Например, следующий запрос включает как поисковый запрос, так и запрос повторной оценки в highlight_query. Без highlight_query выделение учитывало бы только поисковый запрос.
resp = client.search(
query={
"match": {
"comment": {
"query": "foo bar"
}
}
},
rescore={
"window_size": 50,
"query": {
"rescore_query": {
"match_phrase": {
"comment": {
"query": "foo bar",
"slop": 1
}
}
},
"rescore_query_weight": 10
}
},
source=False,
highlight={
"order": "score",
"fields": {
"comment": {
"fragment_size": 150,
"number_of_fragments": 3,
"highlight_query": {
"bool": {
"must": {
"match": {
"comment": {
"query": "foo bar"
}
}
},
"should": {
"match_phrase": {
"comment": {
"query": "foo bar",
"slop": 1,
"boost": 10
}
}
},
"minimum_should_match": 0
}
}
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
comment: {
query: 'foo bar'
}
}
},
rescore: {
window_size: 50,
query: {
rescore_query: {
match_phrase: {
comment: {
query: 'foo bar',
slop: 1
}
}
},
rescore_query_weight: 10
}
},
_source: false,
highlight: {
order: 'score',
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3,
highlight_query: {
bool: {
must: {
match: {
comment: {
query: 'foo bar'
}
}
},
should: {
match_phrase: {
comment: {
query: 'foo bar',
slop: 1,
boost: 10
}
}
},
minimum_should_match: 0
}
}
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
comment: {
query: "foo bar",
},
},
},
rescore: {
window_size: 50,
query: {
rescore_query: {
match_phrase: {
comment: {
query: "foo bar",
slop: 1,
},
},
},
rescore_query_weight: 10,
},
},
_source: false,
highlight: {
order: "score",
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3,
highlight_query: {
bool: {
must: {
match: {
comment: {
query: "foo bar",
},
},
},
should: {
match_phrase: {
comment: {
query: "foo bar",
slop: 1,
boost: 10,
},
},
},
minimum_should_match: 0,
},
},
},
},
},
});
console.log(response); GET /_search
{
"query": {
"match": {
"comment": {
"query": "foo bar"
}
}
},
"rescore": {
"window_size": 50,
"query": {
"rescore_query": {
"match_phrase": {
"comment": {
"query": "foo bar",
"slop": 1
}
}
},
"rescore_query_weight": 10
}
},
"_source": false,
"highlight": {
"order": "score",
"fields": {
"comment": {
"fragment_size": 150,
"number_of_fragments": 3,
"highlight_query": {
"bool": {
"must": {
"match": {
"comment": {
"query": "foo bar"
}
}
},
"should": {
"match_phrase": {
"comment": {
"query": "foo bar",
"slop": 1,
"boost": 10.0
}
}
},
"minimum_should_match": 0
}
}
}
}
}
} Установить тип выделения
Поле type позволяет принудительно установить определенный тип выделения. Допустимые значения: unified, plain и fvh. Следующий пример принудительно использует простой выделения:
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"fields": {
"comment": {
"type": "plain"
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
fields: {
comment: {
type: 'plain'
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
fields: {
comment: {
type: "plain",
},
},
},
});
console.log(response); GET /_search
{
"query": {
"match": { "user.id": "kimchy" }
},
"highlight": {
"fields": {
"comment": { "type": "plain" }
}
}
} Настроить теги выделения
По умолчанию выделение обёртывает выделенный текст в <em> и </em>. Это можно контролировать, задавая pre_tags и post_tags, например:
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"pre_tags": [
"<tag1>"
],
"post_tags": [
"</tag1>"
],
"fields": {
"body": {}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
pre_tags: [
'<tag1>'
],
post_tags: [
'</tag1>'
],
fields: {
body: {}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
pre_tags: ["<tag1>"],
post_tags: ["</tag1>"],
fields: {
body: {},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"pre_tags" : ["<tag1>"],
"post_tags" : ["</tag1>"],
"fields" : {
"body" : {}
}
}
} При использовании быстрого вектора выделения вы можете указать дополнительные теги, и "важность" упорядочена.
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"pre_tags": [
"<tag1>",
"<tag2>"
],
"post_tags": [
"</tag1>",
"</tag2>"
],
"fields": {
"body": {}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
pre_tags: [
'<tag1>',
'<tag2>'
],
post_tags: [
'</tag1>',
'</tag2>'
],
fields: {
body: {}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
pre_tags: ["<tag1>", "<tag2>"],
post_tags: ["</tag1>", "</tag2>"],
fields: {
body: {},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"pre_tags" : ["<tag1>", "<tag2>"],
"post_tags" : ["</tag1>", "</tag2>"],
"fields" : {
"body" : {}
}
}
} Вы также можете использовать встроенную схему тегов styled:
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"tags_schema": "styled",
"fields": {
"comment": {}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
tags_schema: 'styled',
fields: {
comment: {}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
tags_schema: "styled",
fields: {
comment: {},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"tags_schema" : "styled",
"fields" : {
"comment" : {}
}
}
} Выделить все поля
По умолчанию выделяются только поля, содержащие совпадения с запросом. Установите require_field_match в false, чтобы выделить все поля.
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"require_field_match": False,
"fields": {
"body": {
"pre_tags": [
"<em>"
],
"post_tags": [
"</em>"
]
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
require_field_match: false,
fields: {
body: {
pre_tags: [
'<em>'
],
post_tags: [
'</em>'
]
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
require_field_match: false,
fields: {
body: {
pre_tags: ["<em>"],
post_tags: ["</em>"],
},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"require_field_match": false,
"fields": {
"body" : { "pre_tags" : ["<em>"], "post_tags" : ["</em>"] }
}
}
} Объединить совпадения по нескольким полям
Поддерживается unified и fvh выделениями.
Объединённое и быстрое векторное выделение могут объединять совпадения по нескольким полям, чтобы выделить одно поле. Это наиболее интуитивно понятно для многопольных случаев, которые анализируют одну строку разными способами.
В следующих примерах comment анализируется с помощью анализатора standard, а comment.english — анализатором english.
resp = client.indices.create(
index="index1",
mappings={
"properties": {
"comment": {
"type": "text",
"analyzer": "standard",
"fields": {
"english": {
"type": "text",
"analyzer": "english"
}
}
}
}
},
)
print(resp) const response = await client.indices.create({
index: "index1",
mappings: {
properties: {
comment: {
type: "text",
analyzer: "standard",
fields: {
english: {
type: "text",
analyzer: "english",
},
},
},
},
},
});
console.log(response); PUT index1
{
"mappings": {
"properties": {
"comment": {
"type": "text",
"analyzer": "standard",
"fields": {
"english": {
"type": "text",
"analyzer": "english"
}
}
}
}
}
} resp = client.bulk(
index="index1",
refresh=True,
operations=[
{
"index": {
"_id": "doc1"
}
},
{
"comment": "run with scissors"
},
{
"index": {
"_id": "doc2"
}
},
{
"comment": "running with scissors"
}
],
)
print(resp) const response = await client.bulk({
index: "index1",
refresh: "true",
operations: [
{
index: {
_id: "doc1",
},
},
{
comment: "run with scissors",
},
{
index: {
_id: "doc2",
},
},
{
comment: "running with scissors",
},
],
});
console.log(response); PUT index1/_bulk?refresh=true
{"index": {"_id": "doc1" }}
{"comment": "run with scissors"}
{ "index" : {"_id": "doc2"} }
{"comment": "running with scissors"} resp = client.search(
index="index1",
query={
"query_string": {
"query": "running with scissors",
"fields": [
"comment",
"comment.english"
]
}
},
highlight={
"order": "score",
"fields": {
"comment": {}
}
},
)
print(resp) const response = await client.search({
index: "index1",
query: {
query_string: {
query: "running with scissors",
fields: ["comment", "comment.english"],
},
},
highlight: {
order: "score",
fields: {
comment: {},
},
},
});
console.log(response); GET index1/_search
{
"query": {
"query_string": {
"query": "running with scissors",
"fields": ["comment", "comment.english"]
}
},
"highlight": {
"order": "score",
"fields": {
"comment": {}
}
}
} Вышеупомянутый запрос соответствует как «run with scissors», так и «running with scissors» и выделит «running» и «scissors», но не «run». Если обе фразы встречаются в большом документе, то «running with scissors» будет отсортировано выше «run with scissors» в списке фрагментов, поскольку в этом фрагменте больше совпадений.
{
...
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score": 1.0577903,
"hits" : [
{
"_index" : "index1",
"_id" : "doc2",
"_score" : 1.0577903,
"_source" : {
"comment" : "running with scissors"
},
"highlight" : {
"comment" : [
"<em>running</em> <em>with</em> <em>scissors</em>"
]
}
},
{
"_index" : "index1",
"_id" : "doc1",
"_score" : 0.36464313,
"_source" : {
"comment" : "run with scissors"
},
"highlight" : {
"comment" : [
"run <em>with</em> <em>scissors</em>"
]
}
}
]
}
} Нижеприведенный запрос выделяет «run», а также «running» и «scissors», поскольку параметр matched_fields указывает, что для выделения необходимо объединить совпадения из поля comment.english с совпадениями из исходного поля comment.
resp = client.search(
index="index1",
query={
"query_string": {
"query": "running with scissors",
"fields": [
"comment",
"comment.english"
]
}
},
highlight={
"order": "score",
"fields": {
"comment": {
"matched_fields": [
"comment.english"
]
}
}
},
)
print(resp) const response = await client.search({
index: "index1",
query: {
query_string: {
query: "running with scissors",
fields: ["comment", "comment.english"],
},
},
highlight: {
order: "score",
fields: {
comment: {
matched_fields: ["comment.english"],
},
},
},
});
console.log(response); GET index1/_search
{
"query": {
"query_string": {
"query": "running with scissors",
"fields": ["comment", "comment.english"]
}
},
"highlight": {
"order": "score",
"fields": {
"comment": {
"matched_fields": ["comment.english"]
}
}
}
} {
...
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score": 1.0577903,
"hits" : [
{
"_index" : "index1",
"_id" : "doc2",
"_score" : 1.0577903,
"_source" : {
"comment" : "running with scissors"
},
"highlight" : {
"comment" : [
"<em>running</em> <em>with</em> <em>scissors</em>"
]
}
},
{
"_index" : "index1",
"_id" : "doc1",
"_score" : 0.36464313,
"_source" : {
"comment" : "run with scissors"
},
"highlight" : {
"comment" : [
"<em>run</em> <em>with</em> <em>scissors</em>"
]
}
}
]
}
} В следующих примерах comment анализируется с помощью анализатора standard, а comment.english — анализатором english.
resp = client.indices.create(
index="index2",
mappings={
"properties": {
"comment": {
"type": "text",
"analyzer": "standard",
"term_vector": "with_positions_offsets",
"fields": {
"english": {
"type": "text",
"analyzer": "english",
"term_vector": "with_positions_offsets"
}
}
}
}
},
)
print(resp) const response = await client.indices.create({
index: "index2",
mappings: {
properties: {
comment: {
type: "text",
analyzer: "standard",
term_vector: "with_positions_offsets",
fields: {
english: {
type: "text",
analyzer: "english",
term_vector: "with_positions_offsets",
},
},
},
},
},
});
console.log(response); PUT index2
{
"mappings": {
"properties": {
"comment": {
"type": "text",
"analyzer": "standard",
"term_vector": "with_positions_offsets",
"fields": {
"english": {
"type": "text",
"analyzer": "english",
"term_vector": "with_positions_offsets"
}
}
}
}
}
} resp = client.bulk(
index="index2",
refresh=True,
operations=[
{
"index": {
"_id": "doc1"
}
},
{
"comment": "run with scissors"
},
{
"index": {
"_id": "doc2"
}
},
{
"comment": "running with scissors"
}
],
)
print(resp) const response = await client.bulk({
index: "index2",
refresh: "true",
operations: [
{
index: {
_id: "doc1",
},
},
{
comment: "run with scissors",
},
{
index: {
_id: "doc2",
},
},
{
comment: "running with scissors",
},
],
});
console.log(response); PUT index2/_bulk?refresh=true
{"index": {"_id": "doc1" }}
{"comment": "run with scissors"}
{ "index" : {"_id": "doc2"} }
{"comment": "running with scissors"} resp = client.search(
index="index2",
query={
"query_string": {
"query": "running with scissors",
"fields": [
"comment",
"comment.english"
]
}
},
highlight={
"order": "score",
"fields": {
"comment": {
"type": "fvh"
}
}
},
)
print(resp) const response = await client.search({
index: "index2",
query: {
query_string: {
query: "running with scissors",
fields: ["comment", "comment.english"],
},
},
highlight: {
order: "score",
fields: {
comment: {
type: "fvh",
},
},
},
});
console.log(response); GET index2/_search
{
"query": {
"query_string": {
"query": "running with scissors",
"fields": ["comment", "comment.english"]
}
},
"highlight": {
"order": "score",
"fields": {
"comment": {
"type" : "fvh"
}
}
}
} Вышеупомянутый запрос соответствует как «run with scissors», так и «running with scissors» и выделит «running» и «scissors», но не «run». Если обе фразы встречаются в большом документе, то «running with scissors» будет отсортировано выше «run with scissors» в списке фрагментов, поскольку в этом фрагменте больше совпадений.
{
...
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score": 1.0577903,
"hits" : [
{
"_index" : "index2",
"_id" : "doc2",
"_score" : 1.0577903,
"_source" : {
"comment" : "running with scissors"
},
"highlight" : {
"comment" : [
"<em>running</em> <em>with</em> <em>scissors</em>"
]
}
},
{
"_index" : "index2",
"_id" : "doc1",
"_score" : 0.36464313,
"_source" : {
"comment" : "run with scissors"
},
"highlight" : {
"comment" : [
"run <em>with</em> <em>scissors</em>"
]
}
}
]
}
} Нижеприведенный запрос выделяет «run», а также «running» и «scissors», потому что параметр matched_fields указывает на необходимость объединения совпадений из полей comment и comment.english.
resp = client.search(
index="index2",
query={
"query_string": {
"query": "running with scissors",
"fields": [
"comment",
"comment.english"
]
}
},
highlight={
"order": "score",
"fields": {
"comment": {
"type": "fvh",
"matched_fields": [
"comment",
"comment.english"
]
}
}
},
)
print(resp) const response = await client.search({
index: "index2",
query: {
query_string: {
query: "running with scissors",
fields: ["comment", "comment.english"],
},
},
highlight: {
order: "score",
fields: {
comment: {
type: "fvh",
matched_fields: ["comment", "comment.english"],
},
},
},
});
console.log(response); GET index2/_search
{
"query": {
"query_string": {
"query": "running with scissors",
"fields": ["comment", "comment.english"]
}
},
"highlight": {
"order": "score",
"fields": {
"comment": {
"type" : "fvh",
"matched_fields": ["comment", "comment.english"]
}
}
}
} {
...
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score": 1.0577903,
"hits" : [
{
"_index" : "index2",
"_id" : "doc2",
"_score" : 1.0577903,
"_source" : {
"comment" : "running with scissors"
},
"highlight" : {
"comment" : [
"<em>running</em> <em>with</em> <em>scissors</em>"
]
}
},
{
"_index" : "index2",
"_id" : "doc1",
"_score" : 0.36464313,
"_source" : {
"comment" : "run with scissors"
},
"highlight" : {
"comment" : [
"<em>run</em> <em>with</em> <em>scissors</em>"
]
}
}
]
}
} Нижеприведенный запрос не выделит «run» или «scissor», но показывает, что вполне допустимо не указывать поле, к которому объединяются совпадения (comment.english), в выводимых полях.
resp = client.search(
index="index2",
query={
"query_string": {
"query": "running with scissors",
"fields": [
"comment",
"comment.english"
]
}
},
highlight={
"order": "score",
"fields": {
"comment.english": {
"type": "fvh",
"matched_fields": [
"comment"
]
}
}
},
)
print(resp) const response = await client.search({
index: "index2",
query: {
query_string: {
query: "running with scissors",
fields: ["comment", "comment.english"],
},
},
highlight: {
order: "score",
fields: {
"comment.english": {
type: "fvh",
matched_fields: ["comment"],
},
},
},
});
console.log(response); GET index2/_search
{
"query": {
"query_string": {
"query": "running with scissors",
"fields": ["comment", "comment.english"]
}
},
"highlight": {
"order": "score",
"fields": {
"comment.english": {
"type" : "fvh",
"matched_fields": ["comment"]
}
}
}
} {
...
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score": 1.0577903,
"hits" : [
{
"_index" : "index2",
"_id" : "doc2",
"_score" : 1.0577903,
"_source" : {
"comment" : "running with scissors"
},
"highlight" : {
"comment.english" : [
"<em>running</em> <em>with</em> <em>scissors</em>"
]
}
},
{
"_index" : "index2",
"_id" : "doc1",
"_score" : 0.36464313,
"_source" : {
"comment" : "run with scissors"
},
"highlight" : {
"comment.english" : [
"run <em>with</em> <em>scissors</em>"
]
}
}
]
}
} Использование matched_fields с ненулевым массивом приводит к небольшому накладным расходам, поэтому всегда предпочтительнее
"highlight": {
"fields": {
"comment": {}
}
} чем
"highlight": {
"fields": {
"comment": {
"matched_fields": ["comment"],
"type" : "fvh"
}
}
} Технически также допустимо добавлять поля в matched_fields, которые не содержат ту же строку, что и поле, к которому объединяются совпадения. Результаты могут оказаться бессмысленными, и если одно из совпадений находится за пределами текста, то весь запрос завершится ошибкой.
Явное упорядочивание выделенных полей
Elasticsearch выделяет поля в том порядке, в котором они отправляются, но согласно спецификации JSON, объекты неупорядочены. Если вам нужно явно указать порядок выделения полей, укажите fields как массив:
resp = client.search(
highlight={
"fields": [
{
"title": {}
},
{
"text": {}
}
]
},
)
print(resp) response = client.search(
body: {
highlight: {
fields: [
{
title: {}
},
{
text: {}
}
]
}
}
)
puts response const response = await client.search({
highlight: {
fields: [
{
title: {},
},
{
text: {},
},
],
},
});
console.log(response); GET /_search
{
"highlight": {
"fields": [
{ "title": {} },
{ "text": {} }
]
}
} Ни один из встроенных в Elasticsearch выделений не учитывает порядок выделения полей, но плагин может.
Управление выделенными фрагментами
Каждое выделенное поле может контролировать размер выделенного фрагмента в символах (по умолчанию 100) и максимальное количество возвращаемых фрагментов (по умолчанию 5). Например:
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"fields": {
"comment": {
"fragment_size": 150,
"number_of_fragments": 3
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3,
},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"fields" : {
"comment" : {"fragment_size" : 150, "number_of_fragments" : 3}
}
}
} Помимо этого, можно указать, что выделенные фрагменты должны быть отсортированы по баллу:
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"order": "score",
"fields": {
"comment": {
"fragment_size": 150,
"number_of_fragments": 3
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
order: 'score',
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
order: "score",
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3,
},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"order" : "score",
"fields" : {
"comment" : {"fragment_size" : 150, "number_of_fragments" : 3}
}
}
} Если значение number_of_fragments установлено в 0, фрагменты не генерируются, вместо этого возвращается всё содержимое поля, и, конечно же, оно выделяется. Это может быть очень полезно, если необходимо выделить короткие тексты (например, заголовок документа или адрес), но фрагментация не требуется. Обратите внимание, что fragment_size в этом случае игнорируется.
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"fields": {
"body": {},
"blog.title": {
"number_of_fragments": 0
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
fields: {
body: {},
'blog.title' => {
number_of_fragments: 0
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
fields: {
body: {},
"blog.title": {
number_of_fragments: 0,
},
},
},
});
console.log(response); GET /_search
{
"query" : {
"match": { "user.id": "kimchy" }
},
"highlight" : {
"fields" : {
"body" : {},
"blog.title" : {"number_of_fragments" : 0}
}
}
} При использовании fvh можно использовать параметр fragment_offset для управления отступом, с которого начинается выделение.
В случае отсутствия совпадающего фрагмента для выделения по умолчанию ничего не возвращается. Вместо этого можно вернуть фрагмент текста с начала поля, установив no_match_size (по умолчанию 0) на длину текста, который требуется вернуть. Фактическая длина может быть короче или длиннее указанной, поскольку она пытается разбить на границе слова.
resp = client.search(
query={
"match": {
"user.id": "kimchy"
}
},
highlight={
"fields": {
"comment": {
"fragment_size": 150,
"number_of_fragments": 3,
"no_match_size": 150
}
}
},
)
print(resp) response = client.search(
body: {
query: {
match: {
'user.id' => 'kimchy'
}
},
highlight: {
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3,
no_match_size: 150
}
}
}
}
)
puts response const response = await client.search({
query: {
match: {
"user.id": "kimchy",
},
},
highlight: {
fields: {
comment: {
fragment_size: 150,
number_of_fragments: 3,
no_match_size: 150,
},
},
},
});
console.log(response); GET /_search
{
"query": {
"match": { "user.id": "kimchy" }
},
"highlight": {
"fields": {
"comment": {
"fragment_size": 150,
"number_of_fragments": 3,
"no_match_size": 150
}
}
}
} Выделение с использованием списка записей
Вот пример установки поля comment в схеме индекса, чтобы разрешить выделение с помощью записей:
resp = client.indices.create(
index="example",
mappings={
"properties": {
"comment": {
"type": "text",
"index_options": "offsets"
}
}
},
)
print(resp) response = client.indices.create(
index: 'example',
body: {
mappings: {
properties: {
comment: {
type: 'text',
index_options: 'offsets'
}
}
}
}
)
puts response const response = await client.indices.create({
index: "example",
mappings: {
properties: {
comment: {
type: "text",
index_options: "offsets",
},
},
},
});
console.log(response); PUT /example
{
"mappings": {
"properties": {
"comment" : {
"type": "text",
"index_options" : "offsets"
}
}
}
} Вот пример установки поля comment для выделения с использованием term_vectors (это приведет к увеличению размера индекса):
resp = client.indices.create(
index="example",
mappings={
"properties": {
"comment": {
"type": "text",
"term_vector": "with_positions_offsets"
}
}
},
)
print(resp) response = client.indices.create(
index: 'example',
body: {
mappings: {
properties: {
comment: {
type: 'text',
term_vector: 'with_positions_offsets'
}
}
}
}
)
puts response const response = await client.indices.create({
index: "example",
mappings: {
properties: {
comment: {
type: "text",
term_vector: "with_positions_offsets",
},
},
},
});
console.log(response); PUT /example
{
"mappings": {
"properties": {
"comment" : {
"type": "text",
"term_vector" : "with_positions_offsets"
}
}
}
} Указание фрагментера для простого выделения
При использовании выделения plain, вы можете выбрать между фрагментерами simple и span:
resp = client.search(
index="my-index-000001",
query={
"match_phrase": {
"message": "number 1"
}
},
highlight={
"fields": {
"message": {
"type": "plain",
"fragment_size": 15,
"number_of_fragments": 3,
"fragmenter": "simple"
}
}
},
)
print(resp) response = client.search(
index: 'my-index-000001',
body: {
query: {
match_phrase: {
message: 'number 1'
}
},
highlight: {
fields: {
message: {
type: 'plain',
fragment_size: 15,
number_of_fragments: 3,
fragmenter: 'simple'
}
}
}
}
)
puts response const response = await client.search({
index: "my-index-000001",
query: {
match_phrase: {
message: "number 1",
},
},
highlight: {
fields: {
message: {
type: "plain",
fragment_size: 15,
number_of_fragments: 3,
fragmenter: "simple",
},
},
},
});
console.log(response); GET my-index-000001/_search
{
"query": {
"match_phrase": { "message": "number 1" }
},
"highlight": {
"fields": {
"message": {
"type": "plain",
"fragment_size": 15,
"number_of_fragments": 3,
"fragmenter": "simple"
}
}
}
} Ответ:
{
...
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.6011951,
"hits": [
{
"_index": "my-index-000001",
"_id": "1",
"_score": 1.6011951,
"_source": {
"message": "some message with the number 1",
"context": "bar"
},
"highlight": {
"message": [
" with the <em>number</em>",
" <em>1</em>"
]
}
}
]
}
} resp = client.search(
index="my-index-000001",
query={
"match_phrase": {
"message": "number 1"
}
},
highlight={
"fields": {
"message": {
"type": "plain",
"fragment_size": 15,
"number_of_fragments": 3,
"fragmenter": "span"
}
}
},
)
print(resp) response = client.search(
index: 'my-index-000001',
body: {
query: {
match_phrase: {
message: 'number 1'
}
},
highlight: {
fields: {
message: {
type: 'plain',
fragment_size: 15,
number_of_fragments: 3,
fragmenter: 'span'
}
}
}
}
)
puts response const response = await client.search({
index: "my-index-000001",
query: {
match_phrase: {
message: "number 1",
},
},
highlight: {
fields: {
message: {
type: "plain",
fragment_size: 15,
number_of_fragments: 3,
fragmenter: "span",
},
},
},
});
console.log(response); GET my-index-000001/_search
{
"query": {
"match_phrase": { "message": "number 1" }
},
"highlight": {
"fields": {
"message": {
"type": "plain",
"fragment_size": 15,
"number_of_fragments": 3,
"fragmenter": "span"
}
}
}
} Ответ:
{
...
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.6011951,
"hits": [
{
"_index": "my-index-000001",
"_id": "1",
"_score": 1.6011951,
"_source": {
"message": "some message with the number 1",
"context": "bar"
},
"highlight": {
"message": [
" with the <em>number</em> <em>1</em>"
]
}
}
]
}
} Если опция number_of_fragments установлена в 0, используется NullFragmenter, которое вообще не фрагментирует текст. Это полезно для выделения всего содержимого документа или поля.
Как работают внутренние механизмы выделения ключевых слов
Цель выделения ключевых слов — найти лучшие фрагменты текста (содержимого поля документа) для запроса и выделить в них термины запроса. Для этого выделение ключевых слов должно решить несколько вопросов:
- Как разбить текст на фрагменты?
- Как найти лучшие фрагменты среди всех фрагментов?
- Как выделить термины запроса во фрагменте?
Как разбить текст на фрагменты?
Релевантные настройки: fragment_size, fragmenter, type выделения ключевых слов, boundary_chars, boundary_max_scan, boundary_scanner, boundary_scanner_locale.
Простой выделыватель начинает с анализа текста с использованием заданного анализатора и создания потока токенов. Простой выделыватель использует очень простой алгоритм для разбивки потока токенов на фрагменты. Он проходит по терминам в потоке токенов, и каждый раз, когда конечный смещение текущего термина превышает fragment_size, умноженное на количество созданных фрагментов, создается новый фрагмент. Небольшие вычисления выполняются с использованием фрагментировщика span для предотвращения разбиения текста между выделенными терминами. Но в целом, поскольку разбиение выполняется только с помощью fragment_size, некоторые фрагменты могут быть довольно странными, например, начинаться с знака препинания.
Выделение ключевых слов Unified или FVH выполняет лучшую работу по разбивке текста на фрагменты, используя BreakIterator Java. Это гарантирует, что фрагмент является полным предложением, если это допускает fragment_size.
Как найти лучшие фрагменты?
Релевантные настройки: number_of_fragments.
Чтобы найти лучшие, наиболее релевантные фрагменты, выделение ключевых слов должно оценить каждый фрагмент по отношению к заданному запросу. Цель состоит в оценке только тех терминов, которые участвовали в создании совпадения в документе. Для некоторых сложных запросов эта задача еще находится в стадии разработки.
Простой выделыватель создает индекс в оперативной памяти из текущего потока токенов и повторно запускает исходные критерии запроса через планировщик выполнения запросов Lucene, чтобы получить доступ к информации о совпадении низкого уровня для текущего текста. Для более сложных запросов исходный запрос может быть преобразован в запрос span, так как запросы span могут более точно обрабатывать фразы. Затем полученная информация о совпадении низкого уровня используется для оценки каждого отдельного фрагмента. Метод оценки простого выделывателя довольно прост. Каждый фрагмент оценивается по количеству уникальных терминов запроса, найденных в этом фрагменте. Оценка отдельного термина равна его весу, который по умолчанию равен 1. Таким образом, по умолчанию фрагмент, содержащий один уникальный термин запроса, получит оценку 1; фрагмент, содержащий два уникальных термина запроса, получит оценку 2 и так далее. Затем фрагменты сортируются по их оценкам, поэтому фрагменты с наивысшей оценкой выводятся первыми.
FVH не нуждается в анализе текста и создании индекса в оперативной памяти, так как использует предварительно индексированные векторы терминов документов и находит среди них термины, соответствующие запросу. FVH оценивает каждый фрагмент по количеству терминов запроса, найденных в этом фрагменте. Аналогично простому выделывателю, оценка отдельного термина равна его значению усиления. В отличие от простого выделывателя, учитываются все термины запроса, а не только уникальные.
Unified выделыватель может использовать предварительно индексированные векторы терминов или предварительно индексированные смещения терминов, если они доступны. В противном случае, подобно простому выделывателю, он должен создать индекс в оперативной памяти из текста. Unified выделыватель использует модель оценки BM25 для оценки фрагментов.
Как выделить термины запроса во фрагменте?
Релевантные настройки: pre-tags, post-tags.
Цель состоит в выделении только тех терминов, которые участвовали в формировании совпадения в документе. Для некоторых сложных булевых запросов эта задача все еще находится в стадии разработки, так как выделыватели не отражают булеву логику запроса и извлекают только листовые (термины, фразы, префиксы и т. д.) запросы.
Простой выделыватель, получив поток токенов и исходный текст, восстанавливает исходный текст, чтобы выделить только термины из потока токенов, которые содержатся в структуре информации о совпадении низкого уровня из предыдущего шага.
Выделыватели FVH и Unified используют промежуточные структуры данных для представления фрагментов в некотором сыром виде, а затем заполняют их фактическим текстом.
Выделение ключевых слов использует pre-tags, post-tags для кодирования выделенных терминов.
Пример работы выделывателя Unified
Давайте рассмотрим более подробно, как работает выделыватель Unified.
Сначала создаем индекс с текстовым полем content, который будет индексироваться с помощью анализатора english и будет индексироваться без смещений или векторов терминов.
PUT test_index
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "english"
}
}
}
} Мы помещаем следующий документ в индекс:
PUT test_index/_doc/doc1
{
"content" : "For you I'm only a fox like a hundred thousand other foxes. But if you tame me, we'll need each other. You'll be the only boy in the world for me. I'll be the only fox in the world for you."
} И запускаем следующий запрос с запросом выделения:
GET test_index/_search
{
"query": {
"match_phrase" : {"content" : "only fox"}
},
"highlight": {
"type" : "unified",
"number_of_fragments" : 3,
"fields": {
"content": {}
}
}
} После того, как doc1 найден как совпадение для этого запроса, это совпадение передается выделывателю Unified для выделения поля content документа. Поскольку поле content не было индексировано с смещениями или векторами терминов, его исходное значение поля будет проанализировано, и в оперативной памяти будет создан индекс из терминов, соответствующих запросу:
{"token":"onli","start_offset":12,"end_offset":16,"position":3},
{"token":"fox","start_offset":19,"end_offset":22,"position":5},
{"token":"fox","start_offset":53,"end_offset":58,"position":11},
{"token":"onli","start_offset":117,"end_offset":121,"position":24},
{"token":"onli","start_offset":159,"end_offset":163,"position":34},
{"token":"fox","start_offset":164,"end_offset":167,"position":35} Наш сложный запрос фразы будет преобразован в запрос span: spanNear([text:onli, text:fox], 0, true), что означает, что мы ищем термины "onli:" и "fox" в пределах 0 расстояния друг от друга и в заданном порядке. Запрос span будет запущен против созданного ранее индекса в оперативной памяти, чтобы найти следующее совпадение:
{"term":"onli", "start_offset":159, "end_offset":163},
{"term":"fox", "start_offset":164, "end_offset":167} В нашем примере мы получили одно совпадение, но может быть несколько совпадений. Учитывая совпадения, выделыватель Unified разбивает текст поля на так называемые «фрагменты». Каждый фрагмент должен содержать как минимум одно совпадение. Выделыватель Unified с использованием BreakIterator Java гарантирует, что каждый фрагмент представляет собой полное предложение, пока он не превышает fragment_size. В нашем примере у нас есть один фрагмент со следующими свойствами (здесь показан только подмножество свойств):
Passage:
startOffset: 147
endOffset: 189
score: 3.7158387
matchStarts: [159, 164]
matchEnds: [163, 167]
numMatches: 2 Обратите внимание, как фрагмент имеет оценку, вычисленную с помощью формулы оценки BM25, адаптированной для фрагментов. Оценки позволяют выбрать фрагменты с наивысшей оценкой, если доступно больше фрагментов, чем запрошенное пользователем number_of_fragments. Оценки также позволяют сортировать фрагменты по order: "score", если это запрошено пользователем.
На последнем шаге выделыватель Unified извлечет из текста поля строку, соответствующую каждому фрагменту:
"I'll be the only fox in the world for you."
и отформатирует с тегами <em> и </em> все совпадения в этой строке, используя информацию о matchStarts и matchEnds фрагмента:
I'll be the <em>only</em> <em>fox</em> in the world for you.
Этот тип отформатированных строк является конечным результатом выделения ключевых слов, возвращаемым пользователю.
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/highlighting.html