Elasticsearch汉字补全和拼写纠错

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: Elasticsearch汉字补全和拼写纠错

1 使用ES实现的效果

汉字补全

拼写纠错

2 产品搜索与自动补全

Term suggester :词条建议器。对给输入的文本进进行分词,为每个分词提供词项建议

Phrase suggester :短语建议器,在term的基础上,会考量多个term之间的关系

Completion Suggester,它主要针对的应用场景就是"Auto Completion"

Context Suggester:上下文建议器

GET product_completion_index/_search
{
"from": 0,
"size": 100,
"suggest": {
 "czbk-suggest": {
  "prefix": "小米",
  "completion": {
   "field": "searchkey",
   "size": 20,
   "skip_duplicates": true
  }
 }
}
}

2.1 汉字补全OpenAPI

2.1.1 定义自动补全接口

GET product_completion_index/_search
{
"from": 0,
"size": 100,
"suggest": {
 "czbk-suggest": {
  "prefix": "小米",
  "completion": {
   "field": "searchkey",
   "size": 20,
   "skip_duplicates": true
  }
 }
}
}
package com.oldlu.service;
import com.oldlu.commons.pojo.CommonEntity;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import java.util.List;
import java.util.Map;
/**
* @Class: ElasticsearchDocumentService
* @Package com.oldlu.service
* @Description: 文档操作接口
* @Company: http://www.oldlu.com/
*/
public interface ElasticsearchDocumentService {
  //自动补全(完成建议)
  public List<String> cSuggest(CommonEntity commonEntity) throws Exception;
}

2.1.2 定义自动补全实现

