Es_算分函数使用详情

算分函数查询

相关性计算

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

例如,我们搜索 “虹桥如家”,结果如下:

[
  {
    "_score" : 17.850193,
    "_source" : {
      "name" : "虹桥如家酒店真不错",
    }
  },
  {
    "_score" : 12.259849,
    "_source" : {
      "name" : "外滩如家酒店真不错",
    }
  },
  {
    "_score" : 11.91091,
    "_source" : {
      "name" : "迪士尼如家酒店真不错",
    }
  }
]

在elasticsearch中,早期使用的打分算法是TF-IDF算法,公式如下:

image-20210721190152134

在后来的5.1版本升级中,elasticsearch将算法改进为BM25算法,公式如下:

image-20210721190416214

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

image-20210721190907320

根据相关度打分是比较合理的需求,但合理的不一定是产品经理需要的。

以百度为例,你搜索的结果中,并不是相关度越高排名越靠前,而是谁掏的钱多排名就越靠前。如图:

image-20210721191144560

要想人为控制相关性算分,就需要利用elasticsearch中的function score 查询了。

算分函数语法

image-20210721191544750

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. 根据过滤条件,过滤出符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)

  3. 原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。

因此,其中的关键点是:

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

需求:给“如家”这个品牌的酒店排名靠前一些

翻译一下这个需求,转换为之前说的四个要点:

  • 原始条件:不确定,可以任意变化
  • 过滤条件:brand = “如家”
  • 算分函数:可以简单粗暴,直接给固定的算分结果,weight
  • 运算模式:比如求和

因此最终的DSL语句如下:

#不添加算法函数,原始检索。如家酒店的相关性得分并不高
GET /hotel/_search
{
  "query": {
    "match": {
      "all": "北京酒店"
    }
  }
}

#使用算法函数,所有品牌为“如家”的酒店,在原始相关性得分基础上+10,最终相关性得分高了很多
GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "all": "北京酒店"
        }
      },
      "functions": [
        {
          "filter": {"term": {"brand": "如家"}},
          "weight": 10
        }
      ],
      "boost_mode": "sum"
    }
  }
}

不添加算法函数,原始检索。如家酒店的相关性得分并不高

image-20220627173331283

使用算法函数,所有品牌为“如家”的酒店,在原始相关性得分基础上+10,最终相关性得分高了很多

image-20220627173436183

代码实例

image-20220629113515690

@Test
public void testFunctionScore() throws IOException {
    SearchRequest request = new SearchRequest("hotel");

    //设置查询条件
    FunctionScoreQueryBuilder scoreQueryBuilder = QueryBuilders.functionScoreQuery(
        //基础查询
        QueryBuilders.matchQuery("all", "北京酒店"),
        //对应DSL里functions数组
        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                //filter,过滤出来要重新算分的数据
                QueryBuilders.termQuery("brand", "希尔顿"),
                //设置算分函数,使用权重值
                ScoreFunctionBuilders.weightFactorFunction(10)
            )
        }
    );
    //处分函数的加权模式:Multiply,相乘。数据的原始得分 乘 权重值。如果不设置加权模式,默认就是相乘
    scoreQueryBuilder.boostMode(CombineFunction.MULTIPLY);

    request.source().query(scoreQueryBuilder);


    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    SearchHits result = response.getHits();
    //      获取总数量
    long total = result.getTotalHits().value;
    System.out.println("总数量:" + total);
    //      获取数据列表
    SearchHit[] hits = result.getHits();
    for (SearchHit hit : hits) {
        //获取文档对象的原始数据
        String docJson = hit.getSourceAsString();
        HotelDoc doc = JSON.parseObject(docJson, HotelDoc.class);
        System.out.println("查询得到的数据:" + doc);

        System.out.println("匹配度得分:" + hit.getScore());
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Java中,Elasticsearch的算分函数可以使用Elastic提供的Java API来调用。Elasticsearch的算分函数是根据相关度评分算法来计算文档的相关度得分。你可以使用以下代码示例来调用Elasticsearch的算分函数: ```java import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; public class ElasticsearchExample { private static final String INDEX_NAME = "your_index_name"; private static final String FIELD_NAME = "your_field_name"; private static final String SEARCH_KEYWORD = "your_search_keyword"; public static void main(String[] args) { // 创建Elasticsearch客户端 RestHighLevelClient client = createElasticsearchClient(); try { // 创建搜索请求 SearchRequest searchRequest = new SearchRequestDEX_NAME); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // 创建查询构建器 QueryBuilder queryBuilder = QueryBuilders.matchQuery(FIELD_NAME, SEARCH_KEYWORD); // 创建算分函数 FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(queryBuilder, ScoreFunctionBuilders.fieldValueFactorFunction(FIELD_NAME)); // 设置排序方式 searchSourceBuilder.sort(SortBuilders.scoreSort().order(SortOrder.DESC)); // 设置高亮显示 HighlightBuilder highlightBuilder = new HighlightBuilder().field(FIELD_NAME); searchSourceBuilder.highlighter(highlightBuilder); // 将算分函数添加到搜索请求中 searchSourceBuilder.query(functionScoreQueryBuilder); searchRequest.source(searchSourceBuilder); // 执行搜索请求 SearchResponse searchResponse = client.search(searchRequest); // 处理搜索结果 SearchHits hits = searchResponse.getHits(); // TODO: 处理搜索结果 } catch (Exception e) { e.printStackTrace(); } finally { // 关闭Elasticsearch客户端 closeElasticsearchClient(client); } } private static RestHighLevelClient createElasticsearchClient() { // 创建Elasticsearch客户端 // TODO: 创建并返回Elasticsearch客户端实例 } private static void closeElasticsearchClient(RestHighLevelClient client) { // 关闭Elasticsearch客户端 // TODO: 关闭Elasticsearch客户端实例 } } ``` 以上示例代码演示了如何使用Java API调用Elasticsearch的算分函数进行搜索。你可以根据自己的需求进行调整和扩展。请确保在使用之前已经正确配置了Elasticsearch客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值