文档首页/ 云搜索服务 CSS/ 最佳实践/ 使用Elasticsearch自定义规则排序搜索结果
更新时间:2024-12-19 GMT+08:00
分享

使用Elasticsearch自定义规则排序搜索结果

通过Elasticsearch集群可以对搜索结果进行自定义规则排序。

应用场景

Elasticsearch是一个高度可扩展的开源搜索和分析引擎,支持用户通过自定义规则对搜索结果进行排序。自定义排序允许开发者根据业务需求,定义特定的排序规则,以优化搜索结果的相关性和用户体验。该方案可以用于以下场景:
  • 电子商务:根据销量、用户评价、价格等因素对商品进行排序。
  • 内容管理:根据阅读量、发布时间对文章或博客帖子进行排序。
  • 金融服务:根据交易金额、频率或风险评分对交易记录进行排序。
  • 客户支持:根据工单的紧急程度或打开时间对客户请求进行排序。

方案架构

通过自定义规则对搜索结果进行排序是通过Elasticsearch的排序API实现的。通过调用排序API查询数据,实现数据按自定义规则排序。

自定义规则查询有两种方式。

  • 绝对好评率计算总分,按照总分由高到低的顺序排列出查询结果。
    总分 = 匹配得分 * (好评率 * 绝对因子)
    • 匹配得分:根据查询结果计分,内容匹配记1分,否则记0分,得分之和即为匹配得分。
    • 好评率:从匹配项的数据内容中获取好评率的值,一般指单条数据的评分。
    • 绝对因子:自定义的好评比例。
  • 相对好评率计算总分,按照总分由高到低的顺序排列查询结果。
    总分 = 匹配得分 * (好评率 * 相对分数)
    • 匹配得分:根据查询结果计分,内容匹配记1分,否则记0分,得分之和即为匹配得分。
    • 好评率:从匹配项的数据内容中获取好评率的值,一般指单条数据的评分。
    • 相对分数:自定义一个好评率阈值,当好评率大于阈值时,返回一个自定义的相对分数;当好评率小于等于阈值时,返回另一个自定义的相对分数。通过这种方式可以避免异常好评率对查询结果的影响。

方案优势

  • 灵活性:自定义排序规则可以满足各种复杂的业务需求。
  • 扩展性:Elasticsearch的分布式特性支持水平扩展,适应不断增长的数据量。
  • 性能:Elasticsearch的优化机制确保了排序操作的高效性,即使在大规模数据集上也能保持良好的性能。
  • 实时性:Elasticsearch的近实时搜索能力确保排序结果的时效性。

前提条件

已经准备好Elasticsearch集群,且集群处于可用状态。

操作步骤

