9

仿京东淘宝搜索框实战 - 简书

 4 years ago
source link: https://www.jianshu.com/p/45752375f903?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

模拟实战京东搜索效果(一)

0.1762020.04.14 08:05:20字数 1,451阅读 730

搜索是很多内容类app必不可少的功能,而搜索框提示则为用户关键词输入提供了一种引导,一个友好的搜索框提示不仅能提升用户体验,还能帮助用户节省触达商品的时间提升搜索效率。

webp
image.png
webp
image.png
image.png

搜索框功能主要有3部分组成:

  • 智能补全
  • 关联数量
  • 拼写纠错

ES官方文档建议通过phrase Suggester实行搜索框的自动补全,但这种查询对中文支持不太友好,经常会不做提示;下面我们通过n-gram来实现符合中国人民使用习惯的提示框。

  • 什么是edge n-gram

假设有一个词hello,普通建索引时,就是把这个词hello放入倒排索引
用户输入h、he时会找不到索引(倒排索引中只有hello),因此匹配失败
而对于输入即搜索这种应用场景,可以使用一种特殊的n-gram,称为边界n-grams (edge n-grams)
所谓的edge n-gram,就是指它会固定从一边开始,进行窗口滑动,每次滑动长度为1,最终的结果取决于 n 的选择长度
以单词hello为例,它的edge n-gram的结果如下:

h
he
hel
hell
hello

因此可以发现到,在使用edge n-gram建索引时,一个单词会生成好几个索引,而这些索引一定是从头开始
这符合了输入即搜索的特性,即是用户打h、he能找到倒排中的索引h、he,而这些索引对应著的数据就是hello

依赖插件:

构建索引库:

PUT st
{
  
    "mappings" : {
      "dynamic" : "false",
      "properties" : {
        "suggest" : {
          "type" : "text",
          "analyzer" : "autocomplete" ,
          "search_analyzer": "autocomplete_search"
        },
        "weight" : {
          "type" : "integer"
        },
        "count" : {
          "type" : "integer"
        }
      }
    },
    "settings" : {
      "index" : {
        "number_of_shards" : "1",
        "analysis" : {
          "filter" : {
            "length_filter" : {
              "type" : "length",
              "min" : "2"
            },
            "my_pinyin" : {
              "keep_joined_full_pinyin" : "true",
              "lowercase" : "true",
              "keep_original" : "true",
              "remove_duplicated_term" : "true",
              "keep_separate_first_letter" : "false",
              "type" : "pinyin",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            }
          },
          "analyzer" : {
            "autocomplete_search" : {
              "filter" : [
                "lowercase"
              ],
              "tokenizer" : "search_py"
            },
            "autocomplete" : {
              "filter" : [
                "lowercase",
                "length_filter",
                "my_pinyin"
              ],
              "tokenizer" : "autocomplete"
            } 
          },
          "tokenizer" : {
            "autocomplete" : {
               
              "min_gram" : "2",
              "type" : "edge_ngram",
              "max_gram" : "16"
            } ,
            "search_py" : {
              "keep_joined_full_pinyin" : "true",
              "keep_none_chinese_in_first_letter " : "true",
              "lowercase" : "true",
              "none_chinese_pinyin_tokenize" : "false",
              "keep_none_chinese_in_joined_full_pinyin" : "true",
              "keep_original" : "true",
              "keep_first_letter" : "true",
              "keep_separate_first_letter" : "false",
              "type" : "pinyin",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "false"
            }
          }
        },
        "number_of_replicas" : "1" 
      }
    }
}

Mapping
  • _id
    文档id, 这里取关键词md5后的字符串作为id,方便更新和删除
  • suggest
    离线任务定时提取用户高频输入的搜索关键词与商品分类、标签信息索引到该字段;其中analyzer与search_analyzer分别使用自定义分词器。
  • weight
    关键词权重;默认取词频,也可以手动指定
  • count
    搜索词关联的商品数量
