Elastic Search

Ch08. 엘라스틱서치 클라이언트 - Transport 클라이언트

webmaster 2025. 10. 3. 22:19
728x90

엘라스틱서치 7.0에서 Tranport 클라이언트 모듈은 미지원되고 8.0에서는 모두 제거될 예정

 

Transport 클라이언트 연결

dependencies {
    compile group: 'org.elasticsearch.client', name: 'transport', version: '6.4.3'
}
Settings settings = Settings.builder()
	.put("cluster.name", "javacafe-es")
	//.put("client.transport.sniff", true)
	.build();

TransportClient client = new PreBuiltTransportClient(settings)
	.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
  • Transport 클라이언트는 내부적으로 소켓을 사용해 엘라스틱서치 클러스터에 원격으로 연결(Setting 정보에 클러스터 입력)
  • Port와 IP 정보를 입력해야 하며 Port값이 없다면 기본적으로 9300 Port를 사용
  • Transport는 새로운 노드를 추가하거나, 기존 노드를 삭제할 수 있는 스니핑이 내장되어 있음
    • 스니핑 기능이 활성화되면 addTransportAddress 메서드를 호출해서 빌드된 노드 목록을 5초에 한 번씩 갱신

매핑 API 사용하기

동작 Query

PUT movie_java
{
  "settings": {
    "index": {
      "number_of_shards": 3,
      "number_of_replicas": 1
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "movieCd": {
          "type": "keyword",
          "store": true
        },
        "movieNm": {
          "type": "text",
          "store": true,
          "index_options": "docs"
        },
        "movieNmEn": {
          "type": "text",
          "store": true,
          "index_options": "docs"
        }
      }
    }
  }
}

인덱스 생성(Indice API)

String INDEX_NAME = "movie_java";
String TYPE_NAME = "_doc"; // ES 6.x까지는 type 사용 가능, 7.x부터는 제거됨

// 매핑 정보 작성
XContentBuilder indexBuilder = jsonBuilder()
        .startObject()
            .startObject("properties")
                .startObject("movieId")
                    .field("type", "keyword")
                    .field("store", true)
                    .field("index_options", "docs")
                .endObject()
                .startObject("movieNm")
                    .field("type", "text")
                    .field("store", true)
                    .field("index_options", "docs")
                .endObject()
                .startObject("movieNmEn")
                    .field("type", "text")
                    .field("store", true)
                    .field("index_options", "docs")
                .endObject()
            .endObject()
        .endObject();

client.admin().indices().prepareCreate(INDEX_NAME)
        .setSettings(Settings.builder()
                .put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 1)
        )
        .addMapping(TYPE_NAME, indexBuilder)
        .get();

 

 

  • 모든 항목은 startObject(), endObject() 메서드로 표현해야 한다.
  • 필드의 타입이나 속성을 지정할 경우 field() 메서드를 사용하고, 인덱스를 생성할 때는 client(), admin(), indices() 메서드를 사용한다.
  • prepareCreate() 메서드를 이용해 생성한 인덱스의 이름을 지정하고, 설정 정보와 인덱스 정보를 설정
  • 모든 설정이 완료되면 get() 메서드를 호출하면 된다
    • get() 호출 시, 엘라스틱서치로 매핑 정보를 전송하고 인덱스 생성 여부를 결과로 반환

문서 API 사용하기

단건 문서 추가

String INDEX_NAME = "movie_auto_java";
String TYPE_NAME  = "_doc";   
String _id        = "1";

// 문서 색인
IndexResponse response = client.prepareIndex(INDEX_NAME, TYPE_NAME, _id)
        .setSource(jsonBuilder()
            .startObject()
                .field("movieId", "20173732")
                .field("movieNm", "살아남은 아이")
                .field("movieNmEn", "Last Child")
            .endObject()
        )
        .get();
  • 필드명과 필드값을 지정해서 생성 후 setSource() 메서드에 설정하면 된다.
  • prepareIndex() 메서드에 인덱스 명과 타입명, 데이터의 _id를 직접 입력해서 문서를 추가한다.
    • _id 값이 없으면 UUID로 값이 대체됨

