Elasticsearch 探索
1. 基于词项和基于全文的检索
1.1 基于 Term 查询
- Term 是语义的最小单位,Term Level Query 包括了 Term Query / Range Query / Exists Query / Prefix Query /Wildcard Query 等。由
query
嵌套term
组成。 - 在 es 中的 Term 不会对输入进行分词,而是将输入作为一个整体,在倒排索引中查找准确的词项,并且使⽤相关度算分公式,为每个包含该词项的⽂档进⾏相关度算分。
- 可以通过 Constant Score 将查询转换成一个 Filtering,避免算分,利用了缓存。
- Term 查询不会做分词,如果希望完全匹配,应该用
xxx.keyword
字段来进行查询。 query
内嵌套constant_score
和filter
可以将 Query 转换成 Fileter,忽略 TF-IDF 计算,避免算分性能开销。
1.2 全文查询
- 全文查询包括了 Match Query / Match Phrase Query / Query String Query 等。由
query
嵌套match
组成。 - 索引和搜索对会进行分词,查询字符串会先传递到一个分词器,然后生成一个可以查询的词项列表。
- 查询时,会先对输入进行分词,然后逐个进行底层查询,最终合并结果,并为每个文档生成一个算分。
- 可以通过
operator
对分词关系进行控制,或者直接使用+
-
符号也一样。 - 可以通过
minimum_should_match
参数控制最小匹配,用~
符号也一样。 - 可以通过
match_phrase
嵌套slop
允许误差,设置范围。
2. 结构化搜索
- 所谓结构化的数据指日期、布尔、数字,文本也可以是结构化的,比如颜色、标签、标识。
- 结构化数据即有精确的格式,可以逻辑操作,同时有范围课比较。
- 结构话数据可以 Term 查询或 Perfix 前缀查询。
- 结构化的结构只有是或否两个值。
- 在
query
中嵌套constant_score
和filter
和range
可以进行范围查询。gte
是大于等于,lte
是小于等于。 - 在日期的查询中可以使用
now-1y
这样的语法,y 是年,M 是月,w 是周,d 是天,H/h 是小时,m 是分钟,s 是秒。 - 可以通过
constant_score
嵌套filter
和exists
来确定非空字段。 - es 中的 Term 查询是包含而不是相等,需要增加一个 count 字段,通过 bool query 进行精确匹配。
3. 搜索的相关性算分
- 相关性是指一个文档和语句的匹配程度,es 5 前使用 TF-IDF,之后使用 BM25。
- Term Frequency 检索词在文档中出现的词频。
- 最简单的方式是将各个词的 TF 相加,需要考虑一些 Stop Word 是否需要算进去。
- Document Frequency 检索词在所有文档中出现的词频。
- Inverse Document Frequency 是 log(全部文档数/检索词出现的文档数)。从 IDF 中可以知道一个词的重要性。
- TF-IDF 算法本质上是 TF 求和变成加权求和。
- 将各个词的 TF*IDF 相加求和。
- BM25 在 TF 无限增加时,TF-IDF 会不断增加分值,而 BM25 会趋向一个值。
- 在 lucene 中有 boosting 的概念,它也会影响打分,可以在
query
下嵌套boosting
使用。- 当 boosting > 1 时,打分的相关度提升
- 当 0 < boosting < 1 时,打分相关性下降
- 当 boosting < 0 时,贡献为负
4. Query & Filtering 与多字符串多字段查询
- 在 es 中有 Query 和 Filter 两种不同的上下文。
- Query 有相关性算分。
- Filter 不需要算分。
- bool query 在
query
中嵌套bool
使用,是一个或多个查询的组合。- must 必须满足条件,算分。
- should 选择性匹配,算分。
- must_not 必须不能匹配,Filter Context 中不算分。
- filter 必须匹配,Filter COntext 中不算分。
bool
下可以嵌套 must should 等四种等级继续嵌套bool
,这样算分的等级就变了。- 同样通过设置
boost
的值也能影响算分的权重。
- 可以在
query
中嵌套boosting
进行查询,可以指定positive
项、negative
项,同时可以指定negative_boost
的值,他们都是在影响 Query 的算分结果,最后得到的排名会有变化。
5. 单字符串多字段查询 Dis Max Query
1 | PUT /blogs/_doc/1 |
在上面的例子中,我们希望第二个查询排在第一个之前,但是事实是第一个排在第二个之前。
should
的算分过程:
- 查询 should 语句中的两个查询。
- 对查询评分求和,求和乘以匹配总数,最后除以所有语句数,得到一个平均值。
上面的例子不应该是简单的相加,而是找到最匹配的字段,因此可以使用 Disjunction Max Query,它将查询文档中最高的内容评分作为最终评分。
- 在
query
下嵌套dis_max
和queries
组成 Disjunction Max Query。 - 单纯使用 Disjunction Max Query 可能结果总是相同的,因此可以使用
tie_breaker
(最好在 0 ~ 1 之间),它将其他匹配语句的评分与 tie_breaker 相乘,让分数不都相同。
6. 单字符串多字段查询 Multi Match
单字符串多字段查询有三种场景:
- 最佳字段 Best Fields
- 当字段之间相互竞争,又相互关联时,评分来自最匹配的字段。
- 多数字段 Most Fields
- 在处理英文内容时,在主字段抽取词干,加入同义词,用以匹配更多的文档。对于相同的文本,加入子字段,以提供更加精确的匹配,其他字段则作为匹配文档提高相关度的信号,匹配字段越多越好。
- 混合字段 Cross Fields
- 对于某些实体,例如人名、地址、图书信息。需要在多个字段中确认信息,单个字段只能作为整体的一部分,希望在任何这些列出的字段中找到尽可能多的词。
Multi Match Query 在 query
下使用 multi_match
进行声明,其中要说明 type
类型,默认为 Best Fields,同时在 fields
中说明在哪些字段上,tie_breaker
也可以进行指定,而 minimum_should_match
用来说明最小匹配度,说明最小匹配多少个条件。
比如在使用英文分词器进行搜索时,可能两个句子中短的那条容易排在前面,可以通过增加一个字段,使用 standard 分词器,同时在搜索时使用 multi_match
的 most_fields
找内容,在 fields
中可以使用 "title^10","title.std"
来指定权重。
在使用 operator
为 and
的时候往往比较严格,如果用 copy to 处理会需要额外的存储空间,这个时候使用 type
为 cross_fields
就比较好了。
7. 多语言和中文分词与检索
- 当处理语言时,可能搜索和原文不完全匹配,但是希望能够搜索。
- Quick brown fox 和 Fast brown fox / Jumping fox 和 Jumped foxes 。
- 可用策略:
- 归一化词元,清除变音符号。
- 抽取词根,清除单复数和时态。
- 包含同义词。
- 拼写错误,处理同音异形词。