_search API
원본 보기_search API
이 페이지는 Query DSL 구문을 사용하는 _search API를 다룹니다. 검색 사용 사례에 쓸 수 있는 다른 Elastic 쿼리 구문에 대한 개요는 검색 쿼리 작성하기를 참조하세요.
검색은 하나 이상의 쿼리를 조합해 Elasticsearch로 보내는 것을 말합니다. 검색의 쿼리와 일치하는 문서는 응답의 hits, 즉 검색 결과로 반환됩니다.
검색에는 쿼리를 더 잘 처리하기 위한 추가 정보가 포함될 수도 있습니다. 예를 들어 검색을 특정 인덱스로 한정하거나 지정한 개수의 결과만 반환하도록 할 수 있습니다.
검색 API를 사용해 Elasticsearch 데이터 스트림이나 인덱스에 저장된 데이터를 검색하고 집계할 수 있습니다. 이 API의 query 요청 본문 파라미터는 Query DSL로 작성한 쿼리를 받습니다.
다음 요청은 match 쿼리를 사용해 my-index-000001을 검색합니다. 이 쿼리는 user.id 값이 kimchy인 문서와 일치합니다.
GET /my-index-000001/_search
{
"query": {
"match": {
"user.id": "kimchy"
}
}
}
API 응답은 쿼리와 일치하는 상위 10개 문서를 hits.hits 속성에 담아 반환합니다.
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.3862942,
"hits": [
{
"_index": "my-index-000001",
"_id": "kxWFcnMByiguvud1Z8vC",
"_score": 1.3862942,
"_source": {
"@timestamp": "2099-11-15T14:12:12",
"http": {
"request": {
"method": "get"
},
"response": {
"bytes": 1070000,
"status_code": 200
},
"version": "1.1"
},
"message": "GET /search HTTP/1.1 200 1070000",
"source": {
"ip": "127.0.0.1"
},
"user": {
"id": "kimchy"
}
}
}
]
}
}
다음 옵션을 사용해 검색을 원하는 대로 조정할 수 있습니다.
Query DSL
Query DSL은 원하는 결과를 얻기 위해 자유롭게 조합할 수 있는 다양한 쿼리 유형을 지원합니다. 쿼리 유형은 다음과 같습니다.
- 불리언 쿼리를 비롯한 복합 쿼리: 여러 쿼리를 결합해 다중 조건을 기준으로 결과를 매칭할 수 있습니다
- 필터링과 정확한 일치 검색을 위한 텀 수준 쿼리
- 검색 엔진에서 흔히 사용되는 전문 검색 쿼리
- 지리 및 공간 쿼리
집계
검색 집계를 사용하면 검색 결과에 대한 통계와 기타 분석 정보를 얻을 수 있습니다. 집계는 다음과 같은 질문에 답하는 데 도움이 됩니다.
- 내 서버들의 평균 응답 시간은 얼마인가?
- 내 네트워크에서 사용자가 가장 많이 접속한 IP 주소는 무엇인가?
- 고객별 총 거래 매출은 얼마인가?
여러 데이터 스트림과 인덱스 검색
쉼표로 구분한 값과 grep 형태의 인덱스 패턴을 사용해 하나의 요청으로 여러 데이터 스트림과 인덱스를 검색할 수 있습니다. 특정 인덱스의 검색 결과에 가중치를 부여할 수도 있습니다. 쿼리로 여러 데이터 스트림과 인덱스 검색하기를 참조하세요.
검색 결과 페이지네이션
기본적으로 검색은 상위 10개 일치 결과만 반환합니다. 더 많거나 적은 문서를 가져오려면 검색 결과 페이지네이션을 참조하세요.
선택한 필드만 가져오기
검색 응답의 hits.hits 속성에는 각 히트의 전체 문서 _source가 포함됩니다. _source의 일부나 다른 필드만 가져오려면 선택한 필드 가져오기를 참조하세요.
검색 결과 정렬
기본적으로 검색 히트는 각 문서가 쿼리와 얼마나 잘 일치하는지를 나타내는 관련성 점수인 _score를 기준으로 정렬됩니다. 이 점수의 계산 방식을 조정하려면 script_score 쿼리를 사용하세요. 다른 필드 값으로 검색 히트를 정렬하려면 검색 결과 정렬을 참조하세요.
비동기 검색 실행
Elasticsearch 검색은 대용량 데이터에서도 빠르게 실행되도록 설계되어 있으며, 보통 밀리초 단위로 결과를 반환합니다. 이런 이유로 검색은 기본적으로 동기 방식입니다. 검색 요청은 완전한 결과가 나올 때까지 기다린 후 응답을 반환합니다.
하지만 대규모 데이터셋이나 여러 클러스터를 대상으로 하는 검색은 완전한 결과를 얻는 데 더 오래 걸릴 수 있습니다.
긴 대기를 피하려면 대신 비동기(async) 검색을 실행할 수 있습니다. 비동기 검색을 사용하면 오래 걸리는 검색의 부분 결과를 지금 받아보고 완전한 결과는 나중에 확인할 수 있습니다.
데이터를 색인한 뒤 검색하는 대신, 검색 쿼리의 일부로만 존재하는 런타임 필드를 정의할 수 있습니다. 검색 요청에 runtime_mappings 섹션을 지정해 런타임 필드를 정의하며, 선택적으로 Painless 스크립트를 포함할 수 있습니다.
예를 들어 다음 쿼리는 day_of_week라는 런타임 필드를 정의합니다. 포함된 스크립트는 @timestamp 필드 값을 기준으로 요일을 계산하고 emit을 사용해 계산된 값을 반환합니다.
이 쿼리에는 day_of_week에 대해 동작하는 텀 집계도 포함되어 있습니다.
GET /my-index-000001/_search
{
"runtime_mappings": {
"day_of_week": {
"type": "keyword",
"script": {
"source":
"""emit(doc['@timestamp'].value.dayOfWeekEnum
.getDisplayName(TextStyle.FULL, Locale.ENGLISH))"""
}
}
},
"aggs": {
"day_of_week": {
"terms": {
"field": "day_of_week"
}
}
}
}
응답에는 day_of_week 런타임 필드를 기반으로 한 집계가 포함됩니다. buckets 아래에 값이 Sunday인 key가 있습니다. 이 값은 필드를 전혀 색인하지 않고도 day_of_week 런타임 필드에 정의된 스크립트를 기반으로 쿼리가 동적으로 계산한 것입니다.
{
...
***
"aggregations" : {
"day_of_week" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Sunday",
"doc_count" : 5
}
]
}
}
}
기본적으로 검색 요청에는 타임아웃이 없습니다. 요청은 모든 샤드에서 완전한 결과가 올 때까지 기다린 후 응답을 반환합니다.
샤드별로 적용되는 timeout 값을 설정할 수 있으며, 해당 샤드에서 쿼리 단계가 시작되는 시점부터 계산됩니다. 이는 읽기 모델의 다른 부분에 대해 검색 전체 수준의 타임아웃을 강제하지는 않습니다. 어떤 샤드에서 타임아웃 값을 초과하면 부분 결과를 반환하고 검색 응답에 "timed_out": true가 표시됩니다.
{
"took" : 11,
"timed_out" : true,
"_shards" : {
"total" : 40,
"successful" : 40,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 98393,
"relation" : "eq"
},
// ...
}
}
- 불완전할 수 있는 값
특정 검색 요청이 부분 결과를 반환하는 대신 오류를 내야 한다면 default_allow_partial_results 설정을 false로 지정하는 것을 고려하세요.
search.default_search_timeout 클러스터 설정을 구성해 모든 검색 요청에 적용되는 클러스터 전역 기본 타임아웃을 대체 값으로 지정할 수 있습니다. 이 경우 요청은 태스크 취소 API를 통해 취소됩니다.
search.default_search_timeout 설정의 해상도 민감도는 thread_pool.estimated_time_interval 설정에 의해 결정되며, 기본값은 200ms입니다. 즉 search.default_search_timeout이 의미 있는 영향을 주는 최소 임계값도 200ms가 됩니다. 이 전문가용 설정은 영향 범위가 매우 넓으므로 Elastic은 값을 재정의하지 않을 것을 권장합니다.
태스크 관리 API를 사용해 검색 요청을 취소할 수 있습니다. Elasticsearch는 클라이언트의 HTTP 연결이 닫히면 검색 요청을 자동으로 취소하기도 합니다. 검색 요청이 중단되거나 타임아웃될 때 HTTP 연결을 닫도록 클라이언트를 설정하는 것을 권장합니다.
일반적으로 전체 히트 수는 모든 일치 문서를 확인하지 않고서는 정확히 계산할 수 없으며, 많은 문서와 일치하는 쿼리에서는 이 작업의 비용이 큽니다. track_total_hits 파라미터를 사용하면 전체 히트 수를 어떻게 추적할지 제어할 수 있습니다. "최소 10000건의 히트가 있다"처럼 히트 수의 하한만 알아도 충분한 경우가 많기 때문에 기본값은 10,000으로 설정되어 있습니다. 즉 요청은 10,000건까지는 전체 히트 수를 정확히 계산합니다. 일정 임계값 이상에서 정확한 히트 수가 필요하지 않다면 검색 속도를 높이는 좋은 절충안입니다.
true로 설정하면 검색 응답은 항상 쿼리와 일치하는 히트 수를 정확히 추적합니다(예: track_total_hits가 true일 때 total.relation은 항상 "eq"가 됩니다). 그렇지 않은 경우 검색 응답의 "total" 객체에 반환되는 "total.relation"이 "total.value"를 어떻게 해석해야 하는지 결정합니다. "gte" 값은 "total.value"가 쿼리와 일치하는 전체 히트 수의 하한임을 의미하고, "eq" 값은 "total.value"가 정확한 개수임을 나타냅니다.
GET my-index-000001/_search
{
"track_total_hits": true,
"query": {
"match" : {
"user.id" : "elkbee"
}
}
}
... 다음을 반환합니다.
{
"_shards": ...
"timed_out": false,
"took": 100,
"hits": {
"max_score": 1.0,
"total" : {
"value": 2048,
"relation": "eq"
},
"hits": ...
}
}
- 쿼리와 일치하는 전체 히트 수입니다.
- 개수가 정확합니다(예:
"eq"는 같음을 의미).
track_total_hits를 정수로 설정할 수도 있습니다. 예를 들어 다음 쿼리는 쿼리와 일치하는 전체 히트 수를 100개 문서까지 정확히 추적합니다.
GET my-index-000001/_search
{
"track_total_hits": 100,
"query": {
"match": {
"user.id": "elkbee"
}
}
}
응답의 hits.total.relation은 hits.total.value에 반환된 값이 정확한지("eq") 아니면 전체 수의 하한인지("gte")를 나타냅니다.
예를 들어 다음 응답은
{
"_shards": ...
"timed_out": false,
"took": 30,
"hits": {
"max_score": 1.0,
"total": {
"value": 42,
"relation": "eq"
},
"hits": ...
}
}
- 42개 문서가 쿼리와 일치합니다
- 그리고 그 개수는 정확합니다(
"eq")
... total에 반환된 히트 수가 정확함을 나타냅니다.
쿼리와 일치하는 전체 히트 수가 track_total_hits에 설정한 값보다 크면, 응답의 전체 히트 수는 반환된 값이 하한임을 나타냅니다.
{
"_shards": ...
"hits": {
"max_score": 1.0,
"total": {
"value": 100,
"relation": "gte"
},
"hits": ...
}
}
- 쿼리와 일치하는 문서가 최소 100개 있습니다
- 이는 하한값입니다(
"gte").
전체 히트 수를 추적할 필요가 전혀 없다면 이 옵션을 false로 설정해 쿼리 시간을 단축할 수 있습니다.
GET my-index-000001/_search
{
"track_total_hits": false,
"query": {
"match": {
"user.id": "elkbee"
}
}
}
... 다음을 반환합니다.
{
"_shards": ...
"timed_out": false,
"took": 10,
"hits": {
"max_score": 1.0,
"hits": ...
}
}
- 전체 히트 수를 알 수 없습니다.
마지막으로 요청에서 "track_total_hits"를 true로 설정하면 정확한 개수를 강제로 얻을 수 있습니다.
track_total_hits 파라미터를 사용하면 히트 수의 정확도와 성능을 맞바꿀 수 있습니다. 일반적으로 track_total_hits 값이 낮을수록 쿼리가 빨라지며, false일 때 가장 빠른 결과를 반환합니다. track_total_hits를 true로 설정하면 Elasticsearch가 정확한 히트 수를 반환하는데, 이는 Max WAND 최적화를 비활성화하므로 쿼리 성능을 떨어뜨릴 수 있습니다.
특정 쿼리와 일치하는 문서가 있는지만 알고 싶다면 size를 0으로 설정해 검색 결과에는 관심이 없음을 나타낼 수 있습니다. 또한 terminate_after를 1로 설정해 (샤드별로) 첫 번째 일치 문서를 찾는 즉시 쿼리 실행을 종료하도록 지정할 수 있습니다.
GET /_search?q=user.id:elkbee&size=0&terminate_after=1
terminate_after는 항상 post_filter 이후에 적용되며, 샤드에서 충분한 히트가 수집되면 쿼리와 집계 실행을 모두 중단합니다. 다만 집계는 사후 필터링 이전에 적용되므로 집계의 문서 수가 응답의 hits.total과 일치하지 않을 수 있습니다.
size가 0으로 설정되었으므로 응답에는 히트가 포함되지 않습니다. hits.total은 일치하는 문서가 없음을 나타내는 0이거나, 조기 종료 시점에 쿼리와 일치하는 문서가 최소 그만큼 있었음을 의미하는 0보다 큰 값이 됩니다. 또한 쿼리가 조기 종료되었다면 응답에서 terminated_early 플래그가 true로 설정됩니다. 일부 쿼리는 인덱스 통계에서 히트 수를 직접 가져올 수 있는데, 이 경우 쿼리를 실행할 필요가 없으므로 훨씬 빠릅니다. 이런 상황에서는 문서가 수집되지 않고, 반환되는 total.hits가 terminate_after보다 크며, terminated_early는 false로 설정됩니다.
{
"took": 3,
"timed_out": false,
"terminated_early": true,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": null,
"hits": []
}
}
응답의 took 시간은 노드가 쿼리를 받은 직후부터 검색 관련 작업이 모두 끝나고 위 JSON이 클라이언트로 반환되기 직전까지, 이 요청을 처리하는 데 걸린 시간을 밀리초 단위로 나타냅니다. 즉 스레드 풀에서 대기한 시간, 클러스터 전체에 걸친 분산 검색 실행 시간, 모든 결과를 수집하는 시간이 포함됩니다.
_shards.failed는 검색 요청에 대해 결과를 성공적으로 반환하지 못한 샤드 수를 나타냅니다. _shards.failures는 샤드 실패가 발생한 경우에만 반환되며, 인덱스 이름, 샤드 번호, 노드 ID, 실패 사유 같은 세부 정보를 담은 객체 배열을 포함합니다.
"_shards": {
"total": 5,
"successful": 1,
"skipped": 0,
"failed": 4,
"failures": [
{
"shard": 0,
"index": "<index_name>",
"node": "<node_id>",
"reason": {
"type": "node_not_connected_exception",
"reason": "[<node_name>][<ip>:<port>] Node not connected"
}
},
{
"shard": 1,
"index": "<index_name>",
"node": null,
"reason": {
"type": "no_shard_available_action_exception",
"index_uuid": "<index_uuid>",
"shard": "1",
"index": "<index_name>"
}
}
]
}
샤드 실패는 index와 exception을 기준으로 중복이 제거됩니다. 동일한 인덱스에서 같은 예외가 여러 번 발생하면 여러 샤드가 실패했더라도 _shards.failures에는 한 번만 보고됩니다. 그 결과 _shards.failures의 항목 수가 _shards.failed 값보다 적을 수 있습니다.