대량의 문서 추가

// 인덱스 / 타입명
String INDEX_NAME = "movie_auto_java";
String TYPE_NAME  = "_doc";   // ES 6.x까지 사용

// 여러 개의 데이터를 담을 리스트
List<XContentBuilder> contentBuilders = new ArrayList<>();

// 1번 데이터 추가
XContentBuilder builder = jsonBuilder()
        .startObject()
            .field("movieId", "20184623")
            .field("movieNm", "바람 난 아내들 2")
            .field("movieNmEn", "")
        .endObject();
contentBuilders.add(builder);

// 2번 데이터 추가
builder = jsonBuilder()
        .startObject()
            .field("movieId", "20174244")
            .field("movieNm", "버블 패밀리")
            .field("movieNmEn", "Family in the Bubble")
        .endObject();
contentBuilders.add(builder);

// BulkRequestBuilder 객체 생성
BulkRequestBuilder bulkRequest = client.prepareBulk();

// contentBuilders 리스트 순회하며 문서 추가
int idCounter = 1;
for (XContentBuilder xContentBuilder : contentBuilders) {
    bulkRequest.add(
        client.prepareIndex(INDEX_NAME, TYPE_NAME, String.valueOf(idCounter++))
              .setSource(xContentBuilder)
    );
}

// 문서 전송
BulkResponse bulkResponse = bulkRequest.get();
  • 다수의 데이터를 추가할 때 _bluk API를 이용하며, bulkRequestBuilder로 데이터를 저장한다.
  • 색인 요청 후 결과를 확인하려면 IndexResponse 또는 BulkResponse 객체로 전달받으면 된다.
    • IndexResponse: _index, _type, _id, _version, _status, _shards, result 정보도 받을 수 있음
    • BulkResponse: 단순 작업 실패 여부만 확인 가능

문서 조회

String INDEX_NAME = "movie_auto_java";
String TYPE_NAME  = "_doc";  // ES 6.x까지는 type 사용 가능
String _id        = "20184623";

// 단일 문서 조회
GetResponse response = client.prepareGet(INDEX_NAME, TYPE_NAME, _id).get();
  • _id 기반으로 인덱스의 문서를 질의할 때 사용
  • prepareGet() 메서드를 이용하며 결과로 해당 id를 가지는 문서가 반환된다.
  • 결과로 제공되는 GetResponse 객체에서 response.getSource()를 호출해 Map 형태로 객체가 제공

문서 삭제

String INDEX_NAME = "movie_auto_java";
String TYPE_NAME  = "_doc";   

// 단일 문서 삭제
String _id        = "20184623";
DeleteResponse response = client.prepareDelete(INDEX_NAME, TYPE_NAME, _id).get();

//다건 문서 삭제
BulkByScrollResponse bulkResponse = DeleteByQueryAction.INSTANCE
        .newRequestBuilder(client)
        .filter(QueryBuilders.matchQuery("movieNm", "바람 난 아내들 2"))
        .source(INDEX_NAME)
        .get();
  • prepareDelete() 메서드를 이용해 id에 해당하는 문서를 삭제할 수 있다.
  • Query 기반으로 특정 문서를 검색해서 삭제할 수 있다.

문서 업데이트

String INDEX_NAME = "movie_auto_java";
String TYPE_NAME  = "_doc";   
String _id        = "20174244";

// UpdateRequest 생성
UpdateRequest updateRequest = new UpdateRequest(INDEX_NAME, TYPE_NAME, _id)
        .doc(jsonBuilder()
            .startObject()
                .field("movieNm", "수정 문서")
            .endObject()
        );

// 문서 수정 실행
UpdateResponse updateResponse = client.update(updateRequest).get();
  • 문서를 업데이트할 때 update() 메서드를 사용

