:::tips 复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑,常见的有两种:

  • fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名
  • bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索 :::

    算分函数查询

    :::tips 利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时会按照分值降序排列

在ElasticSearch中,早期使用的打分算法是TF-IDF算法
image.png

后来5.1版本升级后ElasticSearch将算法改为了BM25算法
image.png

TF-IDF算法有一个缺陷,就是词条频率越高,文档得分也会越高,单个词条对文档影响较大;而BM25则会让单个词条的算分有一个上限,曲线更加平滑
image.png

根据相关度打分是比较合理的需求,但合理的不一定是产品经理需要的。要想人为控制相关性算分,就需要使用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的运行流程:

  1. 根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
  2. 根据过滤条件,过滤文档
  3. 符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
  4. 将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分

因此,其中的关键点是:

  • 过滤条件:决定哪些文档的算分被修改
  • 算分函数:决定函数算分的算法
  • 运算模式:决定最终算分结果 :::

    1. GET /索引库名/_search
    2. {
    3. "query": {
    4. "function_score": {
    5. "query": {....}, //原始查询,可以是任意条件
    6. "functions": [ //算分函数
    7. {
    8. "filter": { //指定满足的条件
    9. "term": {
    10. "字段名": "搜索词"
    11. }
    12. },
    13. "算分函数": "值" //指定算分函数
    14. }
    15. ],
    16. "boost_mode": "运算模式" //指定运算模式
    17. }
    18. }
    19. }

    布尔查询

    :::tips 布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询,子查询的组合方式有:

  • must:必须匹配,参与算分,类似“与”

  • should:选择性匹配,参与算分,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分

每一个不同的字段,其查询的条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就必须用bool查询,需要注意的是,在搜索时参与打分的字段越多,查询的性能就越差,建议如下:

  • 搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分
  • 其它过滤条件,采用filter查询,不参与算分 :::
    1. GET /索引库名/_search
    2. {
    3. "query": {
    4. "bool": {
    5. "must": [ //表示必须匹配,参与算分
    6. { "term": {"字段名1": "搜索词1" } }
    7. ],
    8. "should": [ //表示选择性匹配,参与算分
    9. { "term": {"字段名2": "搜索词2" } },
    10. { "term": {"字段名3": "搜索词3" } }
    11. ],
    12. "must_not": [ //表示必须不匹配,不参与算分
    13. { "range": { "字段名4": { "lte": "值1" } } }
    14. ],
    15. "filter": [ //表示必须匹配,不参与算分
    16. { "range": {"字段名5": { "gte": "值2" } } }
    17. ]
    18. }
    19. }
    20. }