Setting
  • length_filter
    用来控制分词后的term长度,这里限制为2,当term字符长度<2时会被忽略
  • pinyin
keep_first_letter:这个参数会将词的第一个字母全部拼起来.例如:刘德华->ldh.默认为:true
keep_separate_first_letter:这个会将第一个字母一个个分开.例如:刘德华->l,d,h.默认为:flase.如果开启,可能导致查询结果太过于模糊,准确率太低.
limit_first_letter_length:设置最大keep_first_letter结果的长度,默认为:16
keep_full_pinyin:如果打开,它将保存词的全拼,并按字分开保存.例如:刘德华> [liu,de,hua],默认为:true
keep_joined_full_pinyin:如果打开将保存词的全拼.例如:刘德华> [liudehua],默认为:false
keep_none_chinese:将非中文字母或数字保留在结果中.默认为:true
keep_none_chinese_together:保证非中文在一起.默认为: true, 例如: DJ音乐家 -> DJ,yin,yue,jia, 如果设置为:false, 例如: DJ音乐家 -> D,J,yin,yue,jia, 注意: keep_none_chinese应该先开启.
keep_none_chinese_in_first_letter:将非中文字母保留在首字母中.例如: 刘德华AT2016->ldhat2016, 默认为:true
keep_none_chinese_in_joined_full_pinyin:将非中文字母保留为完整拼音. 例如: 刘德华2016->liudehua2016, 默认为: false
none_chinese_pinyin_tokenize:如果他们是拼音,切分非中文成单独的拼音项. 默认为:true,例如: liudehuaalibaba13zhuanghan -> liu,de,hua,a,li,ba,ba,13,zhuang,han, 注意: keep_none_chinese和keep_none_chinese_together需要先开启.
keep_original:是否保持原词.默认为:false
lowercase:小写非中文字母.默认为:true
trim_whitespace:去掉空格.默认为:true
remove_duplicated_term:保存索引时删除重复的词语.例如: de的>de, 默认为: false, 注意:开启可能会影响位置相关的查询.
ignore_pinyin_offset:在6.0之后,严格限制偏移量,不允许使用重叠的标记.使用此参数时,忽略偏移量将允许使用重叠的标记.请注意,所有与位置相关的查询或突出显示都将变为错误,您应使用多个字段并为不同的字段指定不同的设置查询目的.如果需要偏移量,请将其设置为false。默认值:true

  • autocomplete_search
    查询时使用的分词器
  • autocomplete
    索引时使用的分词器
  • edge_ngram
    edge_ngram是ES自带的token解析器,从min_gram处开始依次分词,当达到max_gram或至文本结尾时停止分词。

插入测试数据

curl -XPUT "http://1.0.0.1:9200/st/_doc/1" -H 'Content-Type: application/json' -d'{   "suggest": "小米手机",  "count":110,"weight":10}'

curl -XPUT "http://1.0.0.1:9200/st/_doc/2" -H 'Content-Type: application/json' -d'{   "suggest": "小米手机新款",  "count":110,"weight":8}'

curl -XPUT "http://1.0.0.1:9200/st/_doc/3" -H 'Content-Type: application/json' -d'{   "suggest": "小米手机 5g",  "count":110,"weight":10}'

curl -XPUT "http://1.0.0.1:9200/st/_doc/4" -H 'Content-Type: application/json' -d'{   "suggest": "小米128g",  "count":110,"weight":6}'

curl -XPUT "http://1.0.0.1:9200/st/_doc/5" -H 'Content-Type: application/json' -d'{   "suggest": "小米袋装",  "count":110,"weight":6}'

curl -XPUT "http://1.0.0.1:9200/st/_doc/6" -H 'Content-Type: application/json' -d'{   "suggest": "华为5g新款",  "count":110,"weight":10}'

curl -XPUT "http://1.0.0.1:9200/st/_doc/7" -H 'Content-Type: application/json' -d'{   "suggest": "华为手机",  "count":110,  "weight":10}'