한 번에 다수의 문서 조회

String INDEX_NAME = "movie_auto_java";
String TYPE_NAME  = "_doc"; // ES 6.x까지 사용

// MultiGet 요청
MultiGetResponse multiGetItemResponses = client.prepareMultiGet()
        .add(INDEX_NAME, TYPE_NAME, "20184623")
        .add(INDEX_NAME, TYPE_NAME, "20174244")
        .get();

// 결과 확인
for (MultiGetItemResponse itemResponse : multiGetItemResponses) {
    GetResponse response = itemResponse.getResponse();
    if (response.isExists()) {
        String json = response.getSourceAsString();
    } 
}
  • _mget API로 엘라스틱 서치에 여러 번 요청할 내용을 한 번에 요청할 수 있다.
  • prepareMultiGet() 메서드를 이용해 검색할 문서를 등록해서 한 번에 호출

Bulk Processor

String INDEX_NAME = "movie_auto_java";
String TYPE_NAME  = "_doc"; // ES 6.x까지 사용

// BulkProcessor 생성
BulkProcessor bulkProcessor = BulkProcessor.builder(
        client,
        new BulkProcessor.Listener() {
            @Override
            public void beforeBulk(long executionId, BulkRequest request) {
                System.out.println("Before bulk: " + request.numberOfActions() + " actions");
            }

            @Override
            public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
                System.out.println("After bulk: " + response.getItems().length + " items");
            }

            @Override
            public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
                System.err.println("Bulk failed: " + failure.getMessage());
            }
        })
        .setBulkActions(1000)
        .setBulkSize(new ByteSizeValue(5, ByteSizeUnit.MB))
        .setConcurrentRequests(1)
        .build();

// 벌크 데이터 추가

bulkProcessor.add(new IndexRequest(INDEX_NAME, TYPE_NAME, "20184623")
        .source(jsonBuilder()
                .startObject()
                .field("movieId", "20184623")
                .field("movieNm", "바람 난 아내들 ?")
                .field("movieNmEn", "")
                .endObject()
        ));

bulkProcessor.add(new IndexRequest(INDEX_NAME, TYPE_NAME, "20174244")
        .source(jsonBuilder()
                .startObject()
                .field("movieId", "20174244")
                .field("movieNm", "버블 패밀리")
                .field("movieNmEn", "Family in the Bubble")
                .endObject()
        ));
  • 한번에 처리할 문서 수, 문서 처리 방법, 데이터 새로고침 주기를 설정할 수 있는 인터페이스를 제공한다.
  • 데이터양에 상관없이 지정한 조건에 만족하면 엘라스틱서치로 색인 요청을 보내는 방식으로 동작

검색 API 사용하기

search API

String INDEX_NAME1 = "movie_auto";
String INDEX_NAME2 = "movie_search";
String TYPE_NAME   = "_doc";  // ES 6.x까지 사용
String FIELD_NAME  = "movieId";
String QUERY       = "20184623";

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME1, INDEX_NAME2)
        .setTypes(TYPE_NAME)
        .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
        .setQuery(QueryBuilders.termQuery(FIELD_NAME, QUERY))
        .setFrom(0)
        .setSize(10)
        .setExplain(true)
        .get();
  • setQuery() 메서드에 원하는 쿼리를 작성해 검색 요청을 할 수 있다.

scroll Api

String INDEX_NAME  = "movie_search";
String FIELD_NAME  = "movieNm";
String QUERY       = "아내들";

// Term 쿼리 생성
QueryBuilder queryBuilder = QueryBuilders.termQuery(FIELD_NAME, QUERY);

// 최초 검색 실행 (Scroll 시작)
SearchResponse scrollResp = client.prepareSearch(INDEX_NAME)
        .addSort(FieldSortBuilder.DOC_FIELD_NAME, SortOrder.ASC)
        .setScroll(new TimeValue(60000))  // Scroll 타임아웃 60초
        .setQuery(queryBuilder)
        .setSize(30)                      // 한번에 가져올 문서 수
        .get();

