关于索引
以语料:【东芝电子元件(上海)有限公司,TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION】 为例,在不同的分析器下,会生成不同的索引,具体如下。
解析器 |
分词结果 |
token数量 |
keyword |
[东芝电子元件(上海)有限公司,TOSHIBA ELECTRONIC DEVICES & STORAGE CORPORATION] |
1 |
standard |
[东,芝,电,子,元,件,上,海,有,限,公,司,toshiba,electronic,devices,storage,corporation] |
17 |
ik_max_word |
[东芝,电子元件,电子,元件,上海,有限公司,有限,公司,toshiba,electronic,devices,storage,corporation] |
13 |
ngram(1,50) |
[东,东芝,东芝电,东芝电子,东芝电子元,东芝电子元件,东芝电子元件(,东芝电子元件(上,东芝电子元件(上海,东芝电子元件(上海)…] |
1925 |
ngram(2,50) |
[东芝,东芝电,东芝电子,东芝电子元,东芝电子元件,东芝电子元件(,东芝电子元件(上,东芝电子元件(上海,东芝电子元件(上海),东芝电子元件(上海)有…] |
1862 |
自定义,仅根据空格和逗号分隔 |
[东芝电子元件(上海)有限公司,toshiba,electronic,devices,&,storage,corporation] |
7 |
各解析器特点:
- keyword:Keyword 解析器保留输入文本作为一个完整的词汇单元,不进行分词,且不主动转换大小写。
- standard:Standard 解析器根据空白字符和标点符号进行分词,并将文本转换为小写
- ik_max_word:IK Max Word 解析器是一种中文分词器,可以处理中文文本
- ngram:Ngram 解析器按照指定的最小和最大 n-gram 大小创建分词结果,这里生成的token数量较大,但实际查询使用并不一定会全部用到。
- 自定义解析器:这里自定义规则仅根据空格和逗号分隔,其生成结果的效果不一定好,可以根据实际需要进行自定义。
检索原理
参考:文本分析概念(Concepts)
搜索方式
全文检索(*Full text queries*)
查询方式 |
原始名称 |
引入版本/废弃 |
特点描述 |
匹配示例 |
匹配说明 |
备注 |
间隔查询 |
intervals |
已经废弃 |
|
|
|
|
匹配查询 |
match |
|
对字段进行全文匹配,不考虑分词 |
"hello world" |
匹配文档中包含 "hello world" 的文本 |
|
匹配布尔前缀查询 |
match_bool_prefix |
7.10.0 |
对字段进行全文匹配,只考虑前缀 |
"hel" |
匹配文档中以 "hel" 开头的文本 |
要求查询字符串中的词语顺序不变。 |
匹配短语查询 |
match_phrase |
|
对字段进行全文匹配,要求短语中的所有词语都出现,且词语顺序不变 |
"hello world" |
匹配文档中包含 "hello world" 的连续文本 |
要求查询字符串中的词语顺序不变。 |
匹配短语前缀查询 |
match_phrase_prefix |
1.4.0 |
对字段进行全文匹配,要求短语中的所有词语都出现,但词语顺序可以变 |
"hel* wor*d" |
匹配文档中包含 "hello world"、"hello worldd"、"hello worl" 等文本 |
对查询字符串中的词语顺序要求更宽松 |
多字段匹配查询 |
multi_match |
1.0.0 |
对多个字段进行全文匹配 |
"hello world" |
匹配文档中包含 "hello world" 的文本,无论该文本出现在哪个字段中 |
多字段匹配查询可以提高查询效率,但也可能会降低查询精度 |
组合字段查询 |
combined_fields |
7.4.0 |
对多个字段进行组合查询,可以是任意类型的查询 |
match("title", "hello world") |
匹配文档中包含 "hello world" 的标题 |
可以实现更复杂的查询逻辑 |
字符串查询 |
query_string |
|
使用查询字符串进行查询 |
"title:hello OR content:world" |
匹配文档中标题包含 "hello" 或内容包含 "world" |
可以查询字符串,但也可能会降低查询效率。 |
简单查询字符串查询 |
simple_query_string |
|
简化版的查询字符串查询 |
"hello world" |
匹配文档中包含 "hello world" 的文本 |
字符串查询的简化版,功能更弱 |
Term查询
查询方式 |
原始名称 |
引入版本 |
特点描述 |
匹配示例 |
匹配描述 |
备注 |
存在查询 |
exists |
0.90.0 |
查询字段是否存在 |
exists("title") |
匹配所有包含标题的文档 |
|
模糊查询 |
fuzzy |
0.10.0 |
模糊匹配 |
fuzzy("title", "hello world") |
匹配包含 "hello world" 的文档,允许部分文本不匹配 |
允许部分文本不匹配 |
ID 查询 |
ids |
0.10.0 |
按文档 ID 查询 |
ids("1234567890", "9876543210") |
匹配文档 ID 为 "1234567890" 或 "9876543210" 的文档 |
|
前缀查询 |
prefix |
0.10.0 |
前缀匹配 |
prefix("title", "hello") |
匹配标题以 "hello" 开头的文档 |
用于前缀匹配 |
范围查询 |
range |
0.10.0 |
范围匹配 |
range("age", 18, 25) |
匹配年龄在 18 到 25 岁之间的文档 |
用于范围匹配 |
正则表达式查询 |
regexp |
0.10.0 |
正则表达式匹配 |
regexp("title", "/hello/i") |
匹配标题包含 "hello" 的文档,不区分大小写 |
用于正则表达式匹配 |
精确查询 |
term |
0.10.0 |
精确匹配 |
term("title", "hello world") |
匹配标题等于 "hello world" 的文档 |
精确匹配 |
多值精确查询 |
terms |
0.10.0 |
多值精确匹配 |
terms("title", ["hello world", "goodbye world"]) |
匹配标题等于 "hello world" 或 "goodbye world" 的文档 |
多值精确匹配 |
多值精确查询(不区分顺序) |
terms set |
6.5.0 |
多值精确匹配,不区分顺序 |
terms_set("title", ["hello world", "goodbye world"]) |
匹配标题包含 "hello world" 或 "goodbye world" 的文档,不区分顺序 |
用于多值精确匹配,不区分顺序 |
通配符查询 |
wildcard |
0.10.0 |
通配符匹配 |
wildcard("title", "he*o") |
匹配标题以 "he" 开头,以 "o" 结尾的文档 |
用于通配符匹配 |
复合查询
查询方式 |
原始名称 |
特点描述 |
使用示例 |
匹配描述 |
备注 |
布尔查询 |
bool |
允许组合多个查询 |
{"bool":{"must":{"term":{"http://user.id/":"kimchy"}},"filter":{"term":{"tags":"production"}},"must_not":{"range":{"age":{"gte":10,"lte":20}}},"should":[{"term":{"tags":"env1"}},{"term":{"tags":"deployed"}}],"minimum_should_match":1,"boost":1.0}} |
匹配满足逻辑条件的记录 |
最常用的查询方式 |
提升查询 |
boosting |
为某些查询结果赋予更高的权重 |
{"boosting":{"positive":{"term":{"text":"apple"}},"negative":{"term":{"text":"pie tart fruit crumble tree"}},"negative_boost":0.5}} |
将包含关键词 "apple" 的文档加权提升,同时通过降低包含词组 "pie tart fruit crumble tree" 的文档的权重 |
可以用于突出显示某些查询结果 |
恒定得分查询 |
constant_score |
为所有结果赋予相同的得分 |
{"constant_score":{"filter":{"term":{"http://user.id/":"kimchy"}},"boost":1.2}} |
匹配标题包含 "hello" 的文档,并为所有结果赋予相同的得分 |
可以用于提高查询效率 |
最大值查询 |
dis_max |
从多个查询中返回得分最高的结果 |
{"dis_max":{"queries":[{"term":{"title":"Quick pets"}},{"term":{"body":"Quick pets"}}],"tie_breaker":0.7}} |
通过dis_max实现了对标题和正文中包含"Quick pets"的文档进行检索,使用0.7的权重来平衡两个查询的得分。 |
可以用于提高查询灵活性 |
函数得分查询 |
function_score |
使用自定义函数为结果赋予得分 |
{"function_score":{"query":{"match_all":{}},"boost":"5","random_score":{},"boost_mode":"multiply"}} |
使用函数分数查询,在匹配所有文档的基础上,通过乘法方式将随机分数与指定的增益因子相乘。 |
可以用于实现复杂的查询逻辑 |
Geo查询
查询方式 |
原始名称 |
特点描述 |
使用示例 |
匹配描述 |
地理边界查询 |
geo_bounding_box |
匹配指定范围内的地理位置 |
{"geo_bounding_box":{"pin.location":{"top_left":{"lat":40.73,"lon":-74.1},"bottom_right":{"lat":40.01,"lon":-71.12}}}} |
匹配坐标范围在 [40.73, -74.1] 到 [40.01, -71.12] 内的文档 |
地理距离查询 |
geo_distance |
匹配指定距离内的地理位置 |
{"geo_distance":{"distance":"200km","pin.location":{"lat":40,"lon":-70}}} |
匹配距离坐标 (40, -70) 在 200 公里内的文档 |
地理网格查询 |
geo_grid |
匹配指定网格内的地理位置 |
{"geo_grid":{"location":{"geohash":"u0"}}} |
匹配坐标位于网格单元中的文档 |
地理多边形查询 |
geo_polygon |
匹配指定多边形内的地理位置 |
{"geo_polygon":{"person.location":{"points":[{"lat":40,"lon":-70},{"lat":30,"lon":-80},{"lat":20,"lon":-90}]}}} |
匹配坐标位于多边形内的文档 |
地理形状查询 |
geo_shape |
匹配指定形状内的地理位置 |
{"geo_shape":{"location":{"shape":{"type":"envelope","coordinates":[[13.0,53.0],[14.0,52.0]]},"relation":"intersects"}}} |
匹配坐标位于形状内的文档,形状可以是多边形、圆形或矩形 |
形状查询
查询方式 |
原始名称 |
特点描述 |
使用示例 |
匹配描述 |
形状查询 |
shape |
匹配指定形状的文档 |
{"shape":{"type":"envelope","coordinates":[[1355.0,5355.0],[1400.0,5200.0]]}} |
匹配坐标位于多边形内的文档,形状可以是多边形、圆形或矩形 |
Join查询
查询方式 |
原始名称 |
特点描述 |
使用示例 |
匹配描述 |
实际示例 |
嵌套查询 |
nested |
用于匹配嵌套文档 |
nested("products", "price", gte(100)) |
匹配嵌套在 "products" 字段中的文档,且 "price" 字段的值大于或等于 100 |
{"nested":{"path":"obj1","query":{"bool":{"must":[{"match":{"http://obj1.name/":"blue"}},{"range":{"obj1.count":{"gt":5}}}]}},"score_mode":"avg"}} |
子文档存在查询 |
has_child |
用于匹配具有子文档的文档 |
has_child("products") |
匹配具有嵌套在 "products" 字段中的文档 |
{"has_child":{"type":"child","query":{"match_all":{}},"max_children":10,"min_children":2,"score_mode":"min"}} |
父文档存在查询 |
has_parent |
用于匹配具有父文档的文档 |
has_parent("orders") |
匹配具有嵌套在 "orders" 字段中的文档 |
{"has_parent":{"parent_type":"parent","query":{"term":{"tag":{"value":"Elasticsearch"}}}}} |
父文档 ID 查询 |
parent_id |
用于匹配指定父文档 ID 的文档 |
parent_id("orders", "1234567890") |
匹配父文档 ID 为 "1234567890" 的文档 |
{"parent_id":{"type":"my-child","id":"1"}} |
Span查询(跨度查询)
Span Query(跨度查询)是一种在文档中查找特定跨度或位置的查询方式。它用于匹配文本中的一段连续片段,这些片段需要满足一定的条件。跨度查询通常用于更复杂的文本匹配需求,例如要求匹配的词语在文档中以特定的顺序或距离出现。
Span Query允许你指定一系列的条件,包括词语的存在、相对位置、顺序等。这样的查询可以用于精确地定位文档中的某些文本片段,而不仅仅是简单的词语匹配。跨度查询在信息检索领域中常用于处理更复杂的语义关系和语境匹配。
查询方式 |
原始名称 |
特点描述 |
使用示例 |
匹配描述 |
span 包含查询 |
span_containing |
匹配包含指定文本的span |
span_containing("title", "hello world") |
匹配标题包含 "hello world" 的span |
span 字段遮罩查询 |
span_field_masking |
允许指定span的字段 |
span_field_masking("title", ["name", "age"]) |
匹配标题的span,但只返回 "name" 和 "age" 字段的值 |
span 首个查询 |
span_first |
匹配文档中第一个匹配的span |
span_first("title", "hello world") |
匹配文档中第一个标题包含 "hello world" 的span |
span 多值查询 |
span_multi |
匹配文档中多个匹配的span |
span_multi("title", ["hello world", "goodbye world"]) |
匹配文档中标题包含 "hello world" 或 "goodbye world" 的span |
span 邻近查询 |
span_near |
匹配文档中邻近的span |
span_near("title", ["hello world", "goodbye world"], 2) |
匹配文档中标题包含 "hello world" 和 "goodbye world" 的span,且这两个span的距离不超过 2 |
span 不包含查询 |
span_not |
匹配不包含指定文本的span |
span_not("title", "hello world") |
匹配标题不包含 "hello world" 的span |
span 或查询 |
span_or |
匹配多个span之一 |
span_or("title", ["hello world", "goodbye world"]) |
匹配文档中包含 "hello world" 或 "goodbye world" 的任意一个span |
span 术语查询 |
span_term |
匹配指定术语的span |
span_term("title", "hello world") |
匹配标题包含术语 "hello world" 的span |
span 范围查询 |
span_within |
匹配范围内的span |
span_within("title", ["hello", "world"], ["goodbye", "world"]) |
匹配标题包含术语 "hello" 或 "world" 的span,但不包含术语 "goodbye" 或 "world" |
专用查询(Specialized queries)
"Specialized queries"(专用查询)通常指的是在搜索引擎或数据库中,为特定的需求或数据类型而设计的特殊查询方式。这些查询被定制或优化,以满足特定的应用场景、数据结构或搜索需求,以提高检索的效率和准确性。
在搜索引擎或数据库的上下文中,"Specialized queries" 可能涉及到针对特殊数据类型的查询,如地理数据查询、时间序列数据查询、全文搜索查询等。这些查询通常利用特定的索引结构、算法或优化策略,以更好地处理特定类型的数据。
查询方式 |
原始名称 |
特点描述 |
使用示例 |
匹配描述 |
距离特征查询 |
distance_feature |
为文档添加距离特征 |
distance_feature("location", "127.0,37.0", 1000) |
为文档添加距离特征,距离为 (127.0,37.0) 的最近 1000 米内的文档 |
相似度查询 |
more_like_this |
查找与给定文档相似的文档 |
more_like_this("title", ["hello world", "goodbye world"]) |
查找与文档标题包含 "hello world" 或 "goodbye world" 的文档相似的文档 |
渗透查询 |
percolate |
查找与给定过滤器匹配的文档 |
percolate("filter", "title:hello world") |
查找与过滤器匹配的文档,过滤器为 "title:hello world" |
排名特征查询 |
rank_feature |
为文档添加排名特征 |
rank_feature("popularity") |
为文档添加排名特征,排名根据 "popularity" 字段的值 |
脚本查询 |
script |
使用脚本自定义查询逻辑 |
script(script_source, script_params) |
使用脚本自定义查询逻辑,脚本源为 "script_source",脚本参数为 "script_params" |
脚本得分查询 |
script_score |
使用脚本为文档赋予得分 |
script_score(script_source, script_params) |
使用脚本为文档赋予得分,脚本源为 "script_source",脚本参数为 "script_params" |
包装器查询 |
wrapper |
包装其他查询 |
wrapper(query, wrapper_source) |
包装其他查询,查询为 "query",包装源为 "wrapper_source" |
固定查询 |
pinned |
固定查询结果 |
pinned("query") |
固定查询结果,查询为 "query" |
规则查询 |
rule |
使用规则匹配文档 |
rule(rule_source) |
使用规则匹配文档,规则源为 "rule_source" |
使用场景分析
这里首先要去分一个概念,全文匹配与结构化检索。其中,Term相关的匹配查询都属于结构化检索,这个可以理解为将ES用作MySQL类似的数据库查询,只不过语法有所区别;而全文检索的场景有所不同,全文查询通常用于在全文本字段(如电子邮件正文)上运行全文查询。他们了解如何对被查询的字段进行分析,并在执行前将每个字段的分析器(analyzer)应用于查询字符串。
首先一个具体表现就是,对于全文检索,其搜索的词是要被analyzer分词,将分词得到的token跟原始的索引进行匹配;结构化搜索的时候,提供的搜索词不会被分词,会直接跟原始索引进行匹配。这里其实就体现出匹配比较关键的两个点,其一是构建什么样的索引才能被匹配,其二是应该用什么样的搜索方式进行查询,这两者都需要结合实际业务场景进行合理选择。
评论区