前言
前面我已经搭建好了ElasticSearch服务,并完成了MySQL到ElasticSearch的数据迁移;
使用ES专门做搜索功能,打破MySQL搜索瓶颈;
ElasticSearch的应用场景
- 数据库字段太多,查询太慢,索引没有办法再做优化;
- 数据库1个count查询就拖死全表;
- MySQL的limit翻到几十几百万页后实在是太慢;
- 数据库like实在太慢,每次like整个服务器cpu内存飙高,拖慢整个线上服务;
- 想要对外/内提供db里的数据的全文检索服务;
- 提供日志(程序运行)查询功能;
本文将使用ElasticSearch的倒排索引取代MySQL的索引,进行大数据查询,提升查询效率;
一、精确查询(termQuery)
term精确查询并不会对查询条件进行分词,类似于MySQL中 select * from table where 字段='xx值';
GET hotel/_search
{
"query": {
"term": {
"brand": {
"value": "万豪"
}
}
},
"from": 0,
"size": 20
}
//按照品牌精确查询 @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; }
二、中文分词器
如果设置了字段的type为keyword,就可以对该字段使用term精确查询;
如果设置了字段的type为text,
当用户进行term查询时,ES会将当前查询条件当做1个term(词条),和当前倒排索引中term(词条)进行匹配?
匹配成功则会查询到数据,如果倒排索引中不存在该term(词条)则查询不到数据。
那我们如何对text类型的字段进行term查询呢?
这就需要利用中文分词器对文档中的内容进行中文分词, 重构ES的倒排索引的结构,把整个文档分词成为若干中文term(词条)
1.ElasticSearch内置分词器
在ElasticSearch默认内置了多种分词器:
- Simple Analyzer - 按照非字母切分(符号被过滤)
- Whitespace Analyzer - 按照空格切分,不转小写
- Keyword Analyzer - 不分词,直接将输入当作输出
2.默认分词无法对中文分词
看看ES是默认使用Standard Analyzer分词器对文档内容进行分词;
GET _analyze
{
"text": "北京市东城区万豪酒店"
}
3.
#因为启动es时候 已经做好的目录挂载 容器内部:/usr/share/elasticsearch/plugins 宿主机:/mydata/elasticsearch/plugins 所以只需要将文件复制到/mydata/elasticsearch/plugins 目录下即可
docker restart elasticsearch
3.3.测试
GET /_analyze { "analyzer": "ik_max_word", "text": "北京市东城区万豪酒店" }
IK分词器有两种分词模式it_max_word
和ik_smart
模式
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> <!--用户可以在这里配置远程扩展停止词字典--> <!-- <entry key="remote_ext_stopwords">http://ip地址:端口号/词典文件</entry> --> </properties>
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" } } ] }
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; }
四、
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; }
query_string
GET hotel/_search
{
"query": {
"query_string": {
"fields": ["name","brand","address","synopsis"],
"query": "万豪 OR 北京 OR 上海"
}
}
}
//根据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; }
当用户进行搜索时,有时会关注该商品的销量、评论数等信息,对某些域(字段)进行进行排序,搜索出销量最高或评论数最多的商品。
使用match_all
price和salesVolume域(字段)进行排序;#排序查询:支持多字段排序 GET hotel/_search { "query": { "match_all": {} }, "sort": [ { "price": { "order": "desc" } }, { "salesVolume": { "order": "asc" } } ] }
//根据销量排序查询 @Override public Map<String, Object> salesSortQuery(Integer current, Integer size, Map<String, Object> searchParam) { //设置查询 SearchRequest searchRequest = new SearchRequest("hotel"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //设置按销量排序 String sortWay = (String) searchParam.get("sortWay"); //组装sort部分 if (StringUtils.hasText(sortWay)) { if ("asc".equals(sortWay)) { searchSourceBuilder.sort("price", SortOrder.ASC).sort("salesVolume", SortOrder.ASC); } else { searchSourceBuilder.sort("price", SortOrder.DESC).sort("salesVolume", SortOrder.DESC); } } //设置分页 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); map.put("sortWay", searchParam.get("sortWay")); } catch (IOException e) { throw new RuntimeException(e); } return map; }
七、范围查询(range)
当用户要搜索商品时,有时会对某一个特定的价格区间进行查询,搜索出符合心理预期价格的商品;
区间范围查询:查询价格在100-500元范围之间的商品;
GET hotel/_search
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 500
}
}
}
}
//处理前端参数 public Long transferToLong(Object param){ if(param==null || "".equals(param)){ return null; }else{ return Long.parseLong((String)param); } } //根据价格范围查询 @Override public Map<String, Object> priceRangeQuery(Integer current, Integer size, Map<String, Object> searchParam) { //设置查询 SearchRequest searchRequest = new SearchRequest("hotel"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //根据价格范围查询 //1.获取参数 Long maxPrice = transferToLong(searchParam.get("maxPrice")); Long minPrice = transferToLong(searchParam.get("minPrice")); //2.组装搜索条件 if (maxPrice != null || maxPrice != null) { RangeQueryBuilder priceRangeQueryBuilder = QueryBuilders.rangeQuery("price"); if (maxPrice != null) { priceRangeQueryBuilder.lte(maxPrice); } if (minPrice != null) { priceRangeQueryBuilder.gte(maxPrice); } } //设置分页 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<String, Object> map = new HashMap<>(); map.put("list", list); map.put("totalResultSize", totalHits); map.put("current", current); //设置总页数 map.put("totalPage", (totalHits + size - 1) / size); map.put("minPrice", searchParam.get("minPrice")); map.put("maxPrice", searchParam.get("maxPrice")); return map; } catch (IOException e) { e.printStackTrace(); } return null; }
select * from table where 字段1='条件1' or 字段2='条件2' ;
连接方式有
- must(and): 条件必须成立
- must_not(not): 条件必须不成立
- should(or): 1个条件可以成立即可
- filter
查询北京市的万豪价格区间在500-2000,最好是五星级;
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"brand": {
"value": "万豪"
}
}
},
{
"term": {
"area": {
"value": "北京市"
}
}
}
],
"should": [
{
"term": {
"specs": {
"value": "五星级"
}
}
}
],
"filter": [
{
"range": {
"price": {
"gte": 500,
"lte": 2000
}
}
}
]
}
}
}
//多条件查询 //搜索框多域、品牌精确、城市精确、星级精确、价格范围、销量排序 @Override public Map<String, Object> searchBoolQuery(Integer current, Integer size, Map<String, Object> searchParam) { //设置查询 SearchRequest searchRequest = new SearchRequest("hotel"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //todo 多条件查询 :多域、品牌精确、城市精确、星级精确、价格范围、销量排序 //设置查询方式 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); //多域 if (StringUtils.hasText(searchParam.get("condition"))) { QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString()) .field("name") .field("synopsis") .field("area") .field("address") .defaultOperator(Operator.OR); boolQueryBuilder.must(queryBuilder); } //品牌精确 if (StringUtils.hasText(searchParam.get("brand"))) { TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brand", searchParam.get("brand")); boolQueryBuilder.filter(termQueryBuilder); } //城市精确 if (StringUtils.hasText(searchParam.get("area"))) { TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("area", searchParam.get("area")); boolQueryBuilder.filter(termQueryBuilder); } //星级精确 if (StringUtils.hasText(searchParam.get("specs"))) { TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("specs", searchParam.get("specs")); boolQueryBuilder.filter(termQueryBuilder); } //价格范围 //1.获取参数 Long maxPrice = transferToLong(searchParam.get("maxPrice")); Long minPrice = transferToLong(searchParam.get("minPrice")); //2.根据情况组装条件 if (maxPrice!=null||minPrice!=null){ RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price"); //gte great than and equal 大于等于 //lte less than and equal 小于等于 if (maxPrice!=null){ rangeQueryBuilder.lte(maxPrice); } if (minPrice!=null){ rangeQueryBuilder.gte(minPrice); } boolQueryBuilder.must(rangeQueryBuilder); } //销量排序 if (StringUtils.hasText(searchParam.get("sortWay"))) { if ("desc".equalsIgnoreCase(searchParam.get("sortWay").toString())) { searchSourceBuilder.sort("salesVolume", SortOrder.DESC); } else { searchSourceBuilder.sort("salesVolume", SortOrder.ASC); } } searchSourceBuilder.query(boolQueryBuilder); //设置分页 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<String, Object> map = new HashMap<>(); map.put("list", list); map.put("totalResultSize", totalHits); map.put("current", current); //设置总页数 map.put("totalPage", (totalHits + size - 1) / size); map.put("brand", searchParam.get("brand")); map.put("area", searchParam.get("area")); map.put("specs", searchParam.get("specs")); map.put("sortWay", searchParam.get("sortWay")); map.put("minPrice", searchParam.get("minPrice")); map.put("maxPrice", searchParam.get("maxPrice")); return map; } catch (IOException e) { throw new RuntimeException(e); } }
用户在进行搜索时,有可能打错字输入了错误的信息。 那么后台在处理时,应该智能的识别错误信息,更加友好的展示相关数据。
自动纠错查询,会自动尝试将搜索条件进行纠错,然后去跟term进行匹配。
-
fuzziness: 查询允许的最大编辑距离,默认为0,允许搜索条件出现几个错别字;
-
prefix_length:设置前几个字符不允许编辑
自动纠错查询
- fuzzines:允许搜索条件出现x个错别字
- prefix_length:不允许搜索条件的第1字出错
用户输入搜索条件为北漂去,也可以搜索area为北京的酒店;
GET hotel/_search
{
"query": {
"fuzzy": {
"area": {
"value": "北漂去",
"fuzziness": 2,
"prefix_length": 1
}
}
}
}
//地址纠错查询 @Override public Map<String, Object> searchFuzzyQuery(Integer current, Integer size, Map<String, Object> searchParam) { //设置查询 SearchRequest searchRequest = new SearchRequest("hotel"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //设置查询方式 String area=(String) searchParam.get("area"); //设置城市为纠错查询 if(StringUtils.hasText(area)){ FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("area", area) .fuzziness(Fuzziness.TWO) //允许搜索条件出现2个错别字 .prefixLength(1); //不允许搜索条件的第1字出错 searchSourceBuilder.query(fuzzyQueryBuilder); } //设置分页 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<String, Object> map = new HashMap<>(); map.put("list", list); map.put("totalResultSize", totalHits); map.put("current", current); map.put("brand", searchParam.get("brand")); map.put("area", searchParam.get("area")); map.put("specs", searchParam.get("specs")); map.put("sortWay", searchParam.get("sortWay")); map.put("minPrice", searchParam.get("minPrice")); map.put("maxPrice", searchParam.get("maxPrice")); //设置总页数 map.put("totalPage", (totalHits + size - 1) / size); return map; } catch (IOException e) { e.printStackTrace(); } return null; }
#高亮查询结果 #告诉前端为什么匹配中了 GET hotel/_search { "query": { "match": { "name": "北京市" } }, "highlight": { "fields": { "name": { "pre_tags": "<font color='red'>", "post_tags": "</font>" } } } }
//按名称高亮查询 @Override public Map<String, Object> searchHighLight(Integer current, Integer size, Map<String, Object> searchParam) { //设置查询 SearchRequest searchRequest = new SearchRequest("hotel"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //设置查询方式 if (!StringUtils.isEmpty(searchParam.get("condition"))) { QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString()) .field("name") .field("synopsis") .field("area") .field("address") .defaultOperator(Operator.OR); searchSourceBuilder.query(queryBuilder); } //设置分页 searchSourceBuilder.from((current - 1) * size); searchSourceBuilder.size(size); //查询高亮设置 HighlightBuilder highlightBuider = new HighlightBuilder(); highlightBuider.field("name"); highlightBuider.preTags("<font color='red'>"); highlightBuider.postTags("</font>"); searchSourceBuilder.highlighter(highlightBuider); 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(); HotelEntity hotelEntity = JSON.parseObject(sourceAsString, HotelEntity.class); //处理高亮结果 Map<String, HighlightField> highlightFields = searchHit.getHighlightFields(); HighlightField highlightField = highlightFields.get("name"); if (highlightField!=null){ Text[] fragments = highlightField.fragments(); hotelEntity.setName(fragments[0].toString()); } list.add(hotelEntity); } Map<String, Object> map = new HashMap<>(); map.put("list", list); map.put("totalResultSize", totalHits); map.put("current", current); //设置总页数 map.put("totalPage", (totalHits + size - 1) / size); return map; } catch (IOException e) { e.printStackTrace(); } return null; }
十一、短语查询(match_phrase)
在搜索条件部分词的前提下,使用短语进行查询;
1.kibana查询
#磁盘空间写满 GET k8s-2023.02.13/_search { "query": { "bool": { "must": [ { "match_phrase": { "message": "app-workspace-373-1675214627414-podgroup-s4l6-5qr7g" }}, { "match_phrase": { "message": "Evicted" }} ]} }, "sort": [ { "@timestamp": { "order": "desc" } }], "from": 0, "_source": { "includes":["@timestamp","host_ip","log.file.path","message"] } }