基于Elasticsearch的企业信息查询优化实践

需求

基于elasticsearch基础的相关度查询,返回的结果往往不是我们想要的结果。精确率达不到要求。如何能做到让搜索引擎返回的结果就是我想要搜的,这是一个伪命题。elasticsearch官方说明:

通常,经过对策略字段应用权重提升,或通过对查询语句结构的调整来强调某个句子的重要性这些方法,就足以获得良好的结果。有时,如果 Lucene 基于词的 TF/IDF 模型不再满足评分需求(例如希望基于时间或距离来评分),则需要更具侵略性的调整。

除此之外,相关度的调试就如兔子洞,一旦跳进去就很难再出来。 最相关 这个概念是一个难以触及的模糊目标,通常不同人对文档排序又有着不同的想法,这很容易使人陷入持续反复调整而没有明显进展的怪圈。

但是为了满足大多数人的需求,比如我搜索京东,我就是想要看京东商城,并不想看到什么北京东芦小卖铺这种。。。对于这种优化,我们慢慢道来。

确定搜索项

1.某查的公司信息查询的一个搜索框在默认情况下,要从几个字段搜索,比如企业名称、法人/股东、联系方式、经营范围、地址、网址、机构代码、注册号等。。。
面对这种情况,我们肯定会想到这是多字段搜索,用multi_match满足多字段搜索。

GET gongsi_*/_search
{
  "query": {
      "multi_match": {
        "query": "京东",
        "fields": [
          "name",
          "alias",
          "business_scope",
          "legal_person_name",
          "reg_location",
          "staff_name",
          "web_list"
        ]
      }
      
  },
  "size": 30
}

返回的结果排序如下所示

"_score" : 25.605856,"name" : "北京东芦京东超市",
"_score" : 24.354025,"name" : "京东寿司",
...

我东哥的狗东呢?

由于Lucene的计算评分方式,把我东哥的京东排序排没影了。
最简单的基于TF/IDF评分方式:

  1. 词在文档中出现的频度是多少? 频度越高,权重 越高 。 5 次提到同一词的字段比只提到 1 次的更相关。
  2. 词在集合所有文档里出现的频率是多少?频次越高,权重 越低 。
  3. 字段的长度是多少? 字段越短,字段的权重 越高 。如果词出现在类似标题 title 这样的字段,要比它出现在内容 body 这样的字段中的相关度更高。

提升权重

我在搜索京东的时候,因为reg_location参与了搜索,导致连注册地址为北京东城区XXX的也被搜出来了。
而我想要的效果是在name里出现京东的时候要比在reg_location出现京东权重更高。排名应该更靠前。
这种情况我需要给multi_match的name字段提升权重。
好消息是提升权重是很容易的事:
先介绍一个基础知识,multi_match 多字段匹配查询的类型有多种,用的比较多的即: best_fields 、 most_fields 和 cross_fields (最佳字段、多数字段、跨字段)。
简单来说就是best_fields 是在上述那么多字段里 以某个尽可能和关键词相关的字段最后计算的评分为主。比如alias比name要短,所以alias计算出来的评分是比name要高的。以alias计算的评分为主,其他字段计算的评分为辅。
most_fields 是尽量多的字段匹配关键词,简单来说就是两个字段里都有京东的要比只有一个字段里有京东的评分高。
cross_fields 使用词中心式(term-centric)的查询方式,这与 best_fields 和 most_fields 使用字段中心式(field-centric)的查询方式非常不同,它将所有字段当成一个大字段,并在 每个字段 中查找 每个词 。

在这里我可能更想使用best_fields,来提高我搜索的准确率。

GET gongsi_*/_search
{
  "query": {
      "multi_match": {
        "query": "京东",
        "type": "best_fields",
        "fields": [
          "name^2",
          "alias^1.5",
          "business_scope",
          "legal_person_name",
          "reg_location",
          "staff_name",
          "web_list"
        ],
        "tie_breaker": "0.3",
        "minimum_should_match": "75%"
      }
      
  },
  "size": 30
}

改造完以后,增加了type:best_fields , 并且赋予name2的权重,alias1.5的权重。
tie_breaker是表示将其他query的分数,乘以tie_breaker,然后综合与最高分数的那个query的分数,综合在一起进行计算
除了取最高分以外,还会考虑其他的query的分数
minimum_should_match 是为了减掉长尾。比如京东如果分词为京和东,最小匹配75%就是说只含有京或者只含有东的不会被匹配到。

返回结果如下:

