Elastic Stack/Elastic Search

Spring Boot - ElasticSearch RestHighLevelClient 검색 결과 하이라이팅 (HighlightBuilder)

super728 2021. 11. 30. 18:58

RestHighLevelClient의 HighlightBuilder를 이용하여 검색 결과를 하이라이팅 처리해보겠습니다. 

Map<String,Object> resultMap = new HashMap<String,Object>();
try {
	SearchRequest searchRequest = new SearchRequest("combook*"); // 인덱스명
	SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
	searchSourceBuilder.size(10000);
	searchSourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
			
			
	searchSourceBuilder.query(QueryBuilders.matchQuery("title", "지구별")); // title 필드 검색
	searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));  // score 높은순 (default)
			
	HighlightBuilder highlightBuilder = new HighlightBuilder(); 
	HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("title"); 
	highlightTitle.highlighterType("unified");  // unified : 통일(default)
	highlightBuilder.field(highlightTitle);  
	searchSourceBuilder.highlighter(highlightBuilder);
			
	searchRequest.source(searchSourceBuilder);
	SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); // 검색 요청
	SearchHits hits = searchResponse.getHits();
	TotalHits totalHits = hits.getTotalHits(); // total 검색 건수
			
	SearchHit[] searchHits = hits.getHits();
	List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
	list = new ArrayList<Map<String,Object>>();
	for (SearchHit hit : searchHits) {
		Map<String, Object> sourceAsMap = hit.getSourceAsMap(); // 검색 결과를 Map으로 한건 한건 받아서 list에 추가
		HighlightField field = hit.getHighlightFields().get("title");
		sourceAsMap.put("highlight", field);
		list.add(sourceAsMap);
	}
	resultMap.put("totalHits", totalHits.value);
	resultMap.put("searchResults", list);
}catch (Exception e) {
	e.printStackTrace();
	resultMap.put("totalHits", -500);
	resultMap.put("exception", e);
}
return resultMap;

searchSourceBuilder.query(QueryBuilders.matchQuery("title", "지구별")); 은 title 필드에서 "지구별" 키워드로 검색을 합니다. 

 

하이라이팅을 설정하는 부분입니다. 

HighlightBuilder highlightBuilder = new HighlightBuilder(); 
HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("title"); // 하이라이팅 하는 필드명을 설정합니다. 
highlightTitle.highlighterType("unified");  // unified : 통일(default) // 하이라이팅 타입을 설정합니다. unified 뿐만 아니라 plain, fvh 옵션을 사용할 수 있습니다. 
highlightBuilder.field(highlightTitle);  // highlightTitle을 highlightBuilder에 적용시킵니다. 
searchSourceBuilder.highlighter(highlightBuilder); // 설정이 적용된 highlightBuilder를 searchSourceBuilder에 반영합니다. 

 

 검색 결과를 가져옵니다. 

for (SearchHit hit : searchHits) {
  Map<String, Object> sourceAsMap = hit.getSourceAsMap(); // 검색 결과를 Map으로 한건 한건 받아서 list에 추가
HighlightField field = hit.getHighlightFields().get("title"); // 하이라이팅 결과 접근
sourceAsMap.put("highlight", field); // 하이라이팅 결과 맵에 추가하기


list.add(sourceAsMap);
}

 

결과 확인

{
  "totalHits": 1,
  "searchResults": [
    {
      "images": "/images/88.jpg|/images/89.jpg|/images/90.jpg",
      "list_price": 180000,
      "book_id": 60,
      "title": "지구별 신화여행",
      "max_age": "10",
      "shipping_fee": "0",
      "reg_date": "20210930000000",
      "new_or_used": "u",
      "highlight": {
        "name": "title",
        "fragments": [
          {
            "fragment": true
          }
        ],
        "fragment": true
      },
      "@timestamp": "2021-11-22T12:49:02.121Z",
      "seller_name": "달콤하게v (개인사업자)",
      "pub_year": null,
      "price": 40000,
      "@version": "1",
      "publisher": "한국삐아제",
      "seller_contact": "010-3333-8888",
      "state": "전30권,깨끗합니다 사진으로 보세요~",
      "updated_date": null,
      "department": "역사/유사",
      "min_age": "5",
      "seller_id": "admin"
    }
  ]
}

highlight 부분에 우리가 원하는 하이라이팅 결과가 포함되어 있지 않습니다. 다시 소스단으로 넘어가서 해결을 해봅시다. 

for (SearchHit hit : searchHits) {
	Map<String, Object> sourceAsMap = hit.getSourceAsMap(); // 검색 결과를 Map으로 한건 한건 받아서 list에 추가
	HighlightField field = hit.getHighlightFields().get("title");
	sourceAsMap.put("hl_name", field.getName());
	Text[] t = field.getFragments();
	sourceAsMap.put("hl_fragments", t[0]);
	sourceAsMap.put("hl_toString", t[0].toString());
	
	list.add(sourceAsMap);
}

결과 확인

{
  "totalHits": 1,
  "searchResults": [
    {
      "images": "/images/88.jpg|/images/89.jpg|/images/90.jpg",
      "hl_name": "title",
      "hl_fragments": {
        "fragment": true
      },
      "list_price": 180000,
      "book_id": 60,
      "title": "지구별 신화여행",
      "max_age": "10",
      "shipping_fee": "0",
      "reg_date": "20210930000000",
      "new_or_used": "u",
      "@timestamp": "2021-11-22T12:49:02.121Z",
      "seller_name": "달콤하게v (개인사업자)",
      "pub_year": null,
      "price": 40000,
      "@version": "1",
      "hl_toString": "<em>지구별</em> 신화여행",
      "publisher": "한국삐아제",
      "seller_contact": "010-3333-8888",
      "state": "전30권,깨끗합니다 사진으로 보세요~",
      "updated_date": null,
      "department": "역사/유사",
      "min_age": "5",
      "seller_id": "admin"
    }
  ]
}

hl_toString 에 원하는 결과가 도출되었습니다.