// Scroll 반복
do {
    // 데이터 출력
    for (SearchHit hit : scrollResp.getHits().getHits()) {
        String movieNm = hit.getSourceAsMap().get("movieNm").toString();
        System.out.println("movieNm: " + movieNm);
    }

    // 다음 Scroll batch 가져오기
    scrollResp = client.prepareSearchScroll(scrollResp.getScrollId())
            .setScroll(new TimeValue(60000))
            .get();

} while (scrollResp.getHits().getHits().length != 0); // 더 이상 문서가 없을 때 종료
  • 요청할 때마다 순차적으로 다음 페이지를 제공하는 Scroll API를 제공한다
  • Scroll API는 첫 페이지를 호출할 때 scroll_id를 전달받아 초기에 설정한 사이즈로 지속적 데이터를 전송한다.

집계 API 사용하기

String INDEX_NAME          = "movie_search";
String TYPE_NAME           = "_doc";  // ES 6.x까지 사용
String FIELD_NAME          = "repNationNm";
String QUERY               = "한국";
String AGGREGATION_NAME    = "countNationNm";
String AGGREGATION_FIELD   = "typeNm";

// Terms Aggregation 생성
AggregationBuilder aggs = AggregationBuilders
        .terms(AGGREGATION_NAME)
        .field(AGGREGATION_FIELD)
        .size(100);

// 검색 + Aggregation 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setTypes(TYPE_NAME)
        .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
        .setQuery(QueryBuilders.termQuery(FIELD_NAME, QUERY))
        .addAggregation(aggs)
        .setSize(0) // 검색 결과는 필요 없고 Aggregation만 가져오기
        .get();

// Aggregation 결과 출력
Terms termBucket = response.getAggregations().get(AGGREGATION_NAME);

for (Terms.Bucket bucket : termBucket.getBuckets()) {
    String typeNm  = bucket.getKeyAsString();
    long docCount   = bucket.getDocCount();
    System.out.println("typeNm: " + typeNm + ", count: " + docCount);
}
  • 집계 API를 사용할 경우 검색 결과는 출력하지 않는다.(setSize = 0)
  • addAggregation 메서드를 이용해 정의한 집계 쿼리를 추가한다.

Min Aggregation

String AGGREGATION_NAME = "minMovieId";
String AGG_FIELD_NAME   = "movieId";

// Min Aggregation 생성
MinAggregationBuilder aggregation = AggregationBuilders
        .min(AGGREGATION_NAME)
        .field(AGG_FIELD_NAME);
  • 최솟값을 구하는 집계(문서의 전체 혹은 필터로 질의된 문서 중 가장 작은 값 구함)

Max Aggregation

String AGGREGATION_NAME = "maxMovieId";
String AGG_FIELD_NAME   = "movieId";

// Max Aggregation 생성
MaxAggregationBuilder aggregation = AggregationBuilders
        .max(AGGREGATION_NAME)
        .field(AGG_FIELD_NAME);
  • 최댓값을 구하는 집계(문서의 전체 혹은 필터로 질의된 문서 중 가장 큰 값 구함)

Sum Aggregation

String AGGREGATION_NAME = "sumMovieId";
String AGG_FIELD_NAME   = "movieId";

// Sum Aggregation 생성
SumAggregationBuilder aggregation = AggregationBuilders
        .sum(AGGREGATION_NAME)
        .field(AGG_FIELD_NAME);
  • 전체 문서 혹은 특정 문서의 합계를 구하는 집계

Avg Aggregation

String AGGREGATION_NAME = "avgMovieId";
String AGG_FIELD_NAME   = "movieId";

// Avg Aggregation 생성
AvgAggregationBuilder aggregation = AggregationBuilders
        .avg(AGGREGATION_NAME)
        .field(AGG_FIELD_NAME);
  • 전체 문서 혹은 특정 문서의 평균을 내는 집계

