【小技巧】修复chrome被2345劫持

Kubernetes 检查YAML文件安全配置:kubesec

  返回  

【Elasticsearch】ElasticSearch 7.8 多字段权重排序

2021/7/20 22:16:05 浏览:

在这里插入图片描述

1.概述

转载并且补充:https://mp.weixin.qq.com/s/0g86s-o7kgn8ZUxA3UBc0w 请看原文

读者提问:ES 的权重排序有没有示列,参考参考?

刚好之前也稍微接触过,于是写了这篇文章,可以简单参考下。

在很多复杂的业务场景下,排序的规则会比较复杂,单一的降序,升序无法满足日常需求。不过 ES 中提供了给文档加权重的方式来排序,还是挺好用的。

首先初始化三条测试数据,方便查看效果:


POST score-test/_doc
{
	"id": 1,
	"title": "Java怎么学",
	"type": 3,
	"userId": 1,
	"tags": [
		"java"
	],
	"textContent": "我要学Java",
	"status": 1,
	"heat": 80
}

POST score-test/_doc
{
	"id": 2,
	"title": "Java怎么学",
	"type": 2,
	"userId": 1,
	"tags": [
		"java"
	],
	"textContent": "我要学Java",
	"status": 1,
	"heat": 99
}

POST score-test/_doc
{
	"id": 3,
	"title": "Java怎么学",
	"type": 1,
	"userId": 1,
	"tags": [
		"java"
	],
	"textContent": "我要学Java",
	"status": 1,
	"heat": 100
}


type:1 为翻译,2 为转载,3 为原创

需求是查询 userId=1 的所有文章,按照热度降序排序,但是原创类型的文章要显示在前面,优先级高于热度。

如果我们简单的按照热度排序的话,那么顺序肯定是 id 为 3(热度:100),2(热度:99),1(热度:80)这样排列的。

但是原创类型的要在前面,那么结果应该是 1(热度:80,类型:原创),3(热度:100,类型:翻译),2(热度:99,类型:转载)。

排序条件肯定是以热度来进行的,这个是肯定的。唯一需要处理的就是怎么将原创类型的排在前面,如果只考虑实现,方式还是有很多种的。

比如:原创类型的热度值可以调的比较高,但是呢,热度值要重新弄一个字段,只用于排序,给用户展示的还是之前的热度值,这样排序就简单了,还是根据热度排就可以实现效果。

2.weightFactorFunction

在 ES 搜索结果中_score 这个字段相信大家并不陌生,这是 ES 给出的评分,我们可以根据评分来排序,然后将原创类型的评分提高就可以实现想要的效果。

直接看 Java 代码吧,通过 FunctionScoreQueryBuilder 来构建查询。

/**
     * 测试分数查询:
     *
     * 测试结果:
     *
     * 符合条件的记录数: 3 hits
     * 当前索引的分数: 100.0, 对应结果:=====>{"id":1,"title":"Java怎么学","type":3,"userId":1,"tags":["java"],"textContent":"我要学Java","status":1,"heat":80}, 指定字段结果:null
     * =================================================
     * 当前索引的分数: 1.0, 对应结果:=====>{"id":3,"title":"Java怎么学","type":1,"userId":1,"tags":["java"],"textContent":"我要学Java","status":1,"heat":100}, 指定字段结果:null
     * =================================================
     * 当前索引的分数: 1.0, 对应结果:=====>{"id":2,"title":"Java怎么学","type":2,"userId":1,"tags":["java"],"textContent":"我要学Java","status":1,"heat":99}, 指定字段结果:null
     * =================================================
     *
     * @throws IOException
     */
    @Test
    public void testSort() throws IOException {
        RestHighLevelClient restHighLevelClient = getHighClient();
        RestClient lowLevelClient = restHighLevelClient.getLowLevelClient();


        FunctionScoreQueryBuilder.FilterFunctionBuilder[] filterFunctionBuilders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("type", 3), ScoreFunctionBuilders.weightFactorFunction(100)),
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("type", 2), ScoreFunctionBuilders.weightFactorFunction(1)),
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("type", 1), ScoreFunctionBuilders.weightFactorFunction(1))
        };
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.termQuery("userId", 1));
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQuery, filterFunctionBuilders);
        searchSourceBuilder.query(functionScoreQueryBuilder)
                .sort("_score", SortOrder.DESC)
                .sort("heat", SortOrder.DESC);
        SearchRequest searchRequest = new SearchRequest("score-test");
