:::tips 复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑,常见的有两种:
- fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名
- bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索
:::
算分函数查询
:::tips 利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时会按照分值降序排列
在ElasticSearch中,早期使用的打分算法是TF-IDF算法
后来5.1版本升级后ElasticSearch将算法改为了BM25算法
TF-IDF算法有一个缺陷,就是词条频率越高,文档得分也会越高,单个词条对文档影响较大;而BM25则会让单个词条的算分有一个上限,曲线更加平滑
根据相关度打分是比较合理的需求,但合理的不一定是产品经理需要的。要想人为控制相关性算分,就需要使用ElasticSearch中的function_score查询,function_score查询中包含四部分内容:
- 原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)
- 过滤条件:filter部分,符合该条件的文档才会重新算分
- 算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),共有四种算分函数:
- weight:函数结果是常量
- field_value_factor:以文档中的某个字段值作为函数结果
- random_score:以随机数作为函数结果
- script_score:自定义算分函数算法
- 运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:
- multiply:相乘
- replace:用function score替换query score
- 其它,例如:sum、avg、max、min
function_score的运行流程:
- 根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
- 根据过滤条件,过滤文档
- 符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
- 将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分
因此,其中的关键点是:
- 过滤条件:决定哪些文档的算分被修改
- 算分函数:决定函数算分的算法
运算模式:决定最终算分结果 :::
GET /索引库名/_search
{
"query": {
"function_score": {
"query": {....}, //原始查询,可以是任意条件
"functions": [ //算分函数
{
"filter": { //指定满足的条件
"term": {
"字段名": "搜索词"
}
},
"算分函数": "值" //指定算分函数
}
],
"boost_mode": "运算模式" //指定运算模式
}
}
}
布尔查询
:::tips 布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询,子查询的组合方式有:
must:必须匹配,参与算分,类似“与”
- should:选择性匹配,参与算分,类似“或”
- must_not:必须不匹配,不参与算分,类似“非”
- filter:必须匹配,不参与算分
每一个不同的字段,其查询的条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就必须用bool查询,需要注意的是,在搜索时参与打分的字段越多,查询的性能就越差,建议如下:
- 搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分
- 其它过滤条件,采用filter查询,不参与算分
:::
GET /索引库名/_search
{
"query": {
"bool": {
"must": [ //表示必须匹配,参与算分
{ "term": {"字段名1": "搜索词1" } }
],
"should": [ //表示选择性匹配,参与算分
{ "term": {"字段名2": "搜索词2" } },
{ "term": {"字段名3": "搜索词3" } }
],
"must_not": [ //表示必须不匹配,不参与算分
{ "range": { "字段名4": { "lte": "值1" } } }
],
"filter": [ //表示必须匹配,不参与算分
{ "range": {"字段名5": { "gte": "值2" } } }
]
}
}
}