elasticsearch实现中文与拼音的搜索联想

常见的电商搜索比如京东、淘宝,输入面膜或者mm,下拉框会有很多引导用户去选择的关键字,比如 面膜 面霜、面膜 补水 ,因为最近项目需求需要加入搜索联想的功能,在这过程中碰过很多次坑,所以在这记录。

常见的搜索联想有通过数据库来实现,比如mysql、oracle,通过sql语句的LIKE 查询,可以实现前缀匹配。这种在数据量不大的情况下是可以的,但是一般电商平台的索引数据量都是非常大,这样查出来的速度就很慢,用户体验也很不好。另外一种是使用搜索引擎实现的搜索,因为搜索引擎会给每个分词加索引,我们获取回来就很快。

倒排索引

Elasticsearch使用一种叫做倒排索引(inverted index)的结构来做快速的全文搜索。倒排索引由在文档中出现的唯一的单词列表,以及对于每个单词在文档中的位置组成。
正序索引 是一个 索引对应一个文档字段

索引 文档 文档
1 中国 中华人民共和国
2 中国 美国

倒排索引 是把文档字段分词,对应文档的索引

文档 1 2
中国 X X
中华人民共和国 X
美国 X

使用elasticsearch实现的搜索联想就是通过分词器进行分词
生成tokens,然后通过倒排索引的方式来搜索出所在的文档,然后会显回来。

spring-boot整合elasticsearch实现搜索联想

pom.xml文件

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>

    <dependencies>

        <!-- Spring Boot Elasticsearch 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

        <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.nlpcn</groupId>
            <artifactId>nlp-lang</artifactId>
            <version>1.7.6</version>
        </dependency>
    </dependencies>

引入一个简单的spring-boot整合elasticsearch的项目,和拼音jar包

注解实现的实体

@Document(indexName = "cityindex", type = "citysuggest")
public class CitySuggest  implements Serializable{
  
  /**
   * 
   */
  private static final long serialVersionUID = 1L;
  
  @Field(type=FieldType.Long)
  private Long id;
  
  @Field(type=FieldType.String)
  private String keyword;
  
  @CompletionField(analyzer="ik_smart",searchAnalyzer="ik_smart",payloads=false)
  private Completion suggesttag;
  

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public Completion getSuggesttag() {
    return suggesttag;
  }

  public void setSuggesttag(Completion suggesttag) {
    this.suggesttag = suggesttag;
  }

  public String getKeyword() {
    return keyword;
  }

  public void setKeyword(String keyword) {
    this.keyword = keyword;
  }
}

注意:同一个索引中的id注解模式@Field(type=FieldType.Long),所有的type中要一致,不然后面定义的联想无效
实现联想的字段类型Completion,也就是官网上面的"type":"competeion"
该项目是elasticsearch2.3.3 + sping-boot 1.7.5
进行分词生成索引

public boolean updateSuggest(City city) {
    AnalyzeRequestBuilder requestBuilder = new AnalyzeRequestBuilder(esClient, AnalyzeAction.INSTANCE, "cityindex", city.getCityname());
    requestBuilder.setAnalyzer("ik_smart");
    AnalyzeResponse response = requestBuilder.get();
    List<AnalyzeToken> tokens = response.getTokens();
    List<String> input = new ArrayList<String>();
    List<CitySuggest> citySuggests = new ArrayList<CitySuggest>();
    for (AnalyzeToken token : tokens) {
      if (token.getTerm().length() < 2) {
        continue;
      }
      if (!input.contains(token.getTerm())) {
        input.add(token.getTerm());
      }
    }
    
    //关键字处理
    for(int i=0,j=input.size();i<j;i++){
      CitySuggest citySuggest = new CitySuggest();
      List<String> itemInput = new ArrayList<String>();
      itemInput.add(input.get(i));
      
      itemInput.add(Pinyin.list2StringSkipNull(Pinyin.pinyin(input.get(i)),""));
      itemInput.add(Pinyin.list2StringSkipNull(Pinyin.firstChar(input.get(i)),""));
      Completion completion = new Completion(list2String(itemInput));
      completion.setOutput(input.get(i));
      citySuggest.setId((i+1L));
      citySuggest.setSuggesttag(completion);
      citySuggest.setKeyword(input.get(i));
      citySuggests.add(citySuggest);
    }
    for(int i=0;i<citySuggests.size();i++){
      citySuggestRepository.save(citySuggests.get(i));
    }
    return true;
  }

获取联想数据的接口

public List<String> suggest(String prefix) {
    CompletionSuggestionBuilder suggestion = SuggestBuilders.completionSuggestion("complete");
    suggestion.analyzer("ik_smart");
    //suggesttag是联想数据字段
    suggestion.text(prefix).field("suggesttag");
    SearchResponse response = this.esClient.prepareSearch("cityindex").setTypes("citysuggest").addSuggestion(suggestion).execute().actionGet();
    Suggest suggest = response.getSuggest();
    // 没有任何数据
    if (suggest == null) {
      return new ArrayList<String>();
    }
    List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> list = response.getSuggest().getSuggestion("complete").getEntries();
    List<String> suggestList = new ArrayList<String>();
    if (list == null) {
      return null;
    } else {
      for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> e : list) {
        for (Suggest.Suggestion.Entry.Option option : e) {
          suggestList.add(option.getText().toString());
        }
      }
    }
    return suggestList;
  }
测试接口

http://10.0.0.80:8080/api/city/suggest?content=mm
效果数据

{
    "result": 0,
    "msg": "获取数据成功",
    "nowtime": 1551255460698,
    "suggests": [
        "面膜"
    ]
}
动态生成索引
POST  /gangyanindex/goodsuggest/_mapping
{
"goodsuggest": {
    "properties": {
        "suggesttag": {
        "max_input_length": 50,
        "payloads": false,
        "analyzer": "ik_smart",
        "preserve_position_increments": true,
        "type": "completion",
        "preserve_separators": true
     },
      "id": {
          "index": "not_analyzed",
          "type": "string"
       },
      "keyword": {
       "type": "string"
      }
   }
 }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容