//        searchRequest.types(EsConstant.DEFAULT_TYPE);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResults = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResults.getHits();
        System.out.println("符合条件的记录数: "+hits.getTotalHits());
        for (SearchHit hit : hits) {
            System.out.print("当前索引的分数: "+hit.getScore());
            System.out.print(", 对应结果:=====>"+hit.getSourceAsString());
            System.out.println(", 指定字段结果:"+hit.getSourceAsMap().get("name"));
            System.out.println("=================================================");
        }


    }

通过 ScoreFunctionBuilders.weightFactorFunction 为文章类型设置对应的权重,原创文章权重为 100,其他的都为 1,这样原创文章的得分就高于其他类型的文章。

在排序的时候优先得分排序,然后热度排序。就可以得到我们想要的结果了。

3.scriptFunction

除了使用 weightFactorFunction 来设置权重,另外介绍一种灵活度更高,适用于更复杂的排序场景的方式 scriptFunction。

scriptFunction 允许我们通过脚本的方式来实现权重,直接看代码:

 /**
     * 测试点:使用脚本进行查询权重赋值
     *
     * 符合条件的记录数: 3 hits
     * 当前索引的分数: 100.0, 对应结果:=====>{"id":1,"title":"Java怎么学","type":3,"userId":1,"tags":["java"],"textContent":"我要学Java","status":1,"heat":80}, 指定字段结果:null
     * =================================================
     * 当前索引的分数: 1.0, 对应结果:=====>{"id":3,"title":"Java怎么学","type":1,"userId":1,"tags":["java"],"textContent":"我要学Java","status":1,"heat":100}, 指定字段结果:null
     * =================================================
     * 当前索引的分数: 1.0, 对应结果:=====>{"id":2,"title":"Java怎么学","type":2,"userId":1,"tags":["java"],"textContent":"我要学Java","status":1,"heat":99}, 指定字段结果:null
     * =================================================
     */
    @Test
    public void testSort1() throws IOException {

        RestHighLevelClient restHighLevelClient = getHighClient();
        RestClient lowLevelClient = restHighLevelClient.getLowLevelClient();


        String scoreScript = "if (doc['type'].value == 3) {" +
                "   return 100;" +
                "} else {" +
                "   return 1;" +
                "}";
        FunctionScoreQueryBuilder.FilterFunctionBuilder[] filterFunctionBuilders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchAllQuery(), ScoreFunctionBuilders.scriptFunction(new Script(scoreScript)))
        };
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.termQuery("userId", 1));
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQuery, filterFunctionBuilders);
        searchSourceBuilder.query(functionScoreQueryBuilder)
                .sort("_score", SortOrder.DESC)
                .sort("heat", SortOrder.DESC);
        SearchRequest searchRequest = new SearchRequest("score-test");
//        searchRequest.types(EsConstant.DEFAULT_TYPE);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResults = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResults.getHits();
        System.out.println("符合条件的记录数: "+hits.getTotalHits());
        for (SearchHit hit : hits) {
            System.out.print("当前索引的分数: "+hit.getScore());
            System.out.print(", 对应结果:=====>"+hit.getSourceAsString());
            System.out.println(", 指定字段结果:"+hit.getSourceAsMap().get("name"));
            System.out.println("=================================================");
        }

    }

scoreScript 就是控制权重的脚本,也就是一段代码(脚本默认是 groovy),是不是方便的多。

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号