TIL

2021_06_25) 통합검색(ElasticSearch) 데이터 받아와서 가공한 후 출력하기(java)

뱅타 2021. 6. 26. 00:04

Preview


ElasticSearch로 데이터 받아오기
public List<Map<String,Object>> totalSearch(
			Map<String,Object> query
			, Map<String,SortOrder> sort
			, Integer size
			){
		/*
		 * search API 참고 주소
		 * https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/java-rest-high-search.html
		 */
		
		// search에 index 조건 걸기
		SearchRequest searchRequest = new SearchRequest();
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		
		// query에 있는 셋 쿼리 조건으로 걸기
			searchSourceBuilder.query(QueryBuilders.matchAllQuery());
		
		// sort 에 있는 셋을 정렬 조건으로 걸기
		for(String key : sort.keySet()) {
			searchSourceBuilder.sort(new FieldSortBuilder(key).order(sort.get(key)));
		}
		
		if(size != null) {
			searchSourceBuilder.size(size);
		}else {
			searchSourceBuilder.size(20);
		}
		
		searchRequest.source(searchSourceBuilder);
		
		List<Map<String,Object>> list = new ArrayList<>();
		try(RestHighLevelClient client = new RestHighLevelClient(restClientBuilder)) {
			SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
			SearchHits searchHits = response.getHits();
			for(SearchHit hit : searchHits) {
				Map<String, Object> sourceMap = hit.getSourceAsMap();
				list.add(sourceMap);
			}
		} catch (IOException e) {}
		
		return list;
		
	}
  • 열심히 검색했지만 전체 index, 전체 field에서 특정 데이터만 들고오는방법을 알수 없음.

 

 

ElasticSearch로 특정 데이터 받아오기


elasticSearch에 직접 요청 보내기
  • 위의 주소에서 알려준 것처럼 직접 요청을 보냄
String keyword = (String) map.get("keyword");
String url = String.format("http://%s:%d/gaia/_search?q=%s",elUtil.getHostname(),elUtil.getPort(),keyword);
String text = getRequestApiGet(url);
public String getRequestApiGet(String url) throws IOException {
		URL obj = new URL(url);
		HttpURLConnection con = (HttpURLConnection) obj.openConnection();
		// optional default is GET
		con.setRequestMethod("GET");
		
		// add requesttt header 헤더를 만드는
		con.setRequestProperty("Accept-Chatset", "UTF-8");
		con.setRequestProperty("Content-Type", "application/json; charset=utf-8");
		
		BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
		String inputLine;
		String resultXmlText = "";
		while((inputLine = in.readLine()) != null) {
			resultXmlText += inputLine;
		}
		in.close();
		con.disconnect();
		return resultXmlText;
	}
  • utf-8 혹은 euc-kor을 인식하지 못함.
  • 간단하게 적었지만 팀장의 도움이 없었다면 상당히 많은 시간을 할애하고 고생했을 듯.

 

 

해결


유니코드로 변환 후 전송.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI

  • 개발자 툴에 보면 아래 요청이 유니코드로 가는것을 알 수 있음.
  • 현재는 한글을 unicode로 변환해서 요청하기 때문에 우측화면처럼 결과값이 잘 받아오지만
  • 변환시키기 전에는 요청했던 url이 그대로 출력됨 e.g) &keyword=한글
  • 그래서 js에서 $.ajax를 호출하기 전에 변환시켜서 그대로 넘겨줌
keyword = encodeURI(keyword)
  • java에서 unicode로 변환시키려다 안되서 js에서 한건 비밀.

 

 

ElasticSearch keyup event
  • keyUp을 작동시킬 때 setTimeout으로 0.5초마다 값 올리기
$('body').on("keyup", "#dropdownMenuSearchInput",function(e){
		let keyword = $(this).val()
		console.log($(this).val())
		setTimeout("elasticTotalSearch('"+keyword+"')", 2000)
	})
  • setTimeout이 생각보다 어려웠음.
setTimeout(elasticTotalSearch, 2000) // 파라미터가 존재않으면      
setTimeout("elasticTotalSearch('"+keyword+"')", 2000) // 성공

setTimeout("elasticTotalSearch('keyword')", 2000) // 파라미터 keyword를 못찾는다고 뜸
setTimeout(elasticTotalSearch('keyword'), 2000) // setTimeout event가 먹히지 않고 바로 function이 실행.
  • 위와 같이 파라미터를 넘기면 작동이 안됨. 삽질을 좀 함.
  • 실제 구현 코드
