Search
🙉

엘라스틱서치 실무 가이드 2

Intro::

엘라스틱서치 실무 가이드 4장 정리본 입니다.

검색 API

문장은 색인 시점에 텀으로 분해된다. 검색 시에는 이 텀을 일치시켜야 검색이 가능해진다. 엘라스틱서치는 색인 시점에 Analyzer를 통해 분석된 텀을 Term, 출현빈도, 문서번호와 같이 역색인 구조로 만들어 내부적으로 저장한다. 검색 시점에는 Keyword 타입과 같은 분석이 불가능한 데이터와 Text 타입과 같은 분석이 가능한 데이터를 구분해서 분석이 가능할 경우 분석기를 이용해 분석을 수행한다. 이를 통해 검색 시점에도 텀을 얻을 수 있으며, 해당 텀으로 역색인 구조를 이용해 문서를 찾고 이를 통해 스코어를 계산해서 결과로 제공한다.

URI 검색

GET movie_search/_search?q=prdtYear:2018
Plain Text
복사
쿼리가 간단하다.
복잡한 질의는 불가능하다는 단점이 있다

URI 검색에서 자주 사용하는 파라미터

파라미터
기본값
설명
q
-
검색을 수행할 쿼리 문자열 조건을 지정한다.
df
-
쿼리에 검색을 수행할 필드가 지정되지 않았을 경우 기본값으로 검색한 필드를 지정한다.
analyzer
검색 대상 필드에 설정된형태소 분석기
쿼리 문자열을 형태소 분석할 때 사용할 형태소 분석기를 지정한다.
analyzer_wildcard
false
접두어/와일드카드(*) 검색 활성화 여부를 지정한다.
default_operator
OR
두 개 이상의 검색 조건이 쿼리 문자열에 포함된 경우 검색 조건 연산자를 설정한다.
_source
true
검색 결과에 문서 본문 포함 여부를 지정한다.
sort
-
검색 결과의 정렬 기준 필드를 지정한다.
from
-
검색을 시작할 문서의 위치를 설정한다.
size
-
반환할 검색 결과 개수를 설정한다.

Request Body 검색

POST movie_search/_search { "query": { "query_string": { "default_field": "movieNmEn", "query": "movieNmEn:* OR prdtYear:2017" } }, "from": 0, "size": 5, "sort": [ { "_score": { "order": "desc" }, "movieCd": { "order": "asc" } } ], "_source": [ "movieCd", "movieNm", "movieNmEn", "typeNm" ] }
Plain Text
복사

Query DSL 이해하기

요청

{ "size": -- 1 "from": -- 2 "timeout": -- 3 "_source": {...} -- 4 "query": {...} -- 5 "aggs": {...} -- 6 "sort": {...} -- 7 }
JSON
복사
1.
리턴받는 결과의 개수를 지정한다. 기본값은 10이다.
2.
몇 번째 문서부터 가져올지 지정한다. 기본값은 0 이며, 페이지별로 구성하려면 다음 번 문서는 10번째부터 가지고 오면 된다.
3.
검색을 요청해서 결과를 받는 데까지 걸리는 시간을 나타낸다. timeout 시간을 너무 짧게 잡으면 전체 샤드에서 timeout을 넘기지 않은 문서만 결과로 출력되기 때문에 상황에 따라 결과의 일부만 나올 수 있다. 기본값은 무한대이다.
4.
검색 시 필요한 필드만 출력하고 싶을 때 사용한다.
5.
검색 조건문이 들어가야 하는 공간이다.
6.
통계 및 집계 데이터를 사용할 때 사용하는 공간이다.
7.
문서 결과를 어떻게 출력할지에 대한 조건을 사용하는 공간이다.

응답

