读者提问: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,类型:转载)。 排序条件肯定是以热度来进行的,这个是肯定的。唯一需要处理的就是怎么将原创类型的排在前面,如果只考虑实现,方式还是有很多种的。 比如:原创类型的热度值可以调的比较高,但是呢,热度值要重新弄一个字段,服务器租用只用于排序,给用户展示的还是之前的热度值,这样排序就简单了,还是根据热度排就可以实现效果。 在 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 searchResults = kittyRestHighLevelClient.search(searchRequest, ArticleDocument.class); searchResults.forEach(doc -> { System.out.println(doc.getId() + "\t" + doc.getType() + "\t" + doc.getHeat()); }); } 通过 ScoreFunctionBuilders.weightFactorFunction 为文章类型设置对应的权重,原创文章权重为 100,其他的都为 1,这样原创文章的得分就高于其他类型的文章。 在排序的时候优先得分排序,然后热度排序。就可以得到我们想要的结果了。 除了使用 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 searchResults = kittyRestHighLevelClient.search(searchRequest, ArticleDocument.class); searchResults.forEach(doc -> { System.out.println(doc.getId() + "\t" + doc.getType() + "\t" + doc.getHeat()); }); } scoreScript 就是控制权重的脚本,也就是一段代码(脚本默认是 groovy),是不是方便的多。背景
weightFactorFunction
scriptFunction