Elasticsearch java高亮顯示
- 爲什麼要寫
- 因爲版本原因很多網上的案例變動較大
- 基於springboot 2.2.2 elasticsearch 7.10.1
原理
-
elastic支持,請求格式如下:
{ "query": { "bool": { "should": [ { "match": { "addName": "test" } }, { "match": { "fileName": "環境" } } ], "minimum_should_match": 2 } }, "highlight": { "fields": { "fileName":{} } } }
基於API的實現
-
網上找到的常用版本
public Object queryKeyWord(HttpServletRequest request, @RequestParam(value = "keyWord")String keyWord, @RequestParam(value = "page")int page, @RequestParam(value = "size")int size) throws IOException { // 在索引中進行查詢 SearchRequest searchRequest = new SearchRequest(esDocumentIndex); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); // 搜索名字 TermQueryBuilder termQuery = QueryBuilders.termQuery("fileName", keyWord); sourceBuilder.query(termQuery); sourceBuilder.from(page); sourceBuilder.size(size); // sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); // 添加高亮 HighlightBuilder highlightBuilder = new HighlightBuilder(); HighlightBuilder.Field highlightName = new HighlightBuilder.Field("fileName"); //把 fileName 域設爲高亮 highlightBuilder.field(highlightName); highlightBuilder.preTags("<span style='color:red'>"); highlightBuilder.postTags("</span>"); sourceBuilder.highlighter(highlightBuilder); // 發起請求 searchRequest.source(sourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); // 保存高亮 ArrayList<Map<String,Object>> list = new ArrayList<>(); SearchHits hits = searchResponse.getHits(); for (SearchHit hit : hits) { // 獲取高亮域 Map<String, HighlightField> highlightFields = hit.getHighlightFields(); HighlightField highlight = highlightFields.get("fileName"); // 獲取原本的 SourceMap Map<String, Object> sourceAsMap = hit.getSourceAsMap(); // 這裏一定要先判斷域是否爲空,因爲可能查詢不到結果 if (highlight != null) { Text[] fragments = highlight.fragments(); StringBuilder new_name = new StringBuilder(); for (Text text : fragments) { new_name.append(text); } // 覆蓋原本的 SourceMap sourceAsMap.put("fileName", new_name); } list.add(sourceAsMap); } return list; }
-
方法二
實現DocumentRepository
public interface DocumentRepository extends ElasticsearchRepository<DocumentPo,Long> { }
構建查詢
// 構建查詢條件 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 添加基本分詞查詢 queryBuilder.withQuery(QueryBuilders.matchQuery("fileName", keyWord)); // 添加高亮 HighlightBuilder highlightBuilder = new HighlightBuilder(); HighlightBuilder.Field highlightName = new HighlightBuilder.Field("fileName"); //把 name 域設爲高亮 highlightBuilder.field(highlightName); highlightBuilder.preTags("<span style='color:red'>"); highlightBuilder.postTags("</span>"); queryBuilder.withHighlightBuilder(highlightBuilder); queryBuilder.withPageable(PageRequest.of(page, size)); // 搜索,獲取結果 Page<DocumentPo> items = documentRepository.search(queryBuilder.build());
發現高亮沒有生效,跟蹤源碼發現在箭頭區域給過濾掉了
解決辦法
自己重寫這個方法,查看結構自己定義一個mapper轉換,返回就行。
但是查看search方法沒有接收參數
@NoRepositoryBean public interface ElasticsearchRepository<T, ID> extends ElasticsearchCrudRepository<T, ID> { <S extends T> S index(S entity); /** * This method is intended to be used when many single inserts must be made that cannot be aggregated to be inserted * with {@link #saveAll(Iterable)}. This might lead to a temporary inconsistent state until {@link #refresh()} is * called. */ <S extends T> S indexWithoutRefresh(S entity); Iterable<T> search(QueryBuilder query); Page<T> search(QueryBuilder query, Pageable pageable); Page<T> search(SearchQuery searchQuery); Page<T> searchSimilar(T entity, String[] fields, Pageable pageable); void refresh(); Class<T> getEntityClass(); }
繼續向下找,發現search調用的是queryForPage
@Override public Page<T> search(SearchQuery query) { return elasticsearchOperations.queryForPage(query, getEntityClass()); }
繼續找發現在下一步參數中多了一個resultsMapper,而出問題的方法也在這個參數對象中,於是我們只需要重寫這個參數並調用這個方法就可以了。
@Override public <T> AggregatedPage<T> queryForPage(SearchQuery query, Class<T> clazz) { return queryForPage(query, clazz, resultsMapper); }
重寫參數
public class HighlightResultMapper implements SearchResultMapper { @Override public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> clazz, Pageable pageable) { long totalHits = searchResponse.getHits().getTotalHits(); List<T> list = new ArrayList<>(); SearchHits hits = searchResponse.getHits(); if (hits.getHits().length> 0) { for (SearchHit searchHit : hits) { Map<String, HighlightField> highlightFields = searchHit.getHighlightFields(); T item = JSON.parseObject(searchHit.getSourceAsString(), clazz); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); if (highlightFields.containsKey(field.getName())) { try { field.set(item, highlightFields.get(field.getName()).fragments()[0].toString()); } catch (IllegalAccessException e) { e.printStackTrace(); } } } list.add(item); } } return new AggregatedPageImpl<>(list, pageable, totalHits); } @Override public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) { return null; } }
更改調用過程
// 構建查詢條件 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 添加基本分詞查詢 queryBuilder.withQuery(QueryBuilders.matchQuery("fileName", keyWord)); // 添加高亮 HighlightBuilder highlightBuilder = new HighlightBuilder(); HighlightBuilder.Field highlightName = new HighlightBuilder.Field("fileName"); //把 name 域設爲高亮 highlightBuilder.field(highlightName); highlightBuilder.preTags("<span style='color:red'>"); highlightBuilder.postTags("</span>"); queryBuilder.withHighlightBuilder(highlightBuilder); queryBuilder.withPageable(PageRequest.of(page, size)); // 搜索,獲取結果 // Page<DocumentPo> items = documentRepository.search(queryBuilder.build()); SearchQuery searchQuery = null; searchQuery= queryBuilder.build(); Page<DocumentPo> items = new ElasticsearchRestTemplate(restHighLevelClient).queryForPage(searchQuery,DocumentPo.class,new HighlightResultMapper());
完!