You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

咨询:如何基于用户特定统计调整Elasticsearch搜索结果得分

嘿,这个需求完全可以用Elasticsearch实现,而且不用把用户行为数据硬塞到company主文档里——咱们可以用**侧栏索引(Side Index)结合函数得分查询(Function Score Query)**来搞定,我给你拆解下具体思路:

核心思路:分离主数据与用户行为数据

不用在company文档里嵌套用户行为,而是单独创建一个存储用户-公司交互记录的索引,我习惯叫它user_company_interactions,结构可以设计成这样:

{
  "user_id": "用户唯一标识GUID",
  "company_id": "关联的company文档ID",
  "open_count": 8, // 用户打开该公司的次数
  "last_open_time": "2024-05-20T16:40:00Z" // 最后一次打开的时间
}

每个用户和公司的交互是独立文档,更新起来特别方便——用户打开公司页面时,直接用upsert操作更新这个索引即可,完全不会影响主索引的结构和性能。

查询时结合行为数据做个性化评分

接下来就是在搜索company时,通过Elasticsearch的function_score查询,拉取当前用户的交互数据,把这些数据作为评分因子加入最终得分。

举个具体的查询示例(假设当前用户ID是user_789,搜索关键词是“互联网公司”):

{
  "query": {
    "function_score": {
      // 基础搜索查询:匹配关键词
      "query": {
        "match": {
          "name": "互联网公司"
        }
      },
      // 自定义评分函数:结合用户行为数据
      "functions": [
        // 因子1:打开次数越多,加分越高
        {
          "script_score": {
            "script": {
              "source": """
                def interaction = params.interaction_data;
                // 如果有用户交互记录,返回打开次数的加权值,否则返回0
                return interaction != null ? interaction.open_count * 0.6 : 0;
              """,
              "params": {
                "interaction_data": {
                  "index": "user_company_interactions",
                  "id": "user_789#{{doc['id'].value}}" // 用「用户ID+公司ID」作为交互文档的唯一ID
                }
              }
            }
          }
        },
        // 因子2:最近打开的公司,加分更多
        {
          "script_score": {
            "script": {
              "source": """
                def interaction = params.interaction_data;
                if (interaction == null) return 0;
                // 计算距离当前的天数,最近7天打开的加2分,30天内加1分,超过的不加
                def days = ChronoUnit.DAYS.between(Instant.parse(interaction.last_open_time), Instant.now());
                return days <=7 ? 2.0 : (days <=30 ? 1.0 : 0);
              """,
              "params": {
                "interaction_data": {
                  "index": "user_company_interactions",
                  "id": "user_789#{{doc['id'].value}}"
                }
              }
            }
          }
        }
      ],
      "boost_mode": "sum" // 把基础查询得分和自定义因子得分相加,你也可以用multiply等模式
    }
  }
}
关键优化点
  • 交互文档ID设计:用user_id#company_id作为user_company_interactions的文档ID,能直接通过ID快速定位到用户对某公司的交互记录,比用term查询更高效。
  • 更新策略:用户打开公司页面时,用upsert操作自动创建/更新交互记录:
    POST user_company_interactions/_update/user_789#company_123
    {
      "script": {
        "source": "ctx._source.open_count +=1; ctx._source.last_open_time = params.now",
        "params": {"now": "2024-05-20T17:00:00Z"}
      },
      "upsert": {
        "user_id": "user_789",
        "company_id": "company_123",
        "open_count":1,
        "last_open_time": "2024-05-20T17:00:00Z"
      }
    }
    
  • 性能优化:可以开启ES的请求缓存,或者在应用层缓存用户近期的交互数据,减少重复查询;脚本里尽量避免复杂计算,也可以把预处理好的权重值直接存在交互文档里。
结论

完全只用Elasticsearch就能实现你的需求,核心就是把用户行为数据和主业务数据分离存储,查询时通过函数得分机制把两者结合起来,既保证了主索引的干净,又能灵活实现个性化排序。

内容的提问来源于stack exchange,提问作者Daniel Lerps

火山引擎 最新活动