Elasticsearch java高亮顯示

Elasticsearch java高亮顯示

  • 爲什麼要寫
    1. 因爲版本原因很多網上的案例變動較大
    2. 基於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的實現

  1. 網上找到的常用版本

    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;
        }
    
  2. 方法二

    實現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());
    

完!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章