分布式搜索引擎Elasticsearch(三):分词器与进阶知识

一.Docker安装Elasticsearch

  • 非Docker安装请参考我的博客:https://blog.csdn.net/weixin_43934607/article/details/100538881

安装单节点Elasticsearch

  • 拉取镜像

    docker pull elasticsearch:6.5.4 
    
  • 创建容器

    docker create --name elasticsearch --net host -e “discovery.type=single-node” -e “network.host=192.168.56.132” elasticsearch:6.5.4

  • 启动

    docker start elasticsearch 
    
  • 查看日志

    docker logs elasticsearch
    

二.ik分词器

1.下载

  • 下载6.5.4 版本ik分词器 并上传到虚拟机

    链接:https://pan.baidu.com/s/1tppoVxIYzOGya9AlLx-Jxw
    提取码:5uax
    复制这段内容后打开百度网盘手机App,操作更方便哦

2.安装方式一

  • 复制分词器压缩包到elastcisearch容器内部
    docker cp elasticsearch-analysis-ik-6.5.4.zip elasticsearch:/usr/share/elasticsearch/plugins/
    
  • 进入容器
    docker exec -it elasticsearch /bin/bash 
    
  • 在elasticsearch/plugins/下创建ik文件夹 并把ik分词器复制到该目录然后解压
    #创建
    mkdir /usr/share/elasticsearch/plugins/ik 
    #复制
    cp /usr/share/elasticsearch/plugins/elasticsearch-analysis-ik-6.5.4.zip /usr/share/elasticsearch/plugins/ik
    #解压
    unzip /usr/share/elasticsearch/plugins/ik/elasticsearch-analysis-ik-6.5.4.zip
    
  • 重启容器即可
    docker restart elasticsearch
    

3.安装方式二:挂载(推荐)

  • 将IK的zip压缩包解压到 /data/es-cluster/ik
    mkdir /data/es-cluster/ik
    cd /data/es-cluster/ik 
    unzip elasticsearch-analysis-ik-6.5.4.zip
    
  • 重新创建容器
    docker create --name es --net host \
    -v /data/es-cluster/ik:/usr/share/elasticsearch/plugins/ik \
    elasticsearch:6.5.4
    

4.增加ik分词器内容

  • 进入:{plugins}/elasticsearch-analysis-ik-*/config/

  • 新建一个自定的词典

  1. 新建文件
    vi my.dic
    
  2. 增加自定的词汇
    杨某某
    爱中国
    
  • 编辑:IKAnalyzer.cfg.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
    <properties>
    	<comment>IK Analyzer 扩展配置</comment>
    	<!--用户可以在这里配置自己的扩展字典 -->
    	<entry key="ext_dict">custom/mydict.dic;custom/single_word_low_freq.dic</entry>
    
    	 <!--用户可以在这里配置自己的扩展停止词字典-->
    	<entry key="ext_stopwords">custom/ext_stopword.dic</entry>
    
     	<!--用户可以在这里配置远程扩展字典 -->
     	<!--修改这里:把location改为自定词典名-->
    	<entry key="remote_ext_dict">my.dic</entry>
    
     	<!--用户可以在这里配置远程扩展停止词字典-->
    	<entry key="remote_ext_stopwords">http://xxx.com/xxx.dic</entry>
    </properties>
    
  • 重启容器 查看分词结果

    GET /_analyze

    {
    	"analyzer":ik_max_word
    	"text":"杨某某爱中国"
    }
    

三.拼音分词器

1.下载6.5.4版本拼音拼音分词器

  • 插件源码地址:https://github.com/medcl/elasticsearch-analysis-pinyin

链接:https://pan.baidu.com/s/1qAFkRWPpRCGr2UFMyOasqQ
提取码:uya6
复制这段内容后打开百度网盘手机App,操作更方便哦

2.重新创建容器

#将zip压缩包,解压到/data/es-cluster/pinyin 
unzip elasticsearch-analysis-pinyin-6.5.4.zip 

#重新创建容器 
docker stop es
docker rm es 

docker create --name es
-v /data/es-cluster/ik:/usr/share/elasticsearch/plugins/ik 
-v /data/es- cluster/pinyin:/usr/share/elasticsearch/plugins/pinyin 
elasticsearch:6.5.4

3.创建映射使用拼音分词器

