- 相关性算分
- TF-IDF
- BM25
- Boosting
- 布尔查询 bool Query
- Boosting Query
- 单字符串多字段查询
相关性算分
-
如何衡量相关性
- Precision(查准率)―尽可能返回较少的无关文档
- Recall(查全率)–尽量返回较多的相关文档
- Ranking -是否能够按照相关度进行排序
-
相关性(Relevance):搜索的相关性算分,描述了一个文档和查询语句匹配的程度
- ES 会对每个匹配查询条件的结果进行算分
_score
- 打分的本质是排序,需要把最符合用户需求的文档排在前面
- ES5之前,默认的相关性算分采用TF-IDF,现在采用BM25
- ES 会对每个匹配查询条件的结果进行算分
-
TF-IDF(term frequency–inverse document frequency)是一种用于信息检索与数据挖掘的常用加权技术
- TF-IDF被公认为是信息检索领域最重要的发明
- 现代搜索引擎,对TF-IDF进行了大量细微的优化
-
Lucene中的TF-IDF评分公式
-
-
TF是词频 (Term Frequency):检索词在文档中出现的频率越高,相关性也越高
-
IDF是逆向文本频率 (Inverse Document Frequency):每个检索词在索引中出现的频率越高,相关性越低
-
字段长度归一值 (field-length norm):字段越短,字段的权重越高
- 检索词出现在一个内容短的 title 要比同样的词出现在一个内容长的 content 字段权重更大
-
-
BM25 就是对 TF-IDF 算法的改进
- 对于 TF-IDF 算法,TF(t) 部分的值越大,整个公式返回的值就会越大
- BM25 就针对这点进行来优化,随着TF(t) 的逐步加大,该算法的返回值会趋于一个数值
-
BM25的公式
-
通过Explain API查看TF-IDF
GET /test_score/_search
{"explain":true,"query":{"match":{"content":"elasticsearch"}}}
- Boosting 是控制相关度的一种手段
- 当 boost > 1 时,打分的权重相对性提升
- 当 0 < boost <1 时,打分的权重相对性降低
- 当 boost <0 时,贡献负分
- 应用场景:希望包含了某项内容的结果不是不出现,而是排序靠后
# 返回匹配 positive 查询的文档并降低匹配 negative 查询的文档相似度分
GET /test_score/_search
{"query":{"boosting":{
"positive":{"term":{"content":"elasticsearch"}},
"negative":{"term":{"content":"like"}},
"negative_boost":0.2}}}
布尔查询 bool Query
- 一个 bool 查询,是一个或者多个查询子句的组合,总共包括4种子句,其中2种会影响算分,2种不影响算分
- must: 相当于&& ,必须匹配,贡献算分
- should: 相当于|| ,选择性匹配,贡献算分
- must_not: 相当于! ,必须不能匹配,不贡献算分
- filter: 必须匹配,不贡献算法
- 在Elasticsearch中,有Query和 Filter两种不同的Context
- Query Context: 相关性算分
- Filter Context: 不需要算分 ,可以利用Cache,获得更好的性能
- 相关性并不只是全文本检索的专利,也适用于 yes | no 的子句,匹配的子句越多,相关性评分越高
- 如果多条查询子句被合并为一条复合查询语句
- 比如 bool 查询,则每个查询子句计算得出的评分会被合并到总的相关性评分中
- bool 查询语法
- 子查询可以任意顺序出现
- 可以嵌套多个查询
- 如果你的bool查询中,没有must条件,should中必须至少满足一条查询
GET /es_db/_search
{"query":{"bool":{
"must":{"match":{"remark":"java developer"}},
"filter":{"term":{"sex":"1"}},
"must_not":{"range":{"age":{"gte":30}}},
"should":[
{"term":{"address.keyword":{"value":"广州天河公园"}}},
{"term":{"address.keyword":{"value":"广州白云山公园"}}}],
"minimum_should_match":1}}}
- 解决结构化查询“包含而不是相等”的问题
- 增加count字段,使用bool查询解决
POST /employee/_bulk
{"index":{"_id":1}}
{"name":"小明","interest":["跑步","篮球"],"interest_count":2}
{"index":{"_id":2}}
{"name":"小红","interest":["跑步"],"interest_count":1}
"query":{"bool":{"must":[
{"term":{"interest.keyword":{"value":"跑步"}}},
{"term":{"interest_count":{"value":1}}}]}}
- 利用bool嵌套实现should not逻辑
"query":{"bool":{
"must":{"match":{"remark":"java developer"}},
"should":[{"bool":{"must_not":[{"term":{"sex":1}}]}}],
"minimum_should_match":1}}
Boosting Query
- 控制字段的 Boosting:Boosting是控制相关的一种手段。可以通过指定字段的boost值影响查询结果
- 当boost>1时,打分的权重相对性提升
- 当0<boost<1时,打分的权重相对性降低
- 当boost<0时,贡献负分
GET /blogs/_search
{"query":{"bool":{"should":[
{"match":{"title":{"query":"apple,ipad","boost":1}}},
{"match":{"content":{"query":"apple,ipad","boost":4}}}]}}}
- 利用must not排除
GET /news/_search
{"query":{"bool":{
"must":{"match":{"content":"apple"}},
"must_not":{"match":{"content":"pie"}}}}}
- 利用negative_boost降低相关性
- negative_boost 对 negative部分query生效
- 计算评分时,boosting部分评分不修改,negative部分query乘以negative_boost值
- negative_boost取值: 0-1.0
- 对某些返回结果不满意,但又不想排除掉可以考虑boosting query的negative_boost
GET /news/_search
{"query":{"boosting":{
"positive":{"match":{"content":"apple"}},
"negative":{"match":{"content":"pie"}},
"negative_boost":0.2}}}
单字符串多字段查询
- 三种场景
- 最佳字段 (Best Fields):当字段之间相互竞争,又相互关联。例如,对于博客的 title和 body这样的字段,评分来自最匹配字段
- 多数字段 (Most Fields):处理英文内容时的一种常见的手段是,在主字段( English Analyzer),抽取词干,加入同义词,以匹配更多的文档。相同的文本,加入子字段(Standard Analyzer),以提供更加精确的匹配。其他字段作为匹配文档提高相关度的信号,匹配字段越多则越好
- 混合字段 (Cross Field):对于某些实体,例如人名,地址,图书信息。需要在多个字段中确定信息,单个字段只能作为整体的一部分。希望在任何这些列出的字段中找到尽可能多的词
- bool should的算法过程
- 查询should语句中的两个查询
- 加两个查询的评分
- 乘以匹配语句的总数
- 除以所有语句的总数
- 最佳字段查询 Dis Max Query
- 将任何与任一查询匹配的文档作为结果返回,采用字段上最匹配的评分最终评分返回
- 竞争关系的字段,不应该讲分数简单叠加,而是应该找到单个最佳匹配的字段的评分
POST blogs/_search
{"query":{"dis_max":{"queries":[
{"match":{"title":"Brown fox"}},
{"match":{"body":"Brown fox"}}]}}}
- 可以通过tie_breaker参数调整
- Tier Breaker是一个介于0-1之间的浮点数
- 0代表使用最佳匹配;1代表所有语句同等重要
- 流程
- 获得最佳匹配语句的评分_score
- 将其他匹配语句的评分与tie_breaker相乘
- 对以上评分求和并规范化
POST /blogs/_search
{"query":{"dis_max":{"queries":[
{"match":{"title":"Quick pets"}},
{"match":{"body":"Quick pets"}}],"tie_breaker":0.2}}}
- Multi Match Query 最佳字段 (Best Fields) 搜索
- Best Fields 是默认类型,可以不用指定
POST /blogs/_search
{"query":{"multi_match":{
"type":"best_fields",
"query":"Quick pets",
"fields":["title","body"],"tie_breaker":0.2}}}
- 使用多数字段(Most Fields)搜索
- 每个字段对于最终评分的贡献可以通过自定义值boost 来控制
# 增加 title 的权重
GET /titles/_search
{"query":{"multi_match":{
"query":"barking dogs",
"type":"most_fields",
"fields":["title^10","title.std"]}}}
- 跨字段(Cross Field)搜索
- 与copy_to相比,其中一个优势就是它可以在搜索时为单个字段提升权重
GET /address/_search
{"query":{"multi_match":{
"query":"湖南常德",
"type":"cross_fields",
"operator":"and",
"fields":["province","city"]}}}
- 可以用copy_to解决,但是需要额外的存储空间
PUT /address
{"mappings":{"properties":{
"province":{"type":"keyword","copy_to":"full_address"},
"city":{"type":"text","copy_to":"full_address"}}},
"settings":{"index":{"analysis.analyzer.default.type":"ik_max_word"}}}
GET /address/_search
{"query":{"match":{"full_address":{"query":"湖南常德","operator":"and"}}}}