{ "took": -- 1 "timed_out": -- 2 "_shards": { "total": -- 3 "successful": -- 4 "failed": -- 5 }, "hits": { "total": -- 6 "max_score": -- 7 "hits": [...] -- 8 } }
JSON
복사
1.
쿼리를 실행한 시간을 나타낸다.
2.
쿼리 시간이 초과할 경우를 나타낸다.
3.
쿼리를 요청한 전체 샤드의 개수를 나타낸다.
4.
검색 요청에 성공적으로 응답한 샤드의 개수를 나타낸다.
5.
검색 요청에 실패한 샤드의 개수를 나타낸다.
6.
검색어에 매칭된 문서의 전체 개수를 나타낸다.
7.
일치하는 문서의 스코어 값 중 가장 높은 값을 출력한다.
8.
각 문서 정보와 스코어 값을 보여준다.

Query DSL 쿼리와 필터

쿼리 컨텍스트

문서가 쿼리와 얼마나 유사한지를 스코어로 계산한다.
질의가 요청될 때마다 엘라스틱서치에서 내부의 루씬을 이용해 계산을 수행한다(이때 결과 캐싱 X)
일반적으로 전문 검색에 많이 사용한다.
캐싱되지 않고 디스크 연산을 수행하기 때문에 상대적으로 느리다.
POST movie_search/_search { "query": { "match": { "movieNm": "기묘한 가족" } } }
JSON
복사

필터 컨텍스트

쿼리의 조건과 문서가 일치하는지(Y/N)을 구분한다.
별도로 스코어를 계산하지 않고 단순 매칭 여부를 검사한다.
자주 사용되는 필터의 결과는 엘라스틱서치가 내부적으로 캐싱한다.
기본적으로 메모리 연산을 수행하기 때문에 상대적으로 빠르다.
POST movie_search/_search { "query": { "bool": { "must": [ { "match_all": {} } ], "filter": [ { "term": { "repGenreNm": "다큐멘터리" } } ] } } }
JSON
복사

Query DSL의 주요 파라미터

Multi Index 검색

기본적으로 모든 검색 요청은 Multi Index 및 Multi Type 검색이 가능하다.
POST movie_search,movie_auto/_search { "query": { "term": { "repGenreNm": "다큐멘터리" } } } POST /log-2019-*/_search
JSON
복사

쿼리 결과 페이징