本文的代码示例仅适用于Elasticsearch 7.x及以上版本的集群。

  1. 登录云搜索服务管理控制台。
  2. 在左侧导航栏,选择“集群管理”,进入Elasticsearch集群列表页面。
  3. 在集群列表页面中,单击集群操作列的“Kibana”登录Kibana页面。
  4. 在Kibana的左侧导航中选择“Dev Tools”,进入命令执行页面。
  5. 创建索引,并指定自定义映射来定义数据类型。

    例如,数据文件“tv.json”的内容如下所示。

    {
    "tv":[
    { "name": "tv1", "description": "USB, DisplayPort", "vote": 0.98 }
    { "name": "tv2", "description": "USB, HDMI", "vote": 0.99 }
    { "name": "tv3", "description": "USB", "vote": 0.5 }
    { "name": "tv4", "description": "USB, HDMI, DisplayPort", "vote": 0.7 }
    ]
    }

    可以执行如下命令,创建索引“mall”,并指定自定义映射来定义数据类型。

    PUT /mall?pretty
    {
      "mappings": {
        "properties": {
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword"
              }
            }
          },
          "description": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword"
              }
            }
          },
          "vote": {
            "type": "float"
          }
        }
      }
    }
  6. 导入数据。
    执行如下命令,将“tv.json”文件中的数据导入到“mall”索引中。
    POST /mall/_bulk?pretty
    { "index": {"_id": "1"}}
    { "name": "tv1", "description": "USB, DisplayPort", "vote": 0.98 }
    { "index": {"_id": "2"}}
    { "name": "tv2", "description": "USB, HDMI", "vote": 0.99 }
    { "index": {"_id": "3"}}
    { "name": "tv3", "description": "USB", "vote": 0.5 }
    { "index": {"_id": "4"}}
    { "name": "tv4", "description": "USB, HDMI, DisplayPort", "vote": 0.7 }
  7. 自定义规则查询数据。分别列举了绝对好评率和相对好评率查询方式。
    假设用户想要查询有USB接口、HDMI接口、DisplayPort接口的电视机,并根据好评率计算各款电视机的总分,根据总分由高到低的顺序排列结果。
    • 用绝对好评率计算总分

      总分的计算公式为“new_score = query_score * (vote * factor)”,执行的命令如下:

      GET /mall/_doc/_search?pretty
      {
        "query":{
          "function_score":{
            "query":{
              "bool":{
                "should":[
                  {"match": {"description": "USB"}},
                  {"match": {"description": "HDMI"}},
                  {"match": {"description": "DisplayPort"}}
                ]
              }
            },
            "field_value_factor":{
              "field":"vote",
              "factor":1
            },
            "boost_mode":"multiply",
            "max_boost":10
          }
        }
      }
      返回结果如下所示,按照总分由高到低的顺序排列查询结果。
      {
        "took" : 4,
        "timed_out" : false,
        "_shards" : {
          "total" : 1,
          "successful" : 1,
          "skipped" : 0,
          "failed" : 0
        },
        "hits" : {
          "total" : {
            "value" : 4,
            "relation" : "eq"
          },
          "max_score" : 0.8388366,
          "hits" : [
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "4",
              "_score" : 0.8388366,
              "_source" : {
                "name" : "tv4",
                "description" : "USB, HDMI, DisplayPort",
                "vote" : 0.7
              }
            },
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "2",
              "_score" : 0.7428025,
              "_source" : {
                "name" : "tv2",
                "description" : "USB, HDMI",
                "vote" : 0.99
              }
            },
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "1",
              "_score" : 0.7352994,
              "_source" : {
                "name" : "tv1",
                "description" : "USB, DisplayPort",
                "vote" : 0.98
              }
            },
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "3",
              "_score" : 0.03592815,
              "_source" : {
                "name" : "tv3",
                "description" : "USB",
                "vote" : 0.5
              }
            }
          ]
        }
      }
    • 用相对好评率计算总分。

      总分的计算公式为“new_score = query_score * inline”,本示例中设置的好评率阈值为0.8,当vote>0.8时,inline取值为1;当vote≤0.8时,inline取值为0.5。执行命令如下:

      GET /mall/_doc/_search?pretty
      {
        "query":{
          "function_score":{
            "query":{
              "bool":{
                "should":[
                  {"match":{"description":"USB"}},
                  {"match":{"description":"HDMI"}},
                  {"match":{"description":"DisplayPort"}}
                ]
              }
            },
            "script_score": {
              "script": {
                "params": {
                  "threshold": 0.8
                },
                "inline": "if (doc[\"vote\"].value > params.threshold) {return 1;} return 0.5;"
              }
            },
            "boost_mode":"multiply",
            "max_boost":10
          }
        }
      }

      返回结果如下所示,按照总分由高到低的顺序排列查询结果。

      {
        "took" : 4,
        "timed_out" : false,
        "_shards" : {
          "total" : 1,
          "successful" : 1,
          "skipped" : 0,
          "failed" : 0
        },
        "hits" : {
          "total" : {
            "value" : 4,
            "relation" : "eq"
          },
          "max_score" : 0.75030553,
          "hits" : [
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "1",
              "_score" : 0.75030553,
              "_source" : {
                "name" : "tv1",
                "description" : "USB, DisplayPort",
                "vote" : 0.98
              }
            },
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "2",
              "_score" : 0.75030553,
              "_source" : {
                "name" : "tv2",
                "description" : "USB, HDMI",
                "vote" : 0.99
              }
            },
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "4",
              "_score" : 0.599169,
              "_source" : {
                "name" : "tv4",
                "description" : "USB, HDMI, DisplayPort",
                "vote" : 0.7
              }
            },
            {
              "_index" : "mall",
              "_type" : "_doc",
              "_id" : "3",
              "_score" : 0.03592815,
              "_source" : {
                "name" : "tv3",
                "description" : "USB",
                "vote" : 0.5
              }
            }
          ]
        }
      }

相关文档