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_scorefilter 可以将 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_scorefilterrange 可以进行范围查询。gte 是大于等于,lte 是小于等于。
  • 在日期的查询中可以使用 now-1y 这样的语法,y 是年,M 是月,w 是周,d 是天,H/h 是小时,m 是分钟,s 是秒。
  • 可以通过 constant_score 嵌套 filterexists 来确定非空字段。
  • 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PUT /blogs/_doc/1
{
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}

PUT /blogs/_doc/2
{
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}

POST /blogs/_search
{
"query": {
"bool": {
"should": [
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}

在上面的例子中,我们希望第二个查询排在第一个之前,但是事实是第一个排在第二个之前。

should 的算分过程:

  • 查询 should 语句中的两个查询。
  • 对查询评分求和,求和乘以匹配总数,最后除以所有语句数,得到一个平均值。

上面的例子不应该是简单的相加,而是找到最匹配的字段,因此可以使用 Disjunction Max Query,它将查询文档中最高的内容评分作为最终评分。

  • query 下嵌套 dis_maxqueries 组成 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_matchmost_fields 找内容,在 fields 中可以使用 "title^10","title.std" 来指定权重。

在使用 operatorand 的时候往往比较严格,如果用 copy to 处理会需要额外的存储空间,这个时候使用 typecross_fields 就比较好了。

7. 多语言和中文分词与检索

  • 当处理语言时,可能搜索和原文不完全匹配,但是希望能够搜索。
    • Quick brown fox 和 Fast brown fox / Jumping fox 和 Jumped foxes 。
  • 可用策略:
    • 归一化词元,清除变音符号。
    • 抽取词根,清除单复数和时态。
    • 包含同义词。
    • 拼写错误,处理同音异形词。

8. Space Jam 全文搜索

9. Search Template 和 Index Alias 查询

10. 综合排序 Function Score Query 优化算分

11. Term & Phrase Suggester

12. 自动补全与基于上下文的提示

13. 配置跨集群搜索