POST movie_search/_search { "from": 0,// 이후에 size만큼 키워가면 된다. "size": 5, "query": { "term": { "repNationNm": "한국" } } }
JSON
복사
엘라스틱서치는 관계형 데이터베이스와 다르게 페이징된 해당 문서만 선택적으로 가져오는 것이 아니라 모든 데이터를 읽게 된다. 때문에 페이지 번호가 높아질수록 쿼리 비용이 덩달아 높아질 수밖에 없다는 점에 주의해야 한다.

쿼리 결과 정렬

POST movie_search/_search { "query": { "term": { "repNationNm": "한국" } }, "sort": { "prdtYear": { "order": "asc" }, "_score": { "order": "desc" } } }
JSON
복사

_source 필드 필터링

검색 결과에서 실제 데이터는 _source 항목 아래에 존재하며 문서 내부에 존재하는 각 필드가 모두 결과로 제공된다. 하지만 항상 모든 필드를 볼 필요는 없으며, 필요에 따라 특정 필드를 검색 결과에서 제거하고 싶을 때도 있을 것이다.
검색 결과에서 특정 칼럼을 제외하면 네트워크 사용량을 줄여 응답 속도도 빠르게 할 수 있다.
POST movie_search/_search { "_source": [ "movieNm" ], "query": { "term": { "repNationNm": "한국" } } }
JSON
복사

범위 검색

문법
연산자
lt
<
gt
>
lte
gte
POST movie_search/_search { "query": { "range": { "prdtYear": { "gte": "2016", "lte": "2017" } } } }
JSON
복사

operator 설정

엘라스틱서치는 검색 시 문장이 들어올 경우 기본적으로 OR 연산으로 동작한다. Query DSL에서는 operator 파라미터를 통해 연산자를 명시적으로 지정하는 것이 가능하다.
POST movie_search/_search { "query": { "match": { "movieNm": { "query": "자전차왕 엄복동", "operator": "and" } } } }
JSON
복사

minimum_should_match 설정

OR연산을 수행할 경우 검색 결과가 너무 많아질 수 있다. 이 경우 텀의 개수가 몇 개 이상 매칭될 때만 검색 결과로 나올게 할 수 있는데 이때 사용하는 파라미터가 minimum_should_match이다.
POST movie_search/_search { "query": { "match": { "movieNm": { "query": "반지의 제왕", "minimum_should_match": 2 } } } }
JSON
복사

fuzziness 설정

단순히 같은 값을 찾는 Match Query를 유사한 값을 찾는 Fuzzy Query로 변경할 수 있다. 오차범위를 통해 잘못 입력된 경우에도 검색이 가능해진다. 한국어에는 적용이 어렵지만 알파벳에 유용하다.
POST movie_search/_search { "query": { "match": { "movieNmEn": { "query": "Fli High", "fuzziness": 1 } } } }
JSON
복사

boost 설정

검색에서 가장 많이 사용하는 파라미터 중 하나다. 이 파라마터는 관련성이 높은 필드나 키워드에 가중치를 더 줄 수 있게 해준다.
POST movie_search/_search { "query": { "multi_match": { "query": "Fly", "fields": ["movieNm^3","movieNmEn"]//movieNm에 가중치 3배 } } }
JSON
복사

Query DSL의 주요 쿼리

Match All Query

색인에 모든 문서를 검색하는 쿼리다.
POST movie_search/_search { "query": { "match_all": {} } }
JSON
복사

Match Query

텍스트, 숫자, 날짜 등이 포함된 문장을 형태소 분석을 통해 텀으로 분리한 후 이 텀들을 이용해 검색 질의를 수행한다. 즉, 검색어가 분석돼야 할 경우에 사용해야 한다.
POST movie_search/_search { "query": { "match": { "movieNm": "그대 장미" } } }
JSON
복사

Multi Match Query

단일 필드가 아닌 여러 개의 필드를 대상으로 검색해야 할 때 사용하는 쿼리다.
POST movie_search/_search { "query": { "multi_match": { "query": "가족", "fields": ["movieNm", "movieNmEn"] } } }
JSON
복사

Term Query

타입
설명
Text 데이터 타입
필드에 데이터가 저장되기 전에 데이터가 분석되어 역색인 구조로 저장된다.
Keyword 데이터 타입
데이터가 분석되지 않고 그대로 필드에 저장된다.
데이터를 분석하지 않고 그대로 검색할 때 사용해야하는 쿼리이다.
POST movie_search/_search { "query": { "term": { "genreAlt": "코미디" } } }
JSON
복사

Bool Query

엘라스틱서치에서는 하나의 쿼리나 여러 개의 쿼리를 조합해서 더 높은 스코어를 가진 쿼리 조건으로 검색을 수행할 수 있다.
Elasticsearch
SQL
설명
must : [필드]
AND 칼럼 = 조건
반드시 조건에 만족하는 문서만 검색된다.
must_not : [필드]
AND 칼럼 != 조건
조건을 만족하지 않는 문서가 검색된다.
should : [필드]
OR 칼럼 = 조건
여러 조건 중 하나 이상을 만족하는 문서가 검색된다.
filter : [필드]
칼럼 IN ( 조건 )
조건을 포함하고 있는 문서를 출력한다. 해당 파라미터를 사용하면 스코어별로 정렬되지는 않는다.
POST movie_search/_search { "query": { "bool": { "must": [ { "term": { "repGenreNm": "코미디" } }, { "match": { "repNationNm": "한국" } } ], "must_not": [ { "match": { "typeNm": "단편" } } ] } } }
JSON
복사

Query String

엘라스틱서치의 기본적으로 내장된 쿼리 분석기이다. 분석된 텀을 대상으로 AND 조건과 만족하는 문서를 찾아 돌려준다. 기존 텀 쿼리와는 다르게 공백은 연산자로 사용되지 않으며 입력된 텍스트 그대로 형태소 분석기에 전달된다는 점이다.
POST movie_search/_search { "query": { "query_string": { "default_field": "movieNm", "query": "(가정) AND (어린이날)" } } }
JSON
복사

Prefix Query

해당 접두어가 있는 모든 문서를 검색하는 데 사용한다. 역색인된 텀은 사전순으로 정렬되고 Prefix Query는 저장된 텀들을스캔해서 일치하는 텀을 찾는다.
POST movie_search/_search { "query": { "prefix": { "movieNm": { "value": "자전차" } } } }
JSON
복사

Exists Query

문서를 색인할 때 필드의 값이 없다면 필드를 생성하지 않거나 필드의 값을 null로 설정할 때가 있다. 이러한 데이터를 제외하고 실제 값이 존재하는 문서만 찾고 싶다면 해당 쿼리를 사용하면 된다.
POST movie_search/_search { "query": { "exists": { "field": "movieNm" } } }
JSON
복사

Wildcard Query

검색어가 와일드 카드와 일치하는 구문을 찾습니다. 이때 입력된 검색어는 형태소 분석이 이뤄지지 않습니다.
와일드카드 옵션
설명
*
문자의 길이와 상관없이 와일드카드와 일치하는 모든 문서를 찾는다.
?
지정된 위치의 한 글자가 다른 경우의 문서를 찾는다.
와일드 카드의 경우 단어의 첫 글자로는 절대 사용해서는 안된다. 첫 글자로 와일드 카드가 사용될 경우 색인된 전체 문서를 찾아야 하는 불상사가 발생한다.
POST movie_search/_search { "query": { "wildcard": { "typeNm": { "value": "장?" } } } }
JSON
복사

Nested Query

비슷하게 설명하자면 분산 데이터 환경에서 SQL 조인과 유사한 기능을 수행하는 쿼리라고 생각하면 된다. Nested Query는 Nested 데이터 타입의 필드를 검색할 때 사용한다.
// 인덱스 생성 PUT movie_nested { "settings": { "number_of_shards": 1, "number_of_replicas": 1 }, "mappings": { "properties": { "repGenreNm": { "type": "keyword" }, "companies": { "type": "nested", "properties": { "companyCd": { "type": "keyword" }, "companyNm": { "type": "keyword" } } } } } } // 도큐먼트 추가 PUT movie_nested/_doc/1 { "movieCd": "20184623", "movieNm": "바람난 아내들2", "movieEn": "", "prdtYear": "2018", "openDt": "", "typeNm": "장편", "prdtStatNm": "개봉예정", "nationAlt": "한국", "genreAlt": "멜로/로맨스", "repNationNm": "한국", "repGenreNm": "멜로/로맨스", "companies": [ { "companyCd": "20173401", "companyNm": "(주)케이피에이기획획" } ] } // Nested Query GET movie_nested/_search { "query": { "bool": { "must": [ { "term": { "repGenreNm": { "value": "멜로/로맨스" } } }, { "nested": { "path": "companies", "query": { "bool": { "must": [ { "term": { "companies.companyCd": { "value": "20173401" } } } ] } } } } ] } } }
JSON
복사

부가적인 검색 API

엘라스틱서치에서 제공되는 검색 API에는 부가적인 기능들을 제공하고, 이를 이용해 검색 결과의 카운트만 요청한다거나 검색을 요청하기 전에 쿼리의 문법 오류를 미리 체크해 볼 수도 있다.

동적 분배 방식의 샤드 선택

검색 시 모든 샤드에서 검색을 수행하게 된다면 사용자에게 중복된 결과를 전달할 수 도 있을 것이다. 이러한 문제를 방지하기 위해 엘라스틱서치는 검색을 수행할 때 동일 데이터를 가지고 있는 샤드 중 하나만 선택해 검색을 수행한다.
특별한 설정을 하지 않는다면 검색 요청의 적절한 분배를 위해 기본적으로 라운드로빈 방식의 알고리즘을 사용한다. 라운드로빈은 순차적으로 샤드를 선택하는 방식이다. 엘라스틱서치에서는 동적 분배 방식으로 검색 요청의 응답시간, 검색 요청을 수행하는 스레드 풀의 크기 등을 고려해 최적의 샤드를 동적으로 결정해준다.
PUT _cluster/settings { "transient": { "cluster": { "routing": { "use_adaptive_replica_selection": true } } } }
JSON
복사

글로벌 타임아웃 설정

개별 검색 요청의 경우에 Requeset Body에 직접 타임아웃을 설정할 수 있다. 하지만 이러한 방식은 많은 불편을 초래하기 때문에 모든 검색 쿼리에 동일하게 적용되도록 정책을 설정하는 것이 좋다.
기본적으로 무제한이다.
PUT _cluster/settings { "transient": { "search.default_search_timeout": "1s" } }
JSON
복사

Search Shards API

검색이 수해오디는 노드 및 샤드에 대한 정보를 확인할 수 있다. 이러한 정보는 질의를 최적화하거나 질의가 정상적으로 수행되지 않을 때 문제를 해결하는 데 유용하게 활용할 수 있다.
POST movie_search/_search_shards
JSON
복사

Multi Search API

여러건의 검색 요청을 통합해서 한번에 요청하고 결과를 종합해서 받을 때 사용되는 API이다.
POST _msearch {"index":"movie_auto"} {"query":{"match_all":{}},"from":0,"size":10} {"index":"movie_search"} {"query":{"match_all":{}},"from":0,"size":10}
JSON
복사

Count API

문서의 본문보다 검색된 문서의 개수가 몇 개인지 숫자가 필요한 경우 사용하는 API이다.
POST movie_search/_count { "query": { "match": { "prdtYear": "2017" } } }
JSON
복사

Validate API

Validate API를 사용하면 쿼리를 실행하기에 앞서 쿼리가 유효하게 작성됐는지 검증하는 것이 가능하다.
POST movie_search/_validate/query { "query": { "match": { "prdtYear": "2017" } } }
JSON
복사
만약 쿼리가 왜 실패했는지 자세한 정보가 필요한 경우 요청 URL 파라미터로 rewrite=true 파라미터를 추가해주면 된다.
POST movie_search/_validate/query?rewrite=true { "query": { "match": { "prdtYear": "2017-10-10" } } } // 응답 { "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "valid" : false, "explanations" : [ { "index" : "movie_search", "valid" : false, "error" : "[movie_search/kAm51CDkRHmWGORnRtu1Sw] QueryShardException[failed to create query: For input string: \"2017-10-10\"]; nested: NumberFormatException[For input string: \"2017-10-10\"];; java.lang.NumberFormatException: For input string: \"2017-10-10\"" } ] }
JSON
복사

Explain API

특정 문서에 대해 요청한 쿼리가 어떻게 스코어가 계산이 되는지 확인할 수 있는 API이다.
// 검색 POST movie_search/_search { "query": { "term": { "prdtYear": { "value": "2017" } } } } // 특정 id의 스코어 값 계산 방식 확인 POST movie_search/_doc/eDzJqmkBjjM-ebDb8PsR/_explain { "query": { "term": { "prdtYear": { "value": "2017" } } } }
JSON
복사

Profile API

쿼리에 대한 상세한 수행 계획별로 수행된 시간을 돌려주므로 성능을 튜닝하거나 디버깅할 때 유용하게 활용할 수 있다.
POST movie_search/_search { "profile": true, "query": { "match_all": {} } }
JSON
복사
응답 결과 중 profile 부분을 눈여겨서 확인하면 된다.

References::