"_score" : 31.691607,"name" : "渑池县京东帮服务站",
"_score" : 30.991676,"name" : "灵宝市京东帮服务店",

这样优化完以后,还是见不到我东哥的狗东,纳尼??

评分参考一些别的权重

分析一下排名靠前的都是什么数据?
个体工商户,还有什么注销的企业。这些排序那么靠前不是砸我们网站招牌嘛。。。
我来搜京东,翻了几十页都看不到我东哥的公司。我就想要骂街了,这什么垃圾网站。

所以:
我并不想让这些个体工商户排那么靠前怎么办?
这里就用到了function_score,通过function重新根据返回的_score重新计算评分。这正是我想要的。

整理一下规则:
如果公司类型是个体工商户的话,就降权,是有限公司的话就升权重。
如果公司注册状态是在业或者存续的话,就升权重,如果是那种注销或者被吊销的,就别排那么靠前。
采集到的数据有个score,这个字段就是某查对某个公司的评分字段。这个字段我们肯定要好好利用好好参考。这个字段的权重很有参考意义。
如果公司注册资本很高的话,也应该往前排一些。

GET gongsi_*/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query": "京东",
          "type": "best_fields",
          "fields": [
            "name^2",
            "alias^1.5",
            "business_scope",
            "legal_person_name",
            "reg_location",
            "staff_name",
            "web_list"
          ],
          "tie_breaker": "0.3",
          "minimum_should_match": "50%"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "score",
            "factor": 0.01
          }
        },
        
        {
          "script_score": {
            "script": " return doc['reg_status'].value == '注销' ? 1 : 2;"
          }
        },
        {
          "script_score": {
            "script": " return doc['company_org_type'].value.indexOf('有限责任公司') > -1 ? 2 : 1;"
          }
        }
      ],
      "boost_mode": "multiply"
    }
  },
  "size": 30,
  "highlight": {
    "fields": {
      "name": {},
      "alias": {},
      "business_scope": {},
      "legal_person_name": {},
      "reg_location": {},
      "staff_name": {},
      "web_list": {}
    }
  }
}

更新以后的规则搜索结果如下所示:

"_score" : 10256.357,"name" : "江苏京东信息技术有限公司",
"_score" : 9654.262,"name" : "京东数字科技控股有限公司",
"_score" : 9309.976,"name" : "北京京东世纪信息技术有限公司",

前面这几家公司都是我东哥的。跟某查那边的搜索结果基本上相似。

又试了几个关键词。也基本上首页能搜到想要的公司。。。

注意*
如果这里提高权重的算法提前确定以后,建议把计算的值在索引时候添加。搜索时就不要再计算了。有助于提高性能。

问题解决

  1. 京东 和 京东方 这种,我搜索京东老是出来京东方的东西,理论上京东方不应该分词为京东。
    分词设置从ik_max_word更换为ik_smart解决了
  2. 我搜索京东,也会出现‘北京东瑞顺成商贸有限公司’这种。理论上城市名字也不应该分词。
但是实际上是有北京和京东这两个关键词的,因为采用了ik_smart这种分词方式。在消除歧义的时候,不太清楚为什么会分成北,京东,瑞,顺,成,商贸,有限公司,这得需要去看IK分词器 消歧义的部分代码。。目前没有深入研究

目前存在的问题

  1. 类似置顶功能无法实现。
比如我在搜索京东时候,需要把京东世纪贸易有限公司置顶。对于一些热搜,也该出现临时排名最高的情况。或者是广告
我的想法是不在搜索引擎里解决,在服务端建个运营人员设置置顶的操作,把某些数据置顶以后,拿到搜索引擎排序的数据以后,把这些置顶的提取出来重排序
  1. 搜索产品名字无法搜到公司名字。比如我搜索美团无法出现北京三快科技
后期会增加产品或者业务或者项目字段。来一起搜
或者同样把一些出名的产品建立单独的库,在前端单独处理排序结果。
  1. 热搜效果没法实现。

  2. 排序还是无法达到最理性的状态。

搜索排序能帮助用户更便捷地找到满足其需求的信息,改进用户体验,提升转化效果。搜索排序是个极其重要的过程。同时想要做好搜索排序也是极其复杂的过程,需要深厚的算法底子来支撑。

如果你看到这里了。想要做下一步优化了。
那就上机器学习吧。
Learning To Rank

推荐阅读

https://elasticsearch.cn/slides/123#page=3

猜你喜欢

转载自blog.csdn.net/u013705066/article/details/103476851
今日推荐