PUT /test(索引库名)/ 
{ 
	"index" : { 
		"analysis" : { 
			"analyzer" : { 
				"pinyin_analyzer" : { 
					"tokenizer" : "my_pinyin" 
				} 
			},
			"tokenizer" : { 
				"my_pinyin" : { 
					"type" : "pinyin", 
					"keep_separate_first_letter" : false, 
					"keep_full_pinyin" : true, 
					"keep_original" : true, 
					"limit_first_letter_length" : 16, 
					"lowercase" : true, 
					"remove_duplicated_term" : true 
				} 
			} 
		}
	}
	"mappings": { 
		"person": { 
			"dynamic": false,
			"properties": { 
				"name": { 
					"type": "text", 
					"analyzer":"ik_max_word", 
					"fields":{ 
						"pinyin":{ 
							"type": "text", 
							"analyzer": "pinyin_analyzer" 
						} 
					} 
				},
				"image": { 
					"type": "keyword", 
					"index":false 
				}
			}
		}
	}
}
  • 拼音分词器字段说明:
    • 这里拼音分词器是定义的 name 的子字段pinyin,通过属性fields指定。
  • my_pinyin中的参数说明:
    • keep_first_letter:启用此选项时,例如:刘德华> ldh,默认值:true
    • keep_separate_first_letter:启用该选项时,将保留第一个字母分开,例如:刘德华> l,d,h,默认:假的,注意:查询结果也许是太模糊,由于长期过频
    • keep_full_pinyin:当启用该选项,例如:刘德华> [ liu,de,hua],默认值:true
    • keep_original:当启用此选项时,也会保留原始输入,默认值:false
    • limit_first_letter_length:设置first_letter结果的最大长度,默认值:16
    • lowercase:小写非中文字母,默认值:true
    • remove_duplicated_term:当启用此选项时,将删除重复项以保存索引,例如:de的> de,默认值:false,注意:位置相关查询可能受影响

4.测试拼音分词器

GET /test/_analyze 
{ 
	"text": ["刘德华"], 
	"analyzer": "pinyin_analyzer" 
}

#测试分词后的结果:
{ 
	"tokens": [ 
		{ 
			"token": "刘德华", 
			"start_offset": 0, 
			"end_offset": 0, 
			"type": 	"word", 
			"position": 0 
		},
		{ 
			"token": "ldh", 
			"start_offset": 0, 
			"end_offset": 0, 
			"type": "word", 
			"position": 0 
		},
		{ 
			"token": "liu", 
			"start_offset": 0, 
			"end_offset": 0, 
			"type": "word", 
			"position": 0 
		},
		{ 
			"token": "de",
			"start_offset": 0, 
			"end_offset": 0, 
			"type": "word", 
			"position": 1 
		},
		{ 
			"token": 
			"hua", 
			"start_offset": 0, 
			"end_offset": 0, 
			"type": "word", 
			"position": 2 
		} 
	] 
}
  • 分词后的这些结果都可会去做匹配查询 有命中的话都会被查到

5.查询

  • 插入测试数据
    POST /test/person/
    {
    	"name":"刘德华"
    }
    
  • 使用拼音查询
    GET /test/_search
    {
    	"query":{
    		"match":{
    			"name.pinyin":{
    				"query":"ldh"
    			}
    		}
    	}
    }
    
  • 返回结果
    在这里插入图片描述

5.SpringBoot怎么使用拼音分词器

SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(
	/**
	 * 此处的有match 改为 multiMatchQuery
	 */
	QueryBuilders.multiMatchQuery(keyWord, "title", "title.pinyin").operator(Operator.AND)) // match查询 
	.withPageable(pageRequest) 
	/**
	 * 设置高亮 此处必须把子字段pinyin 也带上 不然会出现有的时候不会高亮显示
	 * /
	.withHighlightFields(new HighlightBuilder.Field("title"),new HighlightBuilder.Field("title.pinyin")); 
	.build();

四.使用反射实现高亮显示

@Service 
public class SearchService { 

	@Autowired 
	private ElasticsearchTemplate elasticsearchTemplate; 

	public static final Integer ROWS = 10; 

	public SearchResult search(String keyWord, Integer page) { 
		PageRequest pageRequest = PageRequest.of(page - 1, ROWS); 
		//设置分页参数 
		SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(
		QueryBuilders.matchQuery("title", keyWord).operator(Operator.AND)) // match查询 
		.withPageable(pageRequest) 
		.withHighlightFields(new HighlightBuilder.Field("title")) // 设置高亮 
		.build(); 
	
		AggregatedPage<HouseData> housePage =this.elasticsearchTemplate.queryForPage(searchQuery, HouseData.class,
			 new SearchResultMapper() { 
				@Override 
				public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { 
					List<T> result = new ArrayList<>(); 
					if (response.getHits().totalHits == 0) { 
						return new AggregatedPageImpl<> (Collections.emptyList(), pageable, 0L); 
					}
					for (SearchHit searchHit : response.getHits()) { 
						/**
						 * 通过反射写入数据到对象中 
						 * 因为es中的映射类型和实体类类型是一致的 所以可以直接使用反射
						 * /
						T obj = (T) ReflectUtils.newInstance(clazz); 
						try {
							//写入id ,参数 true 表示是否可以读取私有属性
							FieldUtils.writeField(obj, "id", searchHit.getId(), true); 
						} catch (IllegalAccessException e) { 
								e.printStackTrace(); 
						}
		
						Map<String, Object> sourceAsMap = searchHit.getSourceAsMap(); 
						for (Map.Entry<String, Object> entry : sourceAsMap.entrySet()) { 
							Field field = FieldUtils.getField(clazz, entry.getKey(), true); 
							if (null == field) { 
								continue; 
							}	
							try {
								FieldUtils.writeField(obj, entry.getKey(), entry.getValue(), true); 
							} catch (IllegalAccessException e) { 
								e.printStackTrace(); 
							} 
						}
						// 处理高亮 
						for (Map.Entry<String, HighlightField> stringHighlightFieldEntry : searchHit.getHighlightFields().entrySet()) { 
							try {
								Text[] fragments = stringHighlightFieldEntry.getValue().fragments(); 
								StringBuilder sb = new StringBuilder(); 
								for (Text fragment : fragments) { 
									sb.append(fragment.toString()); 
								}
							
								FieldUtils.writeField(obj, stringHighlightFieldEntry.getKey(), sb.toString(), true); 
							} catch (IllegalAccessException e) { 
								e.printStackTrace(); 
							} 
						}
					
						result.add(obj); 
					}
					return new AggregatedPageImpl<>(result, pageable, response.getHits().totalHits); 
				} 
		}); 

		return new SearchResult(housePage.getTotalPages(), housePage.getContent()); 
	} 
}

五.批量操作_bulk

在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。

  • 请求格式如下:(请求格式不同寻常)
    注意最后一行的回车。

    { action: { metadata }}\n
    { request body }\n
    { action: { metadata }}\n
    { request body }\n
    ...
    
  • 批量插入数据:
    注意最后一行的回车。

    {"create":{"_index":"haoke","_type":"user","_id":2001}}
    {"id":2001,"name":"name1","age": 20,"sex": "男"}
    {"create":{"_index":"haoke","_type":"user","_id":2002}}
    {"id":2002,"name":"name2","age": 20,"sex": "男"}
    {"create":{"_index":"haoke","_type":"user","_id":2003}}
    {"id":2003,"name":"name3","age": 20,"sex": "男"}
    
  • 批量删除:
    由于delete没有请求体,所以,action的下一行直接就是下一个action。

    {"delete":{"_index":"haoke","_type":"user","_id":2001}}
    {"delete":{"_index":"haoke","_type":"user","_id":2002}}
    {"delete":{"_index":"haoke","_type":"user","_id":2003}}
    
  • 其他操作就类似了。

一次请求多少性能最高?

  • 整个批量请求需要被加载到接受我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一个最佳的bulk请求大小。超过这个大小,性能不再提升而且可能降低。
  • 最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负载。
  • 幸运的是,这个最佳点(sweetspot)还是容易找到的:试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以使用较小的批次。
  • 通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的批次最好保持在5-15MB大小间。

六.集群系统中深度分页

和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数

  • size: 结果数,默认10
  • from: 跳过开始的结果数,默认0

如果你想每页显示5个结果,页码从1到3,那请求如下

GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
  • 应该当心分页太深或者一次请求太多的结果。结果在返回前会被排序。但是记住一个搜索请求常常涉及多个分片。每个分片生成自己排好序的结果,它们接着需要集中起来排序以确保整体排序正确。

为了理解为什么深度分页是有问题的,让我们假设在一个有5个主分片的索引中搜索。

  • 当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回它们给请求节点(requesting node),它再排序这所有的50个结果以选出顶端的10个结果。
  • 现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值