ES 的权重排序

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

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

{
	id: 1,
	title: "Java怎么学",
	type: 3,
	userId: 1,
	tags: [
		"java"
	],
	textContent: "我要学Java",
	status: 1,
	heat: 80
}
{
	id: 2,
	title: "Java怎么学",
	type: 2,
	userId: 1,
	tags: [
		"java"
	],
	textContent: "我要学Java",
	status: 1,
	heat: 99
}
{
	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,类型:转载)。

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

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

weightFactorFunction

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

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

@Test
public void testSort() {
    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(elasticSearchIndexConfig.getArticleSearchIndexName());
    searchRequest.types(EsConstant.DEFAULT_TYPE);
    searchRequest.source(searchSourceBuilder);

    List<ArticleDocument> searchResults = kittyRestHighLevelClient.search(searchRequest, ArticleDocument.class);
    searchResults.forEach(doc -> {
        System.out.println(doc.getId() + "\t" + doc.getType() + "\t" + doc.getHeat());
    });
}

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

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

scriptFunction

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

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

@Test
public void testSort() {
    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(elasticSearchIndexConfig.getArticleSearchIndexName());
    searchRequest.types(EsConstant.DEFAULT_TYPE);
    searchRequest.source(searchSourceBuilder);

    List<ArticleDocument> searchResults = kittyRestHighLevelClient.search(searchRequest, ArticleDocument.class);
    searchResults.forEach(doc -> {
        System.out.println(doc.getId() + "\t" + doc.getType() + "\t" + doc.getHeat());
    });
}

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

参考ES官方文档

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值