高亮
编辑高亮编辑
高亮器使您能够从搜索结果中的一个或多个字段中获取高亮显示的片段,以便您可以向用户显示查询匹配的位置。当您请求高亮显示时,响应中会包含一个额外的 highlight
元素,用于每个包含高亮显示字段和高亮显示片段的搜索结果。
高亮器在提取要高亮显示的词语时,不会反映查询的布尔逻辑。因此,对于一些复杂的布尔查询(例如嵌套布尔查询、使用 minimum_should_match
的查询等),文档中与查询匹配项不对应的部分可能会被高亮显示。
高亮显示需要字段的实际内容。如果该字段未存储(映射未将 store
设置为 true
),则会加载实际的 _source
,并从 _source
中提取相关字段。
例如,要使用默认高亮器获取每个搜索结果中 content
字段的高亮显示,请在请求正文中包含一个指定 content
字段的 highlight
对象。
response = client.search( body: { query: { match: { content: 'kimchy' } }, highlight: { fields: { content: {} } } } ) puts response
GET /_search { "query": { "match": { "content": "kimchy" } }, "highlight": { "fields": { "content": {} } } }
Elasticsearch 支持三种高亮器:unified
、plain
和 fvh
(快速向量高亮器)。您可以为每个字段指定要使用的 type
高亮器。
统一高亮器编辑
unified
高亮器使用 Lucene 统一高亮器。此高亮器将文本分解为句子,并使用 BM25 算法对单个句子进行评分,就好像它们是语料库中的文档一样。它还支持精确的短语和多词(模糊、前缀、正则表达式)高亮显示。这是默认的高亮器。
普通高亮器编辑
plain
高亮器使用标准的 Lucene 高亮器。它试图在理解词语重要性和短语查询中的任何词语定位标准方面反映查询匹配逻辑。
plain
高亮器最适合于高亮显示单个字段中的简单查询匹配。为了准确反映查询逻辑,它会创建一个微型的内存索引,并通过 Lucene 的查询执行计划程序重新运行原始查询标准,以便访问当前文档的低级匹配信息。对于需要高亮显示的每个字段和每个文档,都会重复此过程。如果要使用复杂查询高亮显示大量文档中的大量字段,建议对 postings
或 term_vector
字段使用 unified
高亮器。
快速向量高亮器编辑
fvh
高亮器使用 Lucene 快速向量高亮器。此高亮器可用于映射中 term_vector
设置为 with_positions_offsets
的字段。快速向量高亮器
- 可以使用
boundary_scanner
进行自定义。 - 需要将
term_vector
设置为with_positions_offsets
,这会增加索引的大小。 - 可以将多个字段的匹配项合并到一个结果中。请参阅
matched_fields
。 - 可以为不同位置的匹配项分配不同的权重,例如在高亮显示提升短语匹配高于词语匹配的 Boosting 查询时,将短语匹配排序在词语匹配之上。
fvh
高亮器不支持跨度查询。如果需要支持跨度查询,请尝试使用其他高亮器,例如 unified
高亮器。
偏移量策略编辑
为了从被查询的词语中创建有意义的搜索片段,高亮器需要知道原始文本中每个词语的开始和结束字符偏移量。这些偏移量可以从以下位置获取:
- 倒排列表。如果在映射中将
index_options
设置为offsets
,则unified
高亮器将使用此信息来高亮显示文档,而无需重新分析文本。它直接在倒排列表上重新运行原始查询,并从索引中提取匹配的偏移量,将集合限制为高亮显示的文档。如果您的字段很大,这一点很重要,因为它不需要重新分析要高亮显示的文本。与使用term_vectors
相比,它还需要更少的磁盘空间。 - 词向量。如果通过在映射中将
term_vector
设置为with_positions_offsets
来提供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
高亮器有效。对于unified
高亮器,默认为sentence
。对于fvh
高亮器,默认为chars
。-
chars
- 使用
boundary_chars
指定的字符作为高亮显示边界。boundary_max_scan
设置控制扫描边界字符的距离。仅对fvh
高亮器有效。 -
sentence
-
在下一个句子边界处断开高亮显示的片段,由 Java 的 BreakIterator 确定。您可以使用
boundary_scanner_locale
指定要使用的语言环境。与
unified
高亮器一起使用时,sentence
扫描器会在fragment_size
旁边的第一个单词边界处拆分大于fragment_size
的句子。您可以将fragment_size
设置为 0 以便永远不会拆分任何句子。 -
word
- 在下一个单词边界处断开高亮显示的片段,由 Java 的 BreakIterator 确定。您可以使用
boundary_scanner_locale
指定要使用的语言环境。
-
- boundary_scanner_locale
- 控制使用哪种语言环境来搜索句子和单词边界。此参数采用语言标记的形式,例如
"en-US"
、"fr-FR"
、"ja-JP"
。更多信息可以在 语言环境语言标记 文档中找到。默认值为 Locale.ROOT。 - encoder
- 指示是否应将代码段进行 HTML 编码:
default
(不编码)或html
(对代码段文本进行 HTML 转义,然后插入高亮显示标记)。 - fields
-
指定要为其检索高亮显示的字段。您可以使用通配符来指定字段。例如,您可以指定
comment_*
以获取所有以comment_
开头的 文本、match_only_text 和 关键字 字段的高亮显示。当您使用通配符时,只有文本、match_only_text 和关键字字段会被高亮显示。如果您使用自定义映射器并希望对某个字段进行高亮显示,则必须显式指定该字段名称。
- fragmenter
- 指定如何在高亮显示片段中拆分文本:
simple
或span
。仅对plain
高亮器有效。默认为span
。 - force_source
-
已弃用;此参数无效。
-
simple
- 将文本分成大小相同的片段。
-
span
- 将文本分成大小相同的片段,但尽量避免在高亮显示的词语之间拆分文本。当您查询短语时,这很有用。默认值。
-
- fragment_offset
- 控制要开始高亮显示的边距。仅在使用
fvh
高亮器时有效。 - fragment_size
- 高亮显示片段的大小(以字符为单位)。默认为 100。
- highlight_query
-
高亮显示除搜索查询以外的查询的匹配项。如果您使用重新评分查询,这将特别有用,因为默认情况下,高亮显示不会考虑这些查询。
Elasticsearch 不会以任何方式验证
highlight_query
是否包含搜索查询,因此可以将其定义为不突出显示合法的查询结果。通常,您应该将搜索查询包含在highlight_query
中。 - matched_fields
- 组合多个字段上的匹配项以突出显示单个字段。这对于以不同方式分析相同字符串的多字段最直观。所有
matched_fields
都必须将term_vector
设置为with_positions_offsets
,但只会加载组合了匹配项的字段,因此只有该字段才能从将store
设置为yes
中受益。仅对fvh
高亮器有效。 - no_match_size
- 如果字段开头没有匹配的片段需要高亮显示,则从此处返回的文本量。默认为 0(不返回任何内容)。
- number_of_fragments
- 要返回的最大片段数。如果片段数设置为 0,则不返回任何片段。相反,将高亮显示并返回整个字段内容。当您需要高亮显示短文本(如标题或地址)但不需要分段时,这非常方便。如果
number_of_fragments
为 0,则忽略fragment_size
。默认为 5。 - order
- 设置为
score
时,按分数对高亮显示的片段进行排序。默认情况下,片段将按照它们在字段中出现的顺序输出(order:none
)。将此选项设置为score
将首先输出最相关的片段。每个高亮显示器都应用其自身的逻辑来计算相关性分数。有关不同高亮显示器如何找到最佳片段的更多详细信息,请参阅文档高亮显示器如何在内部工作。 - 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">
高亮显示示例编辑
覆盖全局设置编辑
您可以全局指定高亮显示器设置,并为各个字段选择性地覆盖它们。
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
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
,高亮显示将只考虑搜索查询。
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
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
。以下是一个强制使用普通高亮显示器的示例
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { fields: { comment: { type: 'plain' } } } } ) puts response
GET /_search { "query": { "match": { "user.id": "kimchy" } }, "highlight": { "fields": { "comment": { "type": "plain" } } } }
配置高亮显示标记编辑
默认情况下,高亮显示会将高亮显示的文本包含在 <em>
和 </em>
中。这可以通过设置 pre_tags
和 post_tags
来控制,例如
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { pre_tags: [ '<tag1>' ], post_tags: [ '</tag1>' ], fields: { body: {} } } } ) puts response
GET /_search { "query" : { "match": { "user.id": "kimchy" } }, "highlight" : { "pre_tags" : ["<tag1>"], "post_tags" : ["</tag1>"], "fields" : { "body" : {} } } }
使用快速向量高亮显示器时,您可以指定其他标记,并且“重要性”是有序的。
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { pre_tags: [ '<tag1>', '<tag2>' ], post_tags: [ '</tag1>', '</tag2>' ], fields: { body: {} } } } ) puts response
GET /_search { "query" : { "match": { "user.id": "kimchy" } }, "highlight" : { "pre_tags" : ["<tag1>", "<tag2>"], "post_tags" : ["</tag1>", "</tag2>"], "fields" : { "body" : {} } } }
您还可以使用内置的 styled
标记架构
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { tags_schema: 'styled', fields: { comment: {} } } } ) puts response
GET /_search { "query" : { "match": { "user.id": "kimchy" } }, "highlight" : { "tags_schema" : "styled", "fields" : { "comment" : {} } } }
高亮显示所有字段编辑
默认情况下,仅高亮显示包含查询匹配项的字段。将 require_field_match
设置为 false
可高亮显示所有字段。
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { require_field_match: false, fields: { body: { pre_tags: [ '<em>' ], post_tags: [ '</em>' ] } } } } ) puts response
GET /_search { "query" : { "match": { "user.id": "kimchy" } }, "highlight" : { "require_field_match": false, "fields": { "body" : { "pre_tags" : ["<em>"], "post_tags" : ["</em>"] } } } }
组合多个字段上的匹配项编辑
这仅受 fvh
高亮显示器支持
快速向量高亮显示器可以组合多个字段上的匹配项以高亮显示单个字段。这对于以不同方式分析相同字符串的多字段最直观。所有 matched_fields
都必须将 term_vector
设置为 with_positions_offsets
,但只会加载匹配项组合到的字段,因此只有该字段才能从将 store
设置为 yes
中受益。
在以下示例中,comment
由 english
分析器分析,comment.plain
由 standard
分析器分析。
response = client.search( body: { query: { query_string: { query: 'comment.plain:running scissors', fields: [ 'comment' ] } }, highlight: { order: 'score', fields: { comment: { matched_fields: [ 'comment', 'comment.plain' ], type: 'fvh' } } } } ) puts response
GET /_search { "query": { "query_string": { "query": "comment.plain:running scissors", "fields": [ "comment" ] } }, "highlight": { "order": "score", "fields": { "comment": { "matched_fields": [ "comment", "comment.plain" ], "type": "fvh" } } } }
以上内容同时匹配“run with scissors”和“running with scissors”,并且会高亮显示“running”和“scissors”,但不会高亮显示“run”。如果两个短语都出现在一个大文档中,则“running with scissors”在片段列表中的排序高于“run with scissors”,因为该片段中有更多匹配项。
response = client.search( body: { query: { query_string: { query: 'running scissors', fields: [ 'comment', 'comment.plain^10' ] } }, highlight: { order: 'score', fields: { comment: { matched_fields: [ 'comment', 'comment.plain' ], type: 'fvh' } } } } ) puts response
GET /_search { "query": { "query_string": { "query": "running scissors", "fields": ["comment", "comment.plain^10"] } }, "highlight": { "order": "score", "fields": { "comment": { "matched_fields": ["comment", "comment.plain"], "type" : "fvh" } } } }
以上内容高亮显示了“run”以及“running”和“scissors”,但仍然将“running with scissors”的排序高于“run with scissors”,因为普通匹配项(“running”)得到了提升。
response = client.search( body: { query: { query_string: { query: 'running scissors', fields: [ 'comment', 'comment.plain^10' ] } }, highlight: { order: 'score', fields: { comment: { matched_fields: [ 'comment.plain' ], type: 'fvh' } } } } ) puts response
GET /_search { "query": { "query_string": { "query": "running scissors", "fields": [ "comment", "comment.plain^10" ] } }, "highlight": { "order": "score", "fields": { "comment": { "matched_fields": [ "comment.plain" ], "type": "fvh" } } } }
以上查询不会高亮显示“run”或“scissor”,但表明不在匹配字段中列出匹配项组合到的字段(comment
)是可以的。
从技术上讲,将字段添加到 matched_fields
中也是可以的,这些字段与匹配项组合到的字段不共享相同的底层字符串。结果可能没有多大意义,如果其中一个匹配项位于文本末尾,则整个查询将失败。
将 matched_fields
设置为非空数组会涉及少量开销,因此始终首选
"highlight": { "fields": { "comment": {} } }
到
"highlight": { "fields": { "comment": { "matched_fields": ["comment"], "type" : "fvh" } } }
显式排序高亮显示的字段编辑
Elasticsearch 按照发送字段的顺序高亮显示它们,但根据 JSON 规范,对象的顺序是未定义的。如果您需要明确高亮显示字段的顺序,请将 fields
指定为数组
response = client.search( body: { highlight: { fields: [ { title: {} }, { text: {} } ] } } ) puts response
GET /_search { "highlight": { "fields": [ { "title": {} }, { "text": {} } ] } }
Elasticsearch 中内置的任何高亮显示器都不关心高亮显示字段的顺序,但插件可能会关心。
控制高亮显示的片段编辑
每个高亮显示的字段都可以控制高亮显示片段的大小(以字符为单位,默认为 100
)和要返回的最大片段数(默认为 5
)。例如
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { fields: { comment: { fragment_size: 150, number_of_fragments: 3 } } } } ) puts response
GET /_search { "query" : { "match": { "user.id": "kimchy" } }, "highlight" : { "fields" : { "comment" : {"fragment_size" : 150, "number_of_fragments" : 3} } } }
除此之外,还可以指定需要按分数对高亮显示的片段进行排序
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { order: 'score', fields: { comment: { fragment_size: 150, number_of_fragments: 3 } } } } ) puts 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
。
response = client.search( body: { query: { match: { 'user.id' => 'kimchy' } }, highlight: { fields: { body: {}, 'blog.title' => { number_of_fragments: 0 } } } } ) puts response
GET /_search { "query" : { "match": { "user.id": "kimchy" } }, "highlight" : { "fields" : { "body" : {}, "blog.title" : {"number_of_fragments" : 0} } } }
使用 fvh
时,可以使用 fragment_offset
参数来控制开始高亮显示的边距。
如果没有匹配的片段需要高亮显示,则默认情况下不返回任何内容。相反,我们可以通过将 no_match_size
(默认为 0
)设置为要返回的文本长度,从字段的开头返回一段文本。实际长度可能比指定的长度短或长,因为它会尝试在单词边界处断开。
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
GET /_search { "query": { "match": { "user.id": "kimchy" } }, "highlight": { "fields": { "comment": { "fragment_size": 150, "number_of_fragments": 3, "no_match_size": 150 } } } }
使用倒排列表进行高亮显示编辑
以下是在索引映射中设置 comment
字段以允许使用倒排列表进行高亮显示的示例
response = client.indices.create( index: 'example', body: { mappings: { properties: { comment: { type: 'text', index_options: 'offsets' } } } } ) puts response
PUT /example { "mappings": { "properties": { "comment" : { "type": "text", "index_options" : "offsets" } } } }
以下是如何设置 comment
字段以允许使用 term_vectors
进行高亮显示的示例(这将导致索引更大)
response = client.indices.create( index: 'example', body: { mappings: { properties: { comment: { type: 'text', term_vector: 'with_positions_offsets' } } } } ) puts response
PUT /example { "mappings": { "properties": { "comment" : { "type": "text", "term_vector" : "with_positions_offsets" } } } }
为普通高亮显示器指定分段器编辑
使用 plain
高亮显示器时,您可以选择 simple
和 span
分段器
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
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>" ] } } ] } }
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
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
。
普通高亮器首先使用给定的分析器分析文本,并从中创建一个标记流。普通高亮器使用一个非常简单的算法将标记流分解成片段。它循环遍历标记流中的词项,每当当前词项的 end_offset 超过 fragment_size
乘以已创建片段的数量时,就会创建一个新的片段。使用 span
分词器可以做更多的计算,以避免在高亮词项之间断开文本。但总的来说,由于断开只由 fragment_size
完成,因此某些片段可能相当奇怪,例如以标点符号开头。
统一高亮器或 FVH 高亮器通过利用 Java 的 BreakIterator
将文本分解成片段,做得更好。这确保了只要 fragment_size
允许,片段就是一个有效的句子。
如何找到最佳片段?编辑
相关设置:number_of_fragments
。
为了找到最佳、最相关的片段,高亮器需要根据给定的查询对每个片段进行评分。目标是仅对参与生成文档“命中”的词项进行评分。对于某些复杂的查询,这仍在进行中。
普通高亮器从当前标记流创建内存索引,并通过 Lucene 的查询执行计划程序重新运行原始查询条件,以获取当前文本的低级匹配信息。对于更复杂的查询,可以将原始查询转换为跨度查询,因为跨度查询可以更准确地处理短语。然后,使用此获得的低级匹配信息对每个片段进行评分。普通高亮器的评分方法非常简单。每个片段的分数由该片段中找到的唯一查询词项的数量决定。单个词项的分数等于其提升值,默认情况下为 1。因此,默认情况下,包含一个唯一查询词项的片段将获得 1 分;包含两个唯一查询词项的片段将获得 2 分,依此类推。然后按分数对片段进行排序,因此得分最高的片段将首先输出。
FVH 不需要分析文本并构建内存索引,因为它使用预先索引的文档词项向量,并在其中找到与查询相对应的词项。FVH 根据在该片段中找到的查询词项的数量对每个片段进行评分。与普通高亮器类似,单个词项的分数等于其提升值。与普通高亮器相比,所有查询词项都会被计算在内,而不仅仅是唯一的词项。
如果可用,统一高亮器可以使用预先索引的词项向量或预先索引的词项偏移量。否则,与普通高亮器类似,它必须从文本创建内存索引。统一高亮器使用 BM25 评分模型对片段进行评分。
如何在片段中高亮显示查询词项?编辑
相关设置:pre-tags
、post-tags
。
目标是仅高亮显示参与生成文档“命中”的词项。对于某些复杂的布尔查询,这仍在进行中,因为高亮器不反映查询的布尔逻辑,而仅提取叶(词项、短语、前缀等)查询。
普通高亮器在给定标记流和原始文本的情况下,会重新组合原始文本,以仅高亮显示来自标记流的词项,这些词项包含在上一步的低级匹配信息结构中。
FVH 和统一高亮器使用中间数据结构以某种原始形式表示片段,然后用实际文本填充它们。
高亮器使用 pre-tags
、post-tags
对高亮显示的词项进行编码。
统一高亮器工作原理示例编辑
让我们更详细地了解统一高亮器是如何工作的。
首先,我们创建一个包含文本字段 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
作为此查询的匹配项后,此匹配项将传递给统一高亮器,以高亮显示文档的 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}
我们的复杂短语查询将转换为跨度查询:spanNear([text:onli, text:fox], 0, true)
,这意味着我们正在寻找彼此之间距离为 0 且按给定顺序排列的词项“onli:”和“fox”。跨度查询将在之前创建的内存索引上运行,以找到以下匹配项
{"term":"onli", "start_offset":159, "end_offset":163}, {"term":"fox", "start_offset":164, "end_offset":167}
在我们的示例中,我们找到了一个匹配项,但可能有多个匹配项。给定匹配项,统一高亮器会将字段的文本分解为所谓的“段落”。每个段落必须至少包含一个匹配项。使用 Java 的 BreakIterator
的统一高亮器可确保每个段落代表一个完整的句子,只要它不超过 fragment_size
。对于我们的示例,我们得到了一个具有以下属性的段落(此处仅显示属性的子集)
Passage: startOffset: 147 endOffset: 189 score: 3.7158387 matchStarts: [159, 164] matchEnds: [163, 167] numMatches: 2
请注意,段落如何具有使用适用于段落的 BM25 评分公式计算的分数。如果可用段落数量超过用户请求的 number_of_fragments
,则分数允许我们选择得分最高的段落。如果用户请求,分数还允许我们按 order: "score"
对段落进行排序。
最后,统一高亮器将从字段的文本中提取与每个段落相对应的字符串
"I'll be the only fox in the world for you."
并将使用段落的 matchStarts
和 matchEnds
信息使用标签 <em> 和 </em> 格式化此字符串中的所有匹配项
I'll be the <em>only</em> <em>fox</em> in the world for you.
这种格式化的字符串是高亮器返回给用户的最终结果。