有产品数据库;定期计算产品的受欢迎度值。 ES里建了一个索引,有按人气值排序。 有一项任务是搞砸推荐系统。推荐系统为每个user_id产生N个推荐产品,推荐值从0到1。 要求按照这样的方式为每个用户展示产品:首先按受欢迎程度排序人气>A(热门产品),然后为用户推荐(已排序),然后其余所有按受欢迎程度排序。 这种排序在ES层面如何实现呢?如何将用户推荐放在那里?
这个排序在ES层面如何实现?如何将用户推荐放在那里?
可以借助
composite
类型的运行时字段对产品进行排序
映射
PUT /product_popularity
{
"mappings": {
"properties": {
"title": {
"type": "keyword"
},
"popularity": {
"type": "double"
},
"recommended_for_users": {
"type": "nested",
"properties": {
"user_id": {
"type": "keyword"
},
"recommendation_value": {
"type": "double"
}
}
}
}
}
}
一些文件
POST /product_popularity/_bulk
{"create":{"_id":1}}
{"title":"speaker","popularity":20,"recommended_for_users":[{"user_id":"A","recommendation_value":0.2},{"user_id":"B","recommendation_value":0.7}]}
{"create":{"_id":2}}
{"title":"fridge","popularity":10,"recommended_for_users":[{"user_id":"A","recommendation_value":0.9},{"user_id":"B","recommendation_value":0.1}]}
{"create":{"_id":3}}
{"title":"table","popularity":40,"recommended_for_users":[{"user_id":"B","recommendation_value":0.4},{"user_id":"C","recommendation_value":0.3}]}
{"create":{"_id":4}}
{"title":"chair","popularity":50,"recommended_for_users":[{"user_id":"A","recommendation_value":0.6},{"user_id":"C","recommendation_value":0.8}]}
{"create":{"_id":5}}
{"title":"shelf","popularity":7,"recommended_for_users":[{"user_id":"B","recommendation_value":0.6},{"user_id":"C","recommendation_value":0.5}]}
使用运行时字段查询
"lowest_top_popularity": 30
和 "user_id": "C"
GET /product_popularity/_search?filter_path=hits.hits
{
"runtime_mappings": {
"popularity_score": {
"type": "composite",
"fields": {
"score": {
"type": "double"
},
"reason": {
"type": "keyword"
}
},
"script": {
"source": """
Map getFieldMap(double score, String reason) {
Map fieldMap = new HashMap();
fieldMap.put("score", score);
fieldMap.put("reason", reason);
return fieldMap;
}
double lowestTopPopularity = params.lowest_top_popularity;
String userName = params.user_id;
double popularity = params._source.popularity;
if (popularity > lowestTopPopularity) {
double score = popularity * 10000;
Map fieldMap = getFieldMap(score, "top popularity");
emit(fieldMap);
return;
}
List recommendedForUsers = params._source.recommended_for_users;
for (def recommendedForUser : recommendedForUsers) {
if (userName.equals(recommendedForUser.user_id)) {
double score = recommendedForUser.recommendation_value + 1000;
Map fieldMap = getFieldMap(score, "recommendation for user");
emit(fieldMap);
return;
}
}
double score = popularity;
Map fieldMap = getFieldMap(score, "non-top popularity");
emit(fieldMap);
""",
"params": {
"lowest_top_popularity": 30,
"user_id": "C"
}
}
}
},
"fields": ["title","popularity_score.score","popularity_score.reason"],
"sort": [
{
"popularity_score.score": {
"order": "desc"
}
}
],
"_source": false
}
运行时字段有两个子字段。
score
字段存储排序分数。 reason
字段是分数的描述。
runtime 字段也有两个参数。
lowest_top_popularity
是将产品纳入顶部列表的最低边框。 user_id
是用户id,查询商品列表。
我提取了
getFieldMap
函数以避免代码重复
当然是回应。包括runtime字段和title字段的子字段
{
"hits" : {
"hits" : [
{
"_index" : "product_popularity",
"_type" : "_doc",
"_id" : "4",
"_score" : null,
"fields" : {
"popularity_score.reason" : [
"top popularity"
],
"title" : [
"chair"
],
"popularity_score.score" : [
500000.0
]
},
"sort" : [
500000.0
]
},
{
"_index" : "product_popularity",
"_type" : "_doc",
"_id" : "3",
"_score" : null,
"fields" : {
"popularity_score.reason" : [
"top popularity"
],
"title" : [
"table"
],
"popularity_score.score" : [
400000.0
]
},
"sort" : [
400000.0
]
},
{
"_index" : "product_popularity",
"_type" : "_doc",
"_id" : "1",
"_score" : null,
"fields" : {
"popularity_score.reason" : [
"non-top popularity"
],
"title" : [
"speaker"
],
"popularity_score.score" : [
20.0
]
},
"sort" : [
20.0
]
},
{
"_index" : "product_popularity",
"_type" : "_doc",
"_id" : "2",
"_score" : null,
"fields" : {
"popularity_score.reason" : [
"non-top popularity"
],
"title" : [
"fridge"
],
"popularity_score.score" : [
10.0
]
},
"sort" : [
10.0
]
},
{
"_index" : "product_popularity",
"_type" : "_doc",
"_id" : "5",
"_score" : null,
"fields" : {
"popularity_score.reason" : [
"non-top popularity"
],
"title" : [
"shelf"
],
"popularity_score.score" : [
7.0
]
},
"sort" : [
7.0
]
}
]
}
}
您可以根据您的用例修改分数计算