正文
四、filter
查询在老寇云交流群发了关于生日快乐的内容(两种方式)
GET /message/_search { "query": { "bool": { "must": [ { "match": { "data": "生日快乐" } }, { "match": { "remark": "老寇云交流群" } } ] } } }
GET /message/_search { "query": { "bool": { "must": [ { "match": { "data": "生日快乐" } } ], "filter": [ { "term": { "remark": "老寇云交流群" } } ] } } }
query 与 filter 对比
filter:只会按照搜索条件过滤出所需要的数据,不会计算相关度分数,因此对相关度没有影响,同时内置自动缓存最常使用filter的数据
query:会计算每个文档对搜索条件的相关度,并按相关度排序,然而需要计算相关度,所以无法缓存结果
应用场景:在进行搜索时,需要根据一些条件筛选部分数据,而且不关注其排序,建议使用filter,反之,使用query
五、定制排序规则
constant_score - 举个栗子
GET /message/_search { "query": { "constant_score": { "filter": { "term": { "data": "生日快乐" } }, "boost": 1.2 } }, "sort": [ { "createDate": { "order": "desc" } } ] }
六、代码实现
1.引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.6.2</version> <exclusions> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>7.6.2</version> </dependency> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.6.2</version> </dependency>
2.增加实体类
@Data @ApiModel(description = "查询表单实体类") public class QueryForm implements Serializable { /** * 页码 */ private Integer pageNum = 1; /** * 条数 */ private Integer pageSize = 10; /** * 是否分页 */ private boolean needPage = false; /** * 查询索引名称 */ private String[] indexNames; /** * 分词搜索 */ private List<SearchDTO> queryStringList; /** * 排序 */ private List<SearchDTO> sortFieldList; /** * 高亮搜索字段 */ private List<String> highlightFieldList; /** * or搜索-精准匹配 */ private List<SearchDTO> orSearchList; }
/** * 搜索DTO * @author Kou Shenhai 2413176044@leimingtech.com * @version 1.0 * @date 2022/3/15 0015 上午 9:45 */ @Data public class SearchDTO implements Serializable { private String field; private String value; }
3.分词查询
@Slf4j @Component public class ElasticsearchUtil { @Autowired private RestHighLevelClient restHighLevelClient; private static final String HIGHLIGHT_PRE_TAGS = "<span class='highlight'>"; private static final String HIGHLIGHT_POST_TAGS = "</span>"; private static final String PINYIN_SUFFIX = ".pinyin"; /** * 关键字高亮显示 * @param queryForm 查询实体类 * @return * @throws IOException */ public HttpResultUtil<SearchVO> search(QueryForm queryForm) throws IOException { long startTime = System.currentTimeMillis(); final String[] indexName = queryForm.getIndexNames(); final List<SearchDTO> orSearchList = queryForm.getOrSearchList(); final List<SearchDTO> sortFieldList = queryForm.getSortFieldList(); final List<String> highlightFieldList = queryForm.getHighlightFieldList(); final List<SearchDTO> queryStringList = queryForm.getQueryStringList(); final Integer pageNum = queryForm.getPageNum(); final Integer pageSize = queryForm.getPageSize(); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //用于搜索文档,聚合,定制查询有关操作 SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(indexName); //or查询 BoolQueryBuilder orQuery = QueryBuilders.boolQuery(); for (SearchDTO dto : orSearchList) { orQuery.should(QueryBuilders.termQuery(dto.getField(),dto.getValue())); } boolQueryBuilder.must(orQuery); //分词查询 BoolQueryBuilder analysisQuery = QueryBuilders.boolQuery(); for (SearchDTO dto : queryStringList) { final String field = dto.getField(); //清除左右空格 String keyword = dto.getValue().trim(); //处理特殊字符 keyword = QueryParser.escape(keyword); analysisQuery.should(QueryBuilders.queryStringQuery(keyword).field(field).field(field.concat(PINYIN_SUFFIX))); } boolQueryBuilder.must(analysisQuery); //高亮显示数据 HighlightBuilder highlightBuilder = new HighlightBuilder(); //设置关键字显示颜色 highlightBuilder.preTags(HIGHLIGHT_PRE_TAGS); highlightBuilder.postTags(HIGHLIGHT_POST_TAGS); //设置显示的关键字 for (String field : highlightFieldList) { highlightBuilder.field(field, 0, 0).field(field.concat(PINYIN_SUFFIX), 0, 0); } highlightBuilder.requireFieldMatch(false); //分页 int start = 0; int end = 10000; if (queryForm.isNeedPage()) { start = (pageNum - 1) * pageSize; end = pageSize; } //设置高亮 searchSourceBuilder.highlighter(highlightBuilder); searchSourceBuilder.from(start); searchSourceBuilder.size(end); //追踪分数开启 searchSourceBuilder.trackScores(true); //注解 searchSourceBuilder.explain(true); //排序 for (SearchDTO dto : sortFieldList) { SortOrder sortOrder; final String desc = "desc"; final String value = dto.getValue(); final String field = dto.getField(); if (desc.equalsIgnoreCase(value)) { sortOrder = SortOrder.DESC; } else { sortOrder = SortOrder.ASC; } searchSourceBuilder.sort(field,sortOrder); } searchSourceBuilder.query(boolQueryBuilder); searchRequest.source(searchSourceBuilder); SearchHits hits = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT).getHits(); List<Map<String,Object>> data = new ArrayList<>(); for (SearchHit hit : hits){ Map<String,Object> sourceData = hit.getSourceAsMap(); Map<String, HighlightField> highlightFields = hit.getHighlightFields(); for (String key : highlightFields.keySet()){ sourceData.put(key,highlightFields.get(key).getFragments()[0].string()); } data.add(sourceData); } long endTime = System.currentTimeMillis(); HttpResultUtil<SearchVO> result = new HttpResultUtil(); SearchVO vo = new SearchVO(); final String searchData = queryStringList.stream().map(i -> i.getValue()).collect(Collectors.joining(",")); final List<String> searchFieldList = queryStringList.stream().map(i -> i.getField()).collect(Collectors.toList()); vo.setRecords(handlerData(data,searchFieldList)); vo.setTotal(hits.getTotalHits().value); vo.setPageNum(queryForm.getPageSize()); vo.setPageSize(queryForm.getPageSize()); result.setMsg("搜索 <span class='highlight'>" + searchData + "</span> 找到 " + vo.getTotal() + " 个与之相关的内容,耗时:" + (endTime - startTime) +"ms"); //处理数据 result.setData(vo); return result; } /** * 处理高亮后的数据 * @param data ES查询结果集 */ private List<Map<String,Object>> handlerData(List<Map<String,Object>> data,List<String> fieldList) { log.info("查询结果:{}",data); if (CollectionUtils.isEmpty(data)) { return Lists.newArrayList(); } if (CollectionUtils.isEmpty(fieldList)) { return data; } for (Map<String, Object> map : data) { for (String field : fieldList) { if (map.containsKey(field.concat(PINYIN_SUFFIX))) { String result1 = map.get(field).toString(); String result2 = map.get(field.concat(PINYIN_SUFFIX)).toString(); //将同义词合并 for (;;) { int start = result1.indexOf(HIGHLIGHT_PRE_TAGS); int end = result1.indexOf(HIGHLIGHT_POST_TAGS); if (start == -1 || end == -1) { break; } String replaceKeyword = result1.substring(start, end).replace(HIGHLIGHT_PRE_TAGS, ""); result2 = result2.replaceAll(replaceKeyword, HIGHLIGHT_PRE_TAGS + replaceKeyword + HIGHLIGHT_POST_TAGS); result1 = result1.substring(end + 1); } map.put(field, result2); map.remove(field.concat(PINYIN_SUFFIX)); } } } return data; } }
4.api测试
@RestController @RequestMapping("/api") @Api(tags = "Elasticsearch API 服务") public class ElasticsearchController { @Autowired private ElasticsearchUtil elasticsearchUtil; @PostMapping("/search") @ApiOperation("ES关键字搜索-高亮显示") @ResponseBody public HttpResultUtil<SearchVO> search(@RequestBody @Validated final QueryForm queryForm, BindingResult bindingResult) throws IOException { if (bindingResult.hasErrors()) { return new HttpResultUtil<SearchVO>().error(bindingResult.getFieldError().getDefaultMessage()); } return elasticsearchUtil.search(queryForm); } }
大功告成