ElasticStack系列之十三 & 联想补全策略
业务需求
1. 实现搜索引擎前缀搜索功能(中文,拼音前缀查询及简拼前缀查询功能)
2. 实现摘要全文检索功能,及标题加权处理功能(按照标题权值高内容权值相对低的权值分配规则,按照索引的相关性进行排序,列出前20条相关性最高的文章)
前缀搜索
中文搜索:
1. 搜索“刘”,匹配到“刘德华”、“刘斌”、“刘德志”
2. 搜索“刘德”,匹配到“刘德华”、“刘德志”
小结:搜索的文字需要匹配到集合中所有名字的子集。
全拼搜索:
1. 搜索“li”,匹配到“刘德华”、“刘斌”、“刘德志”
2. 搜索“liud”,匹配到“刘德华”、“刘德”
3. 搜索“liudeh”,匹配到“刘德华”
小结:搜索的文字转换成拼音后,需要匹配到集合中所有名字转成拼音后的子集
简拼搜索:
1. 搜索“w”,匹配到“我是中国人”,“我爱我的祖国”
2. 搜索“wszg”,匹配到“我是中国人”
小结:搜索的文字取拼音首字母进行组合,需要匹配到组合字符串中前缀匹配的子集
解决方案
方案一:
将 “like” 搜索的字段的 中、英简拼、英全拼 分别用索引的三个字段来进行存储并且 不进行分词,最简单直接,检索索引数据的时候进行通配符查询(like查询),从这三个字段中分别进行搜索,查询匹配的记录然后返回。
优势:存储格式简单,倒排索引存储的数据量最少。
缺点:like 索引数据的时候开销比较大 prefix 查询比 term 查询开销大得多
方案二:
将 中、中简拼、中全拼 用一个字段衍生出三个字段(multi-field)来存储三种数据,并且分词器filter 采用 edge_ngram 类型对分词的数据进行分词处理存储到倒排索引中,当检索索引数据时,检索所有字段的数据。
优势:格式紧凑,检索索引数据的时候采用 term 全匹配规则,也无需对入参进行分词,查询效率高。
缺点:采用以空间换时间的策略,但是对索引来说可以接受。采用衍生字段来存储,增加了存储及检索的复杂度,对于三个字段搜索会将相关度相加,容易混淆查询相关度结果
方案三:
将索引数据存储在一个不需分词的字段中(keyword), 生成倒排索引时进行三种类型倒排索引的生成,倒排索引生成的时候采用 edge_ngram 对倒排进一步拆分,以满足业务场景需求,检索时不对入参进行分词。
优势:索引数据存储简单,检索索引数据的时只需对一个字段采用 term 全匹配查询规则,查询效率极高。
缺点:采用以空间换时间的策略——比方案二要少,对索引数据来说可以接受。
ES 针对这一业务场景解决方案还有很多种,先列出比较典型的这三种方案,选择方案三来进行处理。
准备工作
- pinyin分词插件安装及参数解读
- ElasticSearch edge_ngram 使用
- ElasticSearch multi-field 使用
- ElasticSearch 多种查询特性熟悉
代码
myself_settings.json:
- {
- "refresh_interval":"2s",
- "number_of_replicas":1,
- "number_of_shards":2,
- "analysis":{
- "filter":{
- "autocomplete_filter":{
- "type":"edge_ngram",
- "min_gram":1,
- "max_gram":15
- },
- "pinyin_first_letter_and_full_pinyin_filter" : {
- "type" : "pinyin",
- "keep_first_letter" : true,
- "keep_full_pinyin" : false,
- "keep_joined_full_pinyin": true,
- "keep_none_chinese" : false,
- "keep_original" : false,
- "limit_first_letter_length" : 16,
- "lowercase" : true,
- "trim_whitespace" : true,
- "keep_none_chinese_in_first_letter" : true
- },
- "full_pinyin_filter" : {
- "type" : "pinyin",
- "keep_first_letter" : true,
- "keep_full_pinyin" : false,
- "keep_joined_full_pinyin": true,
- "keep_none_chinese" : false,
- "keep_original" : true,
- "limit_first_letter_length" : 16,
- "lowercase" : true,
- "trim_whitespace" : true,
- "keep_none_chinese_in_first_letter" : true
- }
- },
- "analyzer":{
- "full_prefix_analyzer":{
- "type":"custom",
- "char_filter": [
- "html_strip"
- ],
- "tokenizer":"keyword",
- "filter":[
- "lowercase",
- "full_pinyin_filter",
- "autocomplete_filter"
- ]
- },
- "chinese_analyzer":{
- "type":"custom",
- "char_filter": [
- "html_strip"
- ],
- "tokenizer":"keyword",
- "filter":[
- "lowercase",
- "autocomplete_filter"
- ]
- },
- "pinyin_analyzer":{
- "type":"custom",
- "char_filter": [
- "html_strip"
- ],
- "tokenizer":"keyword",
- "filter":[
- "pinyin_first_letter_and_full_pinyin_filter",
- "autocomplete_filter"
- ]
- }
- }
- }
- }
myself_mapping.json
- {
- "test_type": {
- "properties": {
- "full_name": {
- "type": "text",
- "analyzer": "full_prefix_analyzer"
- },
- "age": {
- "type": "integer"
- }
- }
- }
- }
工程目录:
测试项目代码:
- public class PrefixTest {
- @Test
- public void testCreateIndex() throws Exception{
- TransportClient client = ESConnect.getInstance().getTransportClient();
- //定义索引
- BaseIndex.createWithSetting(client,"baidu_index","esjson/baidu_settings.json");
- //定义类型及字段详细设计
- BaseIndex.createMapping(client,"baidu_index","baidu_type","esjson/baidu_mapping.json");
- }
- @Test
- public void testBulkInsert() throws Exception{
- TransportClient client = ESConnect.getInstance().getTransportClient();
- List<Object> list = new ArrayList<>();
- list.add(new BulkInsert(12l,"我们都有一个家名字叫中国",12));
- list.add(new BulkInsert(13l,"兄弟姐妹都很多景色也不错 ",13));
- list.add(new BulkInsert(14l,"家里盘着两条龙是长江与黄河",14));
- list.add(new BulkInsert(15l,"还有珠穆朗玛峰儿是最高山坡",15));
- list.add(new BulkInsert(16l,"我们都有一个家名字叫中国",16));
- list.add(new BulkInsert(17l,"兄弟姐妹都很多景色也不错",17));
- list.add(new BulkInsert(18l,"看那一条长城万里在云中穿梭",18));
- boolean flag = BulkOperation.batchInsert(client,"baidu_index","baidu_type",list);
- System.out.println(flag);
- }
- }
接下来查看下定义的分词器效果:
- http://192.168.20.114:9200/baidu_index/_analyze?text=刘德华AT2016&analyzer=full_prefix_analyzer
得到的结果内容为:
- {
- "tokens": [
- {
- "token": "刘",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德华",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德华a",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德华at",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德华at2",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德华at20",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德华at201",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "刘德华at2016",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "l",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "li",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "liu",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "liud",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "liude",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "liudeh",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "liudehu",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "liudehua",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "l",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ld",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ldh",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ldha",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ldhat",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ldhat2",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ldhat20",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ldhat201",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- },
- {
- "token": "ldhat2016",
- "start_offset": 0,
- "end_offset": 9,
- "type": "word",
- "position": 0
- }
- ]
- }
看到以上结果,则表明大功告成了!
ElasticStack系列之十三 & 联想补全策略的更多相关文章
- 【Qt编程】基于Qt的词典开发系列<十四>自动补全功能
最近写了一个查单词的类似有道词典的软件,里面就有一个自动补全功能(即当你输入一个字母时,就会出现几个候选项).这个自动补全功能十分常见,百度搜索关键词时就会出现.不过它们这些补全功能都是与你输入的进行 ...
- 9. VIM 系列 - YouCompleteMe 实现代码补全
目录 环境准备 插件安装 环境准备 $ sudo apt install build-essential cmake python3-dev python-dev $ sudo apt install ...
- TensorFlow系列专题(十三): CNN最全原理剖析(续)
目录: 前言 卷积层(余下部分) 卷积的基本结构 卷积层 什么是卷积 滑动步长和零填充 池化层 卷积神经网络的基本结构 总结 参考文献 一.前言 上一篇我们一直说到了CNN[1]卷积层的特性,今天 ...
- 联想功能 Jquery autocomplete.js输入框联想补全功能
转载地址:https://www.cnblogs.com/jinzhiming/p/6768402.html
- Jquery autocomplete.js输入框联想补全功能
Jquery autocomplete.js插件下载地址:http://files.cnblogs.com/files/jinzhiming/autocomplete.rar 有两种用法,一种是直接使 ...
- ES系列十三、Elasticsearch Suggester API(自动补全)
1.概念 1.补全api主要分为四类 Term Suggester(纠错补全,输入错误的情况下补全正确的单词) Phrase Suggester(自动补全短语,输入一个单词补全整个短语) Comple ...
- [jetbrains系列] 外链第三方库+代码补全设置
jetbrains系列的IDE真的是太好用了,有种相见恨晚的感觉. 在开发过程中第三方库是必不可少的,在开发的时候如果有一个可以补全的IDE可以节省查文档的时间. 举个例子:给pycharm配pysp ...
- IDEA 代码自动补全/自动联想 功能
IDEA 的代码补全/自动联想功能,可以仅仅输入几个字母,自动补全一整段代码,非常舒服. 代码自动联想功能在 设置 -> Editor -> Live Templates 查看,很多都非常 ...
- ajax04_实现关键字联想和自动补全
用ajax实现关键字联想和自动补全 遇到的小坑 回调函数相对window.onload的摆放位置 给回调函数addData传数据时,如何操作才能将数据传进去 代码实现 前端代码 <!DOCTYP ...
随机推荐
- Javascript toString()、toLocaleString()、valueOf()三个方法的区别
Array.Boolean.Date.Number等对象都具有toString().toLocaleString().valueOf()三个方法,那这三个方法有什么区别??? 一.JS Array 例 ...
- 今日事——Sprint计划会议
一. Sprint需求: 解屏提醒部分 界面设计 登录功能 备忘功能 成就系统 二.工作认领: 因有成员请假回家,所以延后认领,目前主要任务是学习如何在andriod平台开发并搭建开发环境. 网上 ...
- 《Spring1之第六次站立会议》
<第六次站立会议> 昨天:向小伙伴们请教了我代码的错误的解决方法以及对TCP/IP协议进行了相关的了解. 今天:我把自己项目工程里的服务器端界面进行了优化和完善. 遇到的问题:觉得做的界面 ...
- “吃神么,买神么”的第一个Sprint计划(第二天)
“吃神么,买神么”项目Sprint计划 ——5.22(第二天)立会内容与进度 团队组员各自任务: 冯美欣:logo的设计.搜索框的制作,"登陆/注册"的文字链接: 吴舒婷:导航条. ...
- 10_Java面向对象_第10天(继承、抽象类)_讲义
今日内容介绍 1.继承 2.抽象类 3.综合案例---员工类系列定义 01继承的概述 *A:继承的概念 *a:继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系 *b:在Jav ...
- 第一个spring冲刺心得及感想
在这次spring中,学到了不少东西: 1.团队协作精神 2.任务细节化,任务燃尽图 3.身为sm的责任 但是在过程中也认识到了一些不足 1.对于团队协作完成一个大的项目还是不熟悉 2.个人能力的不足 ...
- 2018最新Web前端经典面试试题及答案
javascript: JavaScript中如何检测一个变量是一个String类型?请写出函数实现 typeof(obj) === "string" typeof obj === ...
- 计算机网络-将C网192.168.25.0划分四个子网,计算每个子网的有效IP地址范围和对应网络掩码
首先计算子网掩码: 将256/4=64,主机块大小64 2的6次方=64 根据主机数量计算出掩码的最后一个字节为11000000,用十进制表示掩码为255.255.255.192 由于IP地址结尾全为 ...
- 用ul li实现边框重合并附带鼠标经过效果
边框重合这个效果并不难,只是我们没有真正的动手做过而已,下面让我们来谈谈用ul li如何实现边框重合,并附带鼠标经过效果 <!DOCTYPE html> <html lang=&qu ...
- java 每一个对象都是根据hashCode区别的 每次返回不同的内存地址
可以通过hashCode比较对象,hashCode如果重写的话 返回的内存地址是一样的 则不能创建对象