ToB企服应用市场:ToB评测及商务社交产业平台
标题:
ElasticSearch-查询
[打印本页]
作者:
尚未崩坏
时间:
2022-8-22 03:10
标题:
ElasticSearch-查询
前言
前面
我已经搭建好了ElasticSearch服务,并完成了MySQL到ElasticSearch的数据迁移;
使用ElasticSearch的初衷就是为了大数据搜索,本文将介绍ElaticSearch中各种查询方法;
一、精确查询(termQuery)
termQuery不会对查询条件进行分词 ,但这并不以为着查询的字段没有进行分词存储;
1.Kibana查询
term精确查询并不会对查询条件进行分词,类似于MySQL中 select * from table where 字段='xx值';
GET hotel/_search
{
"query": {
"term": {
"brand": {
"value": "万豪"
}
}
},
"from": 0,
"size": 20
}
复制代码
2.JavaAPI查询
将以上在Kibana输入的DSM转换成Java代码;
//按照品牌精确查询
@Override
public Map<String, Object> brandTermQuery(int current, int size, Map<String, Object> searchParam) {
//按品牌精确查询实现
//1.获取前端参数
String brand = (String) searchParam.get("brand");
//响应前端的Map
Map<String, Object> resultMap = new HashMap<>();
//2.构建查询条件
//查询请求
SearchRequest hotelSearchRequest = new SearchRequest("hotel");
//请求体
SearchSourceBuilder hotelSearchSourceBuilder = new SearchSourceBuilder();
//如果查询条件为空就查询所有
if (StringUtils.hasText(brand)) {
//请求体-查询部分
TermQueryBuilder hotelTermQueryBuilder = QueryBuilders.termQuery("brand", brand);
hotelSearchSourceBuilder.query(hotelTermQueryBuilder);
}
//请求体-分页部分
hotelSearchSourceBuilder.from((current - 1) * size);
hotelSearchSourceBuilder.size(size);
//查询请求-封装请求体
hotelSearchRequest.source(hotelSearchSourceBuilder);
//3.去查询
try {
SearchResponse hotelSearchResponse = restHighLevelClient.search(hotelSearchRequest, RequestOptions.DEFAULT);
//4.处理查询结果集
SearchHits hotelSearchResponseHits = hotelSearchResponse.getHits();
//获取命中总条目
Long totalHotelHits = hotelSearchResponseHits.getTotalHits().value;
//获取命中的每1个条
SearchHit[] hoteHits = hotelSearchResponseHits.getHits();
//前端
ArrayList<HotelEntity> hotelEntitieList = new ArrayList<>();
if (hoteHits != null || hoteHits.length > 0) {
for (SearchHit hoteHit : hoteHits) {
String sourceAsString = hoteHit.getSourceAsString();
//字符串转换成Java对象
HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class);
hotelEntitieList.add(hotelEntity);
}
}
//前端展示
resultMap.put("list", hotelEntitieList);
resultMap.put("totalResultSize", totalHotelHits);
//设置分页相关
resultMap.put("current", current);
resultMap.put("totalPage", (totalHotelHits + size - 1) / size);
} catch (IOException e) {
throw new RuntimeException(e);
}
return resultMap;
}
复制代码
HotelServiceImpl.java
二、中文分词器
如果设置了字段的type为keyword,就可以对该字段使用term精确查询;
如果设置了字段的type为text,当添加文档时,对于该域的值会进行分词,形成若干term(词条)存储在倒排索引中。
当用户进行term查询时,ES会将当前查询条件当做1个term(词条),和当前倒排索引中term(词条)进行匹配?
匹配成功则会查询到数据,如果倒排索引中不存在该term(词条)则查询不到数据。
那我们如何对text类型的字段进行term查询呢?
这就需要利用中文分词器对文档中的内容进行中文分词, 重构ES的倒排索引的结构,把整个文档分词成为若干中文term(词条)
1.ElasticSearch内置分词器
在ElasticSearch默认内置了多种分词器:
Standard Analyzer
- 默认分词器,按英文空格切分
Simple Analyzer - 按照非字母切分(符号被过滤)
Stop Analyzer - 小写处理,停用词过滤(the,a,is)
Whitespace Analyzer - 按照空格切分,不转小写
Keyword Analyzer - 不分词,直接将输入当作输出
Patter Analyzer - 正则表达式,默认\W+(非字符分割)
2.默认分词无法对中文分词
看看ES是默认使用Standard Analyzer分词器对文档内容进行分词;
GET _analyze
{<br>"text": "北京市东城区万豪酒店"
}
复制代码
此时可以发现Standard Analyzer分词器把每1个汉字形成了一个词,这显然无法满足汉语搜索习惯;
3.安装IK分词器
IK分词器是国人开发的1款智能中文分词器,基于Java语言开发、具有60万字/秒的高速处理能力。
并且用户可以按需求,设置停用词与扩展词。
3.1.上传IK安装包
#因为启动es时候 已经做好的目录挂载
容器内部:/usr/share/elasticsearch/plugins
宿主机:/mydata/elasticsearch/plugins
所以只需要将文件复制到/mydata/elasticsearch/plugins 目录下即可
复制代码
3.2.重启容器
docker restart elasticsearch
复制代码
3.3.测试
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "北京市东城区万豪酒店"
}
复制代码
4.使用IK分词器
4.1.分词模式
IK分词器有两种分词模式it_max_word和ik_smart模式
ik_max_word:细粒度分词,存储的时候分词可以细粒度一些;
ik_smart:粗粒度分词,搜索的时候可以对搜索条件分词粗粒度一些;
4.2.配置自定义词库
IK分词器虽然非常智能。但是中华语言博大精深,其并不能完全识别所有的词。
如在文档存储时,无法对专业术语、店名、网络词语等等。所以IK提供了扩展词库,用户可以按需求添加自定义分词数据。
4.2.1.定义扩展词
4.2.2.定义停用词
4.3.修改IK分词器的配置文件
vim 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">my.dic</entry>
<entry key="ext_stopwords">extra_stopword.dic</entry>
<entry key="remote_ext_dict">http://106.75.109.43:28888/remote.dic</entry>
</properties>
复制代码
4.4.重建索引指定分词器
把数据从原索引(表)中迁移到目标索引(表);
如果是线上环境,在重建索引时,一定要选择异步构建和平滑构建;
PUT hotel_2
{
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"address":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"brand":{
"type": "keyword"
},
"type":{
"type": "keyword"
},
"price":{
"type": "integer"
},
"specs":{
"type": "keyword"
},
"salesVolume":{
"type": "integer"
},
"area":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"imageUrl":{
"type": "text"
},
"synopsis":{
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"createTime":{
"type": "date",
"format": "yyyy-MM-dd"
},
"isAd":{
"type":"integer"
}
}
}
}
#重建索引 异步构建和平滑构建
POST _reindex?wait_for_completion=false&requests_per_second=2000
{
"source": {
"index": "原始索引名字"
},
"dest": {
"index": "目标索引名字"
}
}
#查看任务完成情况
GET _tasks/任务id
#重建别名关联关系
#断开原来的关系
POST _aliases
{
"actions": [
{
"remove": {
"index": "hotel_1",
"alias": "hotel"
}
}
]
}
#删除原来的索引表
DELETE hotel_1
#新建hotel_2的关系
POST _aliases
{
"actions": [
{
"add": {
"index": "hotel_2",
"alias": "hotel"
}
}
]
}
复制代码
dms
4.5.测试文档是否被分词
此时文档在存储时已经被中文分词器进行了中文分词并存储,我们就可以使用termQuery精确查询进行分词结果测试了;
由于termQuery精确查询,不会对查询条件进行分词,所依我根据分词结果进行查询,如果分词成功,就会查询到text字段的结果;
三、分词查询(mathQuery)
上述的term精确查询必须要根据分词之后的结果进行精确查询;
可是用户不知道你的文档是怎么分词的,所以我们需要对用户的查询条件也进行分词;
1.Kibana分词查询
GET hotel/_search
{
"query": {
"match": {
"name":"北京市东城区瑞麟湾"
}
}
}
复制代码
matchQuery会对查询条件进行分词,并拿分词后的结果,去ES中进行逐一匹配,默认取结果并集。
2.JavaAPI分词查询
//根据酒店名称匹配查询
@Override
public Map<String, Object> nameMatchQuery(Integer current, Integer size, Map<String, Object> searchParam) {
//设置查询
SearchRequest searchRequest = new SearchRequest("hotel");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
Map<String, Object> map = new HashMap<>();
//获取name参数
String name = (String) searchParam.get("name");
if (StringUtils.hasText(name)) {
//组装查询对象
MatchQueryBuilder nameMatchQueryBuilder = QueryBuilders.matchQuery("name", name);
searchSourceBuilder.query(nameMatchQueryBuilder);
}
//设置分页
searchSourceBuilder.from((current - 1) * size);
searchSourceBuilder.size(size);
searchRequest.source(searchSourceBuilder);
//处理查询结果
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits().value;
SearchHit[] searchHits = hits.getHits();
List<HotelEntity> list = new ArrayList<>();
for (SearchHit searchHit : searchHits) {
String sourceAsString = searchHit.getSourceAsString();
list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
}
map.put("list", list);
map.put("totalResultSize", totalHits);
map.put("current", current);
//设置总页数
map.put("totalPage", (totalHits + size - 1) / size);
} catch (IOException e) {
throw new RuntimeException(e);
}
return map;
}
复制代码
HotelServiceImpl.java
四、模糊搜索(wildcardQuery)
当在搜索框进行搜索时,展示出所有品牌以美开头的酒店。
wildcardQuery:会对查询条件进行分词,还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)
1.Kibana分词查询
GET hotel/_search
{
"query": {
"wildcard": {
"brand": {
"value": "美*"
}
}
}
}
复制代码
2.JavaAPI分词查询
//根据酒店品牌模糊查询
@Override
public Map<String, Object> nameWildcardQuery(Integer current, Integer size, Map<String, Object> searchParam) {
//设置查询
SearchRequest searchRequest = new SearchRequest("hotel");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//根据酒店名称模糊查询
//1.获取前端参数
String name = (String) searchParam.get("name");
//2.组装查询对象
if (StringUtils.hasText(name)) {
WildcardQueryBuilder brandWildcardQuery = QueryBuilders.wildcardQuery("brand", name+"*");
searchSourceBuilder.query(brandWildcardQuery);
}
//设置分页
searchSourceBuilder.from((current - 1) * size);
searchSourceBuilder.size(size);
searchRequest.source(searchSourceBuilder);
Map<String, Object> map = new HashMap<>();
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits().value;
SearchHit[] searchHits = hits.getHits();
List<HotelEntity> list = new ArrayList<>();
for (SearchHit searchHit : searchHits) {
String sourceAsString = searchHit.getSourceAsString();
list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
}
map.put("list", list);
map.put("totalResultSize", totalHits);
map.put("current", current);
//设置总页数
map.put("totalPage", (totalHits + size - 1) / size);
} catch (IOException e) {
throw new RuntimeException(e);
}
return map;
}
复制代码
HotelServiceImpl.java
五、多域(字段)查询
当用户在搜索框输入查询条件时,为了给用户展示更多的数据,该条件不应该仅仅作用于某1个域(字段),而要让其作用于多个域进行搜索,从而搜索出更多的查询结果。
类似于MySQL数据中的 select * from table 字段1=条件 or 字段2=条件.....;
简而言之就是使用1个条件去多个字段中查询;
当前需要将用户的搜索条件,作用于:酒店名称(name)、酒店描述(synopsis)、酒店地区(area)、酒店地址(address);
1.Kibana查询
GET hotel/_search
{
"query": {
"query_string": {
"fields": ["name","brand","address","synopsis"],
"query": "万豪 OR 北京 OR 上海"
}
}
}
复制代码
2.JavaAPI查询
//根据name,synopsis,area,address进行多域(字段)查询
@Override
public Map<String, Object> searchQueryStringQuery(Integer current, Integer size, Map<String, Object> searchParam) {
//设置查询
SearchRequest searchRequest = new SearchRequest("hotel");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
Map<String, Object> map = new HashMap<>();
//根据name,synopsis,area,address进行多域查询
String condition = (String) searchParam.get("condition");
//组装查询对象
if (StringUtils.hasText(condition)) {
QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(condition)
.field("name")
.field("address")
.field("synopsis")
.field("area")
.defaultOperator(Operator.OR);
searchSourceBuilder.query(queryStringQueryBuilder);
}
//设置分页
searchSourceBuilder.from((current - 1) * size);
searchSourceBuilder.size(size);
searchRequest.source(searchSourceBuilder);
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
long totalHits = hits.getTotalHits().value;
SearchHit[] searchHits = hits.getHits();
List<HotelEntity> list = new ArrayList<>();
for (SearchHit searchHit : searchHits) {
String sourceAsString = searchHit.getSourceAsString();
list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
}
map.put("list", list);
map.put("totalResultSize", totalHits);
map.put("current", current);
//设置总页数
map.put("totalPage", (totalHits + size - 1) / size);
} catch (IOException e) {
throw new RuntimeException(e);
}
return map;
}
复制代码
HotelServiceImpl
六、排序查询(sort)
当用户进行搜索时,有时会关注该商品的销量、评论数等信息,对这些进行进行排序,搜索出销量最高或评论数最多的商品。
1.Kibana查询
#排序查询:支持多字段排序
GET hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": {
"order": "desc"
}
},
{
"salesVolume": {
"order": "asc"
}
}
]
}
复制代码
2.JavaAPI查询
七、范围查询(range)
八、多条件查询
九、纠错查询(fuzzy)
十、高亮展示搜索结果
当用户在搜索框输入搜索条件后,对于查询结果的展示,应将搜索条件以特殊的样式展示,这种查询就称为高亮结果查询;
参考
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4