var timer;
	$('body').on("keyup", "#dropdownMenuSearchInput",function(e){
		console.log($(this).val())
		clearTimeout(timer);
		let keyword = $(this).val()
		timer = setTimeout("elasticTotalSearch('"+keyword+"')", 500)
	})
  • 타자를 치면 가장 마지막 친 글의 0.5초가 지나야지 요청을 한번 보냄.

 

 

elastic에서 받은 데이터 후가공하기
  1. 검색 결과에 따라 issue, milestone, project, member로 나뉘어서 출력할 예정
  1. list로 받은 결과를 loop를 돌면서 하나씩 빼주기

e.g 5개의 값을 담고있는 리스트 중 issue에 2개가 포함되어 있으면 해당 리스트에서 2개를 뺀 3개의 값을 담고있는 리스트로 milestone을 검사하게끔.

  • code
$.each(res.hits.hits, function(i, hit){
		console.log(i)
		console.log(hit)
		if(key in hit["_source"]){
			searchResult = $("#headerTemplate .dropdown-menu.total-search-dropdown").children("a").clone()
			if(key == "issue_sid"){
				searchResult.html('<i class="icon-fire"></i>'+hit._source.issue_his_cont)
			}else if(key == "milest_sid"){
				searchResult.html('<i class="icon-fire"></i>'+hit._source.issue_his_cont)
			}else if(key == "proj_no"){
				searchResult.html('<i class="icon-fire"></i>'+hit._source.proj_title)
			}else if(key == "mem_no"){
				searchResult.html('<i class="icon-fire"></i>'+hit._source.mem_no)
			}
			var index = res.hits.hits.indexOf(hit);
			if (index !== -1) {
				console.log(res.hits.hits.splice(index-cnt, 1));
				cnt++;
			}
			$(".dropdown .dropdown-menu.total-search-dropdown").prepend(searchResult);
		}
	})

5개의 값 중 이슈에 2개읙 값이 포함되어 있으면 오류가 남. (4째 값이존재하지 않기 때문)

 

 

해결 방안


  1. copyList를 만들고 해당하는 값이 존재하면 이 녀석을 빼준다.(원본말고)
  1. 해당하는 값이 존재해서 빼 줄때 i를 같이 빼준다.
    • 실행해 보니 어림없다. 곰곰히 생각해 보니 말 그대로 어림없는게 맞는듯 하다.
    • 우리는 for안에서 i 를 가지고 논다.상단의i에 영향을 주기 위해서는 each문 위에 i를 선언해주거나
    • each() 자체를 건들어야할 듯.
  1. .reverse()를 사용한다. $().reverse().each(function(...){})형태
    • 안됨. 문법을 잘 못 썻는지 계속 오류남.
    • 이것도 곰곰히 생각해 보고 document를 읽어봤는데 내가 쓸 용도와는 다른 듯 함.
    • 나는 0,1,2,3,4,,, 이렇게 읽는게 아니라 4,3,2,1,0 이렇게 읽고 싶은데 reverse는 거꾸로 뒤집어서
    • index는 여전히 0,1,2,3,4,,,로 읽는 듯함.

 

  • copyList = res.hits.hits를 하는 순간 서로 주소값이 같아짐.
copyList = new Array[]
copyList = res.hits.hits // 다시 주소값이 같아짐.
  • 그러다 ...을 발견.
res.hits.hits = [...copyList]
💡
[...arr].reverse() 배열을 복사하기 위해서 spread operator(전개 연산자)를 사용하였습니다. '...'이 spread operator(전개 연산자)입니다. spread operator(전개연산자)는 배열이나 객체에서 element들을 꺼내어, 복사해줍니다. 즉, 위 코드에서 [...arr] 구문은, 원본 배열인 arr를 복사한 새로운 배열입니다. 출처: https://hianna.tistory.com/448 [어제 오늘 내일]

 

 

Summary


  • elasticSearch에서 값을 받아오는게 생각보다 힘들었음.
  • 값을 받아온 뒤 후처리하는것도 생각보다 힘들었음.
  • [...]

 

Tomorrow


  • 통합검색 css를 수정하고
  • 좀 더 가다듬고
  • 시나리오를 짜면 될 듯 함.
728x90
반응형