curl -XPUT "http://1.0.0.1:9200/st/_doc/8" -H 'Content-Type: application/json' -d'{   "suggest": "小蜜蜂", "count":110, "weight":8}'

  • 全量同步商城分类及标签数据,这类权重值给到Int最大值
  • 通过spark离线任务增量迭代客户端搜索埋点日志,剔除过长搜索词后进行分组统计,并将sessionid去重后>3的入库

关联商品数量

  • 在增量更新suggest中关键词时会去商品库进行一次聚合查询
  • 为了避免对用户搜索业务的影响,任务会放在凌晨进行
  • 为提升效率,应该使用Elasticsearch的Multi Search接口批量进行count,同时批量更新数据库里建议词的count值
  • 由于商品是实时入库的而关联统计是定期离线执行的,所以在进行相关提示时会显示“约”
  • 离线任务会定时对近一月用户输入关键词进行分析统计,将weight值<10的删除,weight>10的入库,以修正增量数据的误差
  • 删除时不要使用delete by query进行删除,这样会非常损耗es的性能,应该使用关键词md5后的id批量删除
  • 由于数据量较大,更新时间比较长,为了不影响搜索性能,这里会先写入到一个临时索引库,通过bulk批量添加到临时索引中,然后通过别名切换来更新;

自动补全效果展示

  • 输入“小米sj”获取【小米手机】相关提示词;支持特殊符号输入(如 H&M服饰),也可全拼音或全汉字输入
GET st/_search
{
  "sort": [
    {
      "weight": {
        "order": "desc"
      }
    }
  ], 
  "query": {
        "multi_match": {
          "query": "小米sj",
          "fuzziness": 1,
          "prefix_length" : 1,
          "fields": ["suggest"]
        }
      }

}

需要注意的是,这里使用fuzziness来模糊匹配提升用户体验,fuzziness=1 允许用户输入一个错别字,并通过prefix_length设置为1来跳过开头首个字符的判断,因为一般用户输入出错大多发生在后面

{
        "_index" : "st",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : null,
        "_source" : {
          "suggest" : "小米手机",
          "count" : 110,
          "weight" : 10
        },
        "sort" : [
          10
        ]
      },
      {
        "_index" : "st",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : null,
        "_source" : {
          "suggest" : "小米手机新款",
          "count" : 110,
          "weight" : 8
        },
        "sort" : [
          8
        ]
      }

智能纠错效果展示

  • 如果Elasticsearch返回的是空结果,此时应该需要增加拼写纠错的处理(拼写纠错也可以在调用Elasticsearch搜索的时候带上,但是通常情况下用户并没有拼写错误,所以建议还是在后面单独调用suggester);如果返回的suggest不为空,则根据新的词调用建议词服务;比如用户输入了【小密】,调用Elasticsearch的suggester获取到的结果是【小米】,则再根据小米进行搜索建议词处理
GET st/_search
{
  "_source": false, 
  "size": 0, 
  "suggest": { 
    "term-suggestion": {
      "text": "小密",
     "term": {
       "field": "suggest",
       "min_word_length":2, 
       "prefix_length":1,
       “min_doc_freq”:2,
       "size":2
     }
    }
  }
}

需要注意:
其中min_word_length是用来控制候选词长度的,这里设置为2,意思是当term长度>=2才会被显示;
prefix_length=1表示忽略首字符是错别字,大多数输入错别字发生在后面;
min_doc_freq 当建议词出现文档频率低于该值时将被忽略,线上可适当调大该值以提升搜索效果

  • 返回结果
{
        "text" : "小密",
        "offset" : 0,
        "length" : 0,
        "options" : [
          {
            "text" : "小米",
            "score" : 0.5,
            "freq" : 5
          },
          {
            "text" : "小蜜",
            "score" : 0.5,
            "freq" : 1
          }
        ]
      }

易企秀商城


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK