Elasticsearch 中的建议

问题描述 投票:0回答:1

有产品数据库;定期计算产品的受欢迎度值。 ES里建了一个索引,有按人气值排序。 有一项任务是搞砸推荐系统。推荐系统为每个user_id产生N个推荐产品,推荐值从0到1。 要求按照这样的方式为每个用户展示产品:首先按受欢迎程度排序人气>A(热门产品),然后为用户推荐(已排序),然后其余所有按受欢迎程度排序。 这种排序在ES层面如何实现呢?如何将用户推荐放在那里?

这个排序在ES层面如何实现?如何将用户推荐放在那里?

elasticsearch recommendation-engine
1个回答
0
投票

可以借助

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
                ]
            }
        ]
    }
}

您可以根据您的用例修改分数计算

© www.soinside.com 2019 - 2024. All rights reserved.