Stats Aggregation

String AGGREGATION_NAME = "statsMovieCd";
String AGG_FIELD_NAME   = "movieCd";

// Stats Aggregation 생성
StatsAggregationBuilder aggregation = AggregationBuilders
        .stats(AGGREGATION_NAME)
        .field(AGG_FIELD_NAME);
  • 기본 집계의 결과와 해당 문서의 개수까지 한 번에 볼 수 있는 집계

Extened Stats Aggregation

String AGGREGATION_NAME = "extendedStatsMovieId";
String AGG_FIELD_NAME   = "movieId";

// Extended Stats Aggregation 생성
ExtendedStatsAggregationBuilder aggregation = AggregationBuilders
        .extendedStats(AGGREGATION_NAME)
        .field(AGG_FIELD_NAME);
  • stats Aggregation의 확장 버전으로 stats에서 제공했던 것 외의 제곱의 합, 분산확률, 표준 편차, 표준편차 범위 정보를 추가 제공

Sub Aggregation

String INDEX_NAME                     = "movie_auto_java";
String TYPE_NAME                      = "_doc";

// 사용자 기준 Terms Aggregation
String AGGREGATION_NAME_FOR_USER      = "repNationNm";
String AGGREGATION_FIELD_FOR_USER     = "repNationNm";

// 팔로워 기준 Stats Sub-Aggregation
String AGGREGATION_NAME_FOR_FOLLOWER  = "typeNm";
String AGGREGATION_FIELD_FOR_FOLLOWER = "typeNm";

// Stats Sub-Aggregation 생성
StatsAggregationBuilder subAggs = AggregationBuilders
        .stats(AGGREGATION_NAME_FOR_FOLLOWER)
        .field(AGGREGATION_FIELD_FOR_FOLLOWER);

// Terms Aggregation 생성 + Sub-Aggregation 추가
AggregationBuilder aggs = AggregationBuilders
        .terms(AGGREGATION_NAME_FOR_USER)
        .field(AGGREGATION_FIELD_FOR_USER)
        .size(1000)
        .subAggregation(subAggs);
  • 다수의 집계 연산을 중첩할 수 있다.

QueryDsl API 사용하기

match All Query

아무 조건 없이 전체 대상으로 질의하고 싶을 때 호출하는 쿼리

String INDEX_NAME = "movie_search";

// Match All 쿼리
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();

// 최초 검색 실행 (Scroll 시작)
SearchResponse scrollResp = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)                      // 한 번에 가져올 문서 수
        .setScroll(new TimeValue(60000))  // Scroll 타임아웃 60초
        .get();
  • QueryBuilders의 matchAllQuery()에 사용되며, 질의할 문장을 setQuery에 담아 엘라스틱 쿼리로 전송한다.

Full text Queries

루씬에서 제공하는 쿼리문법을 활용해 검색을 질의할 때 사용

1) match Query

String INDEX_NAME = "movie_search";
String FIELD_NAME = "movieNm";
String QUERY       = "아이";  // 검색할 키워드

// Match Query 생성
QueryBuilder queryBuilder = QueryBuilders.matchQuery(FIELD_NAME, QUERY);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)  // 한 번에 가져올 문서 수
        .get();
  • 기본적인 형태소 분석을 통해 검색어를 분석해서 질의를 수행
  • 분리된 Term 간에 And, Or 연산을 선택해서 질의 가능

2) Common Terms Query

String INDEX_NAME     = "movie_search";
String FIELD_NAME     = "movieNm";
String QUERY_KEYWORD  = "Family"; // 검색 키워드

// Common Terms Query 생성
QueryBuilder queryBuilder = QueryBuilders.commonTermsQuery(FIELD_NAME, QUERY_KEYWORD);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30) // 한 번에 가져올 문서 수
        .get();
  • 많이 검색되는 단어와 적게 검색되는 단어 중 어떤 단어가 중요한지를 판단해서 검색 스코어링을 변경하는 알고리즘을 가지는 쿼리

