如何在ElasticSearch中实现条件排序,让有效期内的推广课程优先展示
实现方案
方案1:多字段优先级排序(最直观,易调试)
核心逻辑是新增一个自定义排序因子,将符合推广条件的课程标记为更高排序优先级,排在所有普通课程之前,之后再执行你原来的普通排序规则。
ES原生DSL示例:
{ "query": { // 这里放你原来的搜索条件,比如关键词匹配、分类筛选等 "match_all": {} }, "sort": [ // 第一排序规则:符合推广条件的优先 { "_script": { "type": "number", "script": { "lang": "painless", "source": """ // 判断是否为有效推广课程 if (doc['is_sponsored'].value == 1 && doc['sponsored_start_date'].value.toInstant().isBeforeZonedDateTime(new ZonedDateTime()) && doc['sponsored_end_date'].value.toInstant().isAfterZonedDateTime(new ZonedDateTime())) { return 2; } return 1; """ }, "order": "desc" } }, // 第二排序规则:你原来的普通排序,比如按相关度、发布时间、销量等,示例是按相关度得分倒序 { "_score": { "order": "desc" } } ] }
如果存在ES和PHP时区不一致的问题,也可以直接在PHP侧生成当前时间传入脚本,替换掉new ZonedDateTime()即可:
"params": { "now": "<?php echo date('Y-m-d H:i:s');?>" }
方案2:function_score加权(适合默认按相关度排序的场景)
给有效推广课程加上一个足够大的权重值,确保它的整体得分远高于普通课程,自动排在最前方。
ES原生DSL示例:
{ "query": { "function_score": { "query": { // 这里放你原来的搜索条件 "match_all": {} }, "functions": [ { "filter": { "bool": { "must": [ {"term": {"is_sponsored": 1}}, {"range": {"sponsored_start_date": {"lte": "now"}}}, {"range": {"sponsored_end_date": {"gte": "now"}}} ] } }, "weight": 10000 } ], "boost_mode": "sum" } } }
PHP 客户端代码示例(以elasticsearch官方SDK为例)
以下是方案1的PHP实现代码:
<?php require 'vendor/autoload.php'; $client = Elasticsearch\ClientBuilder::create()->setHosts(['127.0.0.1:9200'])->build(); $params = [ 'index' => 'test', 'type' => 'courses', 'body' => [ 'query' => [ // 替换为你实际的搜索条件 'match_all' => new stdClass() ], 'sort' => [ [ '_script' => [ 'type' => 'number', 'script' => [ 'lang' => 'painless', 'source' => "if (doc['is_sponsored'].value == 1 && doc['sponsored_start_date'].value.toInstant().isBeforeZonedDateTime(new ZonedDateTime()) && doc['sponsored_end_date'].value.toInstant().isAfterZonedDateTime(new ZonedDateTime())) { return 2; } return 1;" ], 'order' => 'desc' ] ], [ '_score' => ['order' => 'desc'] ] ] ] ]; $response = $client->search($params); print_r($response); ?>
注意事项
- 如果需要调整推广课程内部的排序规则,比如推广结束时间越近越靠前/越晚越靠前,只需要修改script的返回值即可,比如将符合条件的返回值调整为
2 + (doc['sponsored_end_date'].value.toInstant().toEpochMilli()/100000000000)即可实现推广过期时间越晚,排序越靠前。 - 如果字段存在空值,可在script判断中增加
doc['字段名'].size() > 0的校验,避免查询报错。 - 若时区存在差异,推荐在PHP侧生成格式化后的当前时间作为参数传入查询脚本,避免时区不匹配导致的时间判断错误。
内容的提问来源于stack exchange,提问作者Dksingh
相关产品推荐
相关产品推荐