/*
  * @Description: 自动补全 根据用户的输入联想到可能的词或者短语
  * @Method: suggester
  * @Param: [commonEntity]
  * @Update:
  * @since: 1.0.0
  * @Return: org.elasticsearch.action.search.SearchResponse
  *
  */
  public List<String> cSuggest(CommonEntity commonEntity) throws Exception {
    //定义返回
    List<String> suggestList = new ArrayList<>();
    //构建查询请求
    SearchRequest searchRequest = new
SearchRequest(commonEntity.getIndexName());
    //通过查询构建器定义评分排序
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
    //构造搜索建议语句,搜索条件字段
    CompletionSuggestionBuilder completionSuggestionBuilder =new
CompletionSuggestionBuilder(commonEntity.getSuggestFileld());
    //搜索关键字
    completionSuggestionBuilder.prefix(commonEntity.getSuggestValue());
    //去除重复
    completionSuggestionBuilder.skipDuplicates(true);
    //匹配数量
    completionSuggestionBuilder.size(commonEntity.getSuggestCount());
    searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion("czbk-
suggest", completionSuggestionBuilder));
    //czbk-suggest为返回的字段,所有返回将在czbk-suggest里面,可写死,sort按照评分排
    searchRequest.source(searchSourceBuilder);
    //定义查找响应
    SearchResponse suggestResponse = client.search(searchRequest,
RequestOptions.DEFAULT);
    //定义完成建议对象
    CompletionSuggestion completionSuggestion =
suggestResponse.getSuggest().getSuggestion("czbk-suggest");
    List<CompletionSuggestion.Entry.Option> optionsList =
completionSuggestion.getEntries().get(0).getOptions();
    //从optionsList取出结果
    if (!CollectionUtils.isEmpty(optionsList)) {
      optionsList.forEach(item ->
suggestList.add(item.getText().toString()));
   }
    return suggestList;
 }

2.1.3 定义自动补全控制器

/*
  * @Description 自动补全
  * @Method: suggester
  * @Param: [commonEntity]
  * @Update:
  * @since: 1.0.0
  * @Return: com.oldlu.commons.result.ResponseData
  *
  */
  @GetMapping(value = "/csuggest")
  public ResponseData cSuggest(@RequestBody CommonEntity commonEntity) {
    // 构造返回数据
    ResponseData rData = new ResponseData();
    if (StringUtils.isEmpty(commonEntity.getIndexName()) ||
StringUtils.isEmpty(commonEntity.getSuggestFileld()) ||
StringUtils.isEmpty(commonEntity.getSuggestValue())) {
      rData.setResultEnum(ResultEnum.PARAM_ISNULL);
      return rData;
   }
    //批量查询返回结果
    List<String> result = null;
    try {
      //通过高阶API调用批量新增操作方法
      result = elasticsearchDocumentService.cSuggest(commonEntity);
      //通过类型推断自动装箱(多个参数取交集)
      rData.setResultEnum(result, ResultEnum.SUCCESS, result.size());
      //日志记录
      logger.info(TipsEnum.CSUGGEST_GET_DOC_SUCCESS.getMessage());
   } catch (Exception e) {
      //日志记录
      logger.error(TipsEnum.CSUGGEST_GET_DOC_FAIL.getMessage(), e);
      //构建错误返回信息
      rData.setResultEnum(ResultEnum.ERROR);
   }
    return rData;
 }

2.1.4 自动补全调用验证

http://localhost:8888/v1/docs/csuggest

参数

{
 "indexName": "product_completion_index",
 "suggestFileld": "searchkey",
 "suggestValue": "小米",
 "suggestCount": 13
}

indexName索引名称

suggestFileld:自动补全查找列

suggestValue:自动补全输入的关键字

suggestCount:自动补全返回个数(京东是13个)

返回

{
 "code": "200",
 "desc": "操作成功!",
 "data": [
   "小米10",
   "小米10Pro",
   "小米8",
   "小米9",
   "小米充电宝",
   "小米手机",
   "小米摄像头",
   "小米电视",
   "小米电饭煲",
   "小米笔记本",
   "小米耳环",
   "小米路由器"
 ],
 "count": 12
}

tips: 自动补全自动去重

2.2 拼音补全OpenAPI

使用拼音访问【小米】

http://localhost:8888/v1/docs/csuggest

全拼访问
{
 "indexName": "product_completion_index",
 "suggestFileld": "searchkey",
 "suggestValue": "xiaomi",
  "suggestCount": 13
}
全拼访问(分隔)
{
 "indexName": "product_completion_index",
 "suggestFileld": "searchkey",
 "suggestValue": "xiao mi",
  "suggestCount": 13
}
首字母访问
{
 "indexName": "product_completion_index",
 "suggestFileld": "searchkey",
 "suggestValue": "xm",
  "suggestCount": 13
}

2.2.1 下载拼插件

wget https://github.com/medcl/elasticsearch-analysis-

pinyin/releases/download/v7.4.0/elasticsearch-analysis-pinyin-7.4.0.zip

或者

https://github.com/medcl/elasticsearch-analysis-pinyin/releases/tag/v7.4.0

d671074e83c24f808ca94671452bc303.png

当我们创建索引时可以自定义分词器,通过指定映射去匹配自定义分词器

{
 "indexName": "product_completion_index",
 "map": {
   "settings": {
     "number_of_shards": 1,
     "number_of_replicas": 2,
     "analysis": {
       "analyzer": {
"ik_pinyin_analyzer": {
           "type": "custom",
           "tokenizer": "ik_smart",
           "filter": "pinyin_filter"
         }
       },
       "filter": {
         "pinyin_filter": {
           "type": "pinyin",
           "keep_first_letter": true,
           "keep_separate_first_letter": false,
           "keep_full_pinyin": true,
           "keep_original": true,
           "limit_first_letter_length": 16,
           "lowercase": true,
           "remove_duplicated_term": true
         }
       }
     }
   },
   "mapping": {
     "properties": {
       "name": {
         "type": "text"
       },
       "searchkey": {
         "type": "completion",
         "analyzer": "ik_pinyin_analyzer"
       }
     }
   }
 }
}

调用【新增文档开发API】接口进行新增数据

开始拼音补全

3 什么是语言处理(拼写纠错)

场景描述

例如:错误输入"【adidaas官方旗舰店】 ”能够纠错为【adidas官方旗舰店】

3.1 语言处理OpenAPI

GET product_completion_index/_search
{
"suggest": {
 "czbk-suggestion": {
  "text": "adidaas官方旗舰店",
  "phrase": {
   "field": "name",
   "size": 13
  }
 }
}
}

返回

3.1.1 定义拼写纠错接口

//拼写纠错
 public String pSuggest(CommonEntity commonEntity) throws Exception;

3.1.2 定义拼写纠错实现

/*
  * @Description: 拼写纠错
  * @Method: psuggest
  * @Param: [commonEntity]
  * @Update:
  * @since: 1.0.0
  * @Return: java.util.List<java.lang.String>
  *
  */
  @Override
  public String pSuggest(CommonEntity commonEntity) throws Exception {
    //定义返回
    String pSuggestString = new String();
    //定义查询请求
SearchRequest searchRequest = new
SearchRequest(commonEntity.getIndexName());
    //定义查询条件构建器
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //定义排序器
    searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
    //构造短语建议器对象(参数为匹配列)
    PhraseSuggestionBuilder pSuggestionBuilder = new
PhraseSuggestionBuilder(commonEntity.getSuggestFileld());
    //搜索关键字(被纠错的值)
    pSuggestionBuilder.text(commonEntity.getSuggestValue());
    //匹配数量
    pSuggestionBuilder.size(1);
    searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion("czbk-
suggest", pSuggestionBuilder));
    searchRequest.source(searchSourceBuilder);
    //定义查找响应
    SearchResponse suggestResponse = client.search(searchRequest,
RequestOptions.DEFAULT);
    //定义短语建议对象
    PhraseSuggestion phraseSuggestion =
suggestResponse.getSuggest().getSuggestion("czbk-suggest");
    //获取返回数据
    List<PhraseSuggestion.Entry.Option> optionsList =
phraseSuggestion.getEntries().get(0).getOptions();
    //从optionsList取出结果
    if (!CollectionUtils.isEmpty(optionsList)
&&optionsList.get(0).getText()!=null) {
      pSuggestString = optionsList.get(0).getText().string().replaceAll("
","");
   }
    return pSuggestString;
 }

3.1.3 定义拼写纠错控制器

/*
  * @Description: 拼写纠错
  * @Method: suggester2
  * @Param: [commonEntity]
  * @Update:
  * @since: 1.0.0
  * @Return: com.oldlu.commons.result.ResponseData
  *
  */
 @GetMapping(value = "/psuggest")
 public ResponseData pSuggest(@RequestBody CommonEntity commonEntity) {
   // 构造返回数据
   ResponseData rData = new ResponseData();
   if (StringUtils.isEmpty(commonEntity.getIndexName()) ||
StringUtils.isEmpty(commonEntity.getSuggestFileld()) ||
StringUtils.isEmpty(commonEntity.getSuggestValue())) {
     rData.setResultEnum(ResultEnum.PARAM_ISNULL);
     return rData;
}
   //批量查询返回结果
   String result = null;
   try {
     //通过高阶API调用批量新增操作方法
     result = elasticsearchDocumentService.pSuggest(commonEntity);
     //通过类型推断自动装箱(多个参数取交集)
     rData.setResultEnum(result, ResultEnum.SUCCESS, null);
     //日志记录
     logger.info(TipsEnum.PSUGGEST_GET_DOC_SUCCESS.getMessage());
   } catch (Exception e) {
     //日志记录
     logger.error(TipsEnum.PSUGGEST_GET_DOC_FAIL.getMessage(), e);
     //构建错误返回信息
     rData.setResultEnum(ResultEnum.ERROR);
   }
   return rData;
 }

3.1.4 语言处理调用验证

http://localhost:8888/v1/docs/psuggest

参数

{
 "indexName": "product_completion_index",
 "suggestFileld": "name",
 "suggestValue": "adidaas官方旗舰店"
}

indexName索引名称

suggestFileld:自动补全查找列

suggestValue:自动补全输入的关键字

返回

{
 "code": "200",
 "desc": "操作成功!",
 "data": "adidas官方旗舰店"
}

4 总结

  1. 需要一个搜索词库/语料库,不要和业务索引库在一起,方便维护和升级语料库
  2. 根据分词及其他搜索条件去语料库中查询若干条(京东13条、淘宝(天猫)10条、百度4条)记录
    返回
  3. 为了提升准确率,通常都是前缀搜索
相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
6月前
探索正则表达式:强大文本匹配与处理工具
探索正则表达式:强大文本匹配与处理工具
|
6月前
|
数据采集 机器学习/深度学习 自然语言处理
【相关问题解答2】bert中文文本摘要代码:结果输出为一些重复的标点符号和数字
【相关问题解答2】bert中文文本摘要代码:结果输出为一些重复的标点符号和数字
53 0
Elasticsearch之实战掌握误拼写时的fuzzy模糊搜索技术
Elasticsearch之实战掌握误拼写时的fuzzy模糊搜索技术
|
算法 C语言 数据安全/隐私保护
【C++技能树】快速文本匹配 --正则表达式介绍与C++正则表达式使用
假设要判断一个QQ号是否有效,他必须满足以下三个规则
122 0
|
存储 自然语言处理 关系型数据库
Kibana查询语言(KQL)AND、OR匹配,模糊匹配
Kibana查询语言(KQL)AND、OR匹配,模糊匹配
|
存储 自然语言处理 前端开发
基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案
假期重新把之前在新浪博客里面的文字梳理了下,搬到这里。文本介绍基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案。
392 0
基于solr实现通用:输入提示、纠错、拼音搜索、繁体搜索方案
|
存储 自然语言处理 索引
ElasticSearch配置IK灵活匹配单个汉字与词组
需求:在检索单个中文字符时,能够匹配包含该单字的文档;在检索词语时,就不按单字进行匹配。也就是说以商品为例,如果搜索“酒”字,能够匹配到关于“啤酒”“白酒”“红酒”等所有的文档;但如果搜索“啤酒”词语,就只匹配“啤酒”。另外,在匹配时,能够全文匹配的结果排在前面,包含分词匹配的结果排在后面,并且要按匹配度与销量来排序。
|
自然语言处理 Java Maven
如何在java中去除中文文本的停用词
  1.  整体思路 第一步:先将中文文本进行分词,这里使用的HanLP-汉语言处理包进行中文文本分词。 第二步:使用停用词表,去除分好的词中的停用词。 2.  中文文本分词环境配置 使用的HanLP-汉语言处理包进行中文文本分词。
1956 0