3) Query String Query

String INDEX_NAME       = "movie_search";
String FIELD_NAME       = "movieNm";
String QUERY_KEYWORD    = "버블 OR 하이";  // 검색 키워드

// Query String Query 생성
QueryBuilder queryBuilder = QueryBuilders
        .queryStringQuery(QUERY_KEYWORD)
        .field(FIELD_NAME);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();


// boolean 연산자 포함 예제
QUERY_KEYWORD = "+버블 -하이";  // 포함/제외 조건
queryBuilder = QueryBuilders
        .queryStringQuery(QUERY_KEYWORD)
        .field(FIELD_NAME);

response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();
  • 검색할 때 연산자를 따로 지정하는 것이 아닌 쿼리문 자체에 And, Or 절을 사용해 질의하고 싶을 때 사용한다.
  • 특정 키워드를 필수 조건으로 넣거나 빼기 위해 사용

Term level Queries

특정 문자의 일부만 알고 있거나 문자가 아닌 숫자의 범위 혹은 _id 값으로 조회하고 싶을 때 사용하는 API

1) Term Query

String INDEX_NAME     = "movie_search";
String FIELD_NAME     = "repNationNm";
String QUERY_KEYWORD  = "한국";  // 검색 키워드

// Term Query 생성
QueryBuilder queryBuilder = QueryBuilders.termQuery(FIELD_NAME, QUERY_KEYWORD);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)  // 필요 시 한 번에 가져올 문서 수
        .get();
  • 지정된 필드에 정확한 텀이 들어있는 문서를 찾을 때 사용한다.
    • Keyword 타입으로 설정된 필드에서 문서를 찾을 때 사용

2) WildCard Query

String INDEX_NAME      = "movie_search";
String FIELD_NAME      = "movieNmEn";
String QUERY_KEYWORD   = "F?m*";  // 와일드카드 검색 키워드

// Wildcard Query 생성
QueryBuilder queryBuilder = QueryBuilders.wildcardQuery(FIELD_NAME, QUERY_KEYWORD);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)  // 한 번에 가져올 문서 수
        .get();
  • 특정 단어에 대한 정규 표현식을 이용해 전체 문서를 대상으로 조회하고 싶을 때 사용한다.

Compound Queries

특정 쿼리와 다른 다수의 쿼리를 조합해서 사용하는 API를 의미

1) Bool Query

String INDEX_NAME                   = "movie_search";

// MUST 조건
String FIELD_NAME_MUST              = "repNationNm";
String QUERY_KEYWORD_MUST           = "한국";

// MUST_NOT 조건
String FIELD_NAME_MUST_NOT          = "repGenreNm";
String QUERY_KEYWORD_MUST_NOT       = "드라마";

// SHOULD 조건
String FIELD_NAME_SHOULD            = "prdtYear";
String QUERY_KEYWORD_SHOULD         = "2017";

// FILTER 조건
String FIELD_NAME_FILTER            = "movied";
String QUERY_KEYWORD_FILTER         = "20173732";

// Bool Query 생성
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
        .must(QueryBuilders.termQuery(FIELD_NAME_MUST, QUERY_KEYWORD_MUST))
        .mustNot(QueryBuilders.termQuery(FIELD_NAME_MUST_NOT, QUERY_KEYWORD_MUST_NOT))
        .should(QueryBuilders.termQuery(FIELD_NAME_SHOULD, QUERY_KEYWORD_SHOULD))
        .filter(QueryBuilders.termQuery(FIELD_NAME_FILTER, QUERY_KEYWORD_FILTER));

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();
  • 특정 필드에서 질의문을 넣거나 뺄 때 사용하는 API(여러 개 쿼리 조합 가능)
  • must, mustNot, should, filter를 조합해서 쿼리 구성할 수 있다.

2) Dismax Query

String INDEX_NAME       = "movie_search";

// DisMax Query에 사용할 필드와 키워드
String FIELD_NAME1      = "repNationNm";
String QUERY_KEYWORD1   = "한국";

String FIELD_NAME2      = "repGenreNm";
String QUERY_KEYWORD2   = "드라마";

// DisMax Query 생성
QueryBuilder queryBuilder = QueryBuilders.disMaxQuery()
        .add(QueryBuilders.termQuery(FIELD_NAME1, QUERY_KEYWORD1))
        .add(QueryBuilders.termQuery(FIELD_NAME2, QUERY_KEYWORD2))
        .boost(1.5f)
        .tieBreaker(0.5f);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();
  • tie_breaker를 사용해 스코어를 조정할 수 있는 API
    • 가장 많은 점수를 받은 필드를 제외 나머지 필드의 점수를 합산한 후 tie_breaker의 값으로 나눠서 스코어를 재계산

Geo Queries

좌푯값으로 특정 범위 내에 있는 문서를 검색할 때 사용하는 API

1) Geo_Shape Query

String INDEX_NAME  = "movie_search_datatype";
String FIELD_NAME  = "filmLocation";

// Geo 좌표 생성
CoordinatesBuilder coordinatesBuilder = new CoordinatesBuilder()
        .coordinate(0, 0)
        .coordinate(0, 10)
        .coordinate(10, 10)
        .coordinate(10, 0)
        .coordinate(0, 0);

// MultiPoint 도형 생성
MultiPointBuilder multiPoint = new MultiPointBuilder(coordinatesBuilder.build());

// GeoShape Query 생성 (WITHIN 관계)
GeoShapeQueryBuilder queryBuilder = QueryBuilders.geoShapeQuery(FIELD_NAME, multiPoint)
        .relation(ShapeRelation.WITHIN);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();
  • 지정된 지형과 교차하거나 포함된 지점의 문서를 찾는다.
  • API를 사용하기 위해서는 spatial4j와 jts 라이브러리가 필요

2) Geo Bounding Box Query

String INDEX_NAME  = "movie_search_datatype";
String FIELD_NAME  = "filmLocation";

// Geo Bounding Box Query 생성
QueryBuilder queryBuilder = QueryBuilders.geoBoundingBoxQuery(FIELD_NAME)
        .setCorners(40.73, -74.1, 40.717, -73.99);  // topLeft(lat, lon), bottomRight(lat, lon)

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();
  • 지리적 포인트가 지정한 사각형에 포함되는 문서를 찾을 때 사용하는 API

3) Geo Distance Query

String INDEX_NAME  = "movie_search_datatype";
String FIELD_NAME  = "filmLocation";

// GeoDistance Query 생성
QueryBuilder queryBuilder = QueryBuilders.geoDistanceQuery(FIELD_NAME)
        .point(40, -70)                    // 기준 좌표 (위도, 경도)
        .distance(3, DistanceUnit.KILOMETERS);  // 반경 3km

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();
  • 특정 문서를 중심으로 일정 거리 내에 있는 지리적 정보가 있는 문서를 찾는 API

4) Geo Polygon Query

String INDEX_NAME = "movie_search_datatype";
String FIELD_NAME = "filmLocation";

// Polygon 좌표 생성
List<GeoPoint> points = new ArrayList<>();
points.add(new GeoPoint(40, -70));
points.add(new GeoPoint(30, -80));
points.add(new GeoPoint(20, -90));

// GeoPolygon Query 생성
QueryBuilder queryBuilder = QueryBuilders.geoPolygonQuery(FIELD_NAME, points);

// 검색 실행
SearchResponse response = client.prepareSearch(INDEX_NAME)
        .setQuery(queryBuilder)
        .setSize(30)
        .get();
  • 지정한 다각형 내의 지리적 포인트가 있는 문서를 찾는 API
728x90