쿼리 DSL
쿼리 DSL (Query DSL)
Elasticsearch 검색의 맨 아래에는 Query DSL이라는 JSON 기반 쿼리 언어가 깔려 있어요. 복잡한 검색·필터링·집계를 모두 이 언어로 표현하고, _search 엔드포인트가 이 문법으로 된 쿼리를 받아요. 전체 텍스트 검색부터 정확한 키워드 일치, 시맨틱·벡터·지리 검색까지 아우르는 Elasticsearch의 원조이자 가장 강력한 쿼리 언어예요.
출처: 공식문서 — Query DSL
쿼리 절의 두 종류
Query DSL은 쿼리의 AST(추상 구문 트리)처럼 생각할 수 있어요. 두 종류의 절(clause)로 이루어져 있죠.
- 리프 쿼리 절(Leaf query clauses) — 특정 필드에서 특정 값을 찾는 절이에요.
match,term,range쿼리가 대표적이고, 그 자체로도 쓸 수 있어요. - 복합 쿼리 절(Compound query clauses) — 다른 리프·복합 쿼리를 감싸서 논리적으로 결합하거나(
bool,dis_max) 동작을 바꾸는(constant_score) 절이에요.
같은 절이라도 쿼리 컨텍스트에서 쓰느냐 필터 컨텍스트에서 쓰느냐에 따라 동작이 달라져요. 이게 Query DSL을 이해하는 핵심이에요.
쿼리 컨텍스트 vs 필터 컨텍스트
쿼리 컨텍스트에서는 절이 "이 문서가 이 쿼리 절과 얼마나 잘 맞나요?"에 답해요. 일치 여부를 정할 뿐 아니라 _score 메타데이터 필드에 관련성 점수도 계산해요. 쿼리 절이 query 파라미터에 전달될 때 쿼리 컨텍스트가 작동해요.
필터 컨텍스트에서는 절이 "이 문서가 이 쿼리 절에 맞나요?"라는 이진 질문에만 답해요. 답은 예/아니요뿐이죠. 필터는 점수를 계산하지 않아서 더 빠르고, Elasticsearch가 자주 쓰는 필터를 자동으로 캐시해서 이후 검색도 빨라져요.
필터는 구조화된 데이터(숫자, 날짜, 불리언, keyword, 지리점·도형 등 예측 가능한 형식)를 다룰 때 특히 효과적이에요. 날짜 범위 확인("timestamp가 2015~2016년 사이인가"), 특정 값 확인("status가 published인가") 같은 '반드시 갖춰야 하는' 조건을 구현하기 좋아요.
필터 컨텍스트는 쿼리 절이 filter 파라미터에 전달될 때 작동해요. 예를 들어 bool 쿼리의 filter·must_not 파라미터, constant_score의 filter 파라미터, 필터 집계 같은 데서요.
쿼리와 필터를 함께 쓰는 예
아래 검색은 다음 조건이 모두 맞는 문서를 찾아요.
title필드에search라는 단어가 포함되고content필드에elasticsearch라는 단어가 포함되며status필드에published가 정확히 일치하고publish_date필드가 2015년 1월 1일 이후예요.
GET /_search
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "2015-01-01" }}}
]
}
}
}
여기서 흐름을 짚어 볼게요. query 파라미터는 쿼리 컨텍스트를 나타내요. bool과 두 match 절은 쿼리 컨텍스트에서 쓰이므로 문서가 얼마나 잘 맞는지 점수를 매깁니다. 반면 filter 파라미터는 필터 컨텍스트예요. 안의 term과 range 절은 맞지 않는 문서를 걸러내지만, 일치하는 문서의 점수에는 영향을 주지 않아요.
필터가 주는 이점
필터가 왜 유용한지 정리하면 이래요.
- 단순한 이진 논리 — 점수 계산 없이 예/아니요로만 판정해요.
- 성능 — 관련성 점수를 계산하지 않아 쿼리보다 빨라요.
- 캐싱 — 자주 쓰는 필터는 Elasticsearch가 자동으로 캐시해요.
- 리소스 효율 — 전체 텍스트 쿼리보다 CPU를 덜 써요.
- 쿼리 결합 — 점수가 매겨진 쿼리와 조합해 결과 집합을 효율적으로 다듬을 수 있어요.
요컨대 점수에 영향을 줘야 하는 조건은 쿼리 컨텍스트에, 나머지 조건은 필터 컨텍스트에 두는 게 기본 원칙이에요.
집계 (Aggregations)
Query DSL로 데이터를 집계할 수도 있어요. 집계는 검색과 같은 데이터 구조를 쓰기 때문에 매우 빨라서, 검색·필터·분석을 한 요청에서 동시에 처리할 수 있어요. 집계는 검색 쿼리 문맥 안에서 계산돼요. 종류는 세 가지예요.
- Metric — 필드 값에서 합계, 평균 같은 지표를 계산해요.
- Bucket — 필드 값·범위·기준에 따라 문서를 버킷으로 묶어요.
- Pipeline — 다른 집계의 결과에 대해 집계를 실행해요.
집계는 검색 API의 aggs 파라미터로 실행해요.
비싼 쿼리 제한하기
구현 방식 때문에 본질적으로 느린 쿼리 유형이 있어요. script, fuzzy, regexp, prefix, wildcard, text·keyword 필드에 대한 range, 조인 쿼리, script_score·percolate 같은 것들이죠. 클러스터 안정성에 영향을 줄 수 있어서, search.allow_expensive_queries 설정을 false로 두면(기본값은 true) 이런 쿼리 실행을 막을 수 있어요.
더 알아보기
- _search API로 검색하기 — 쿼리를 담는 검색 REST API
- Elasticsearch에서 검색 인터페이스 고르기 — Query DSL과 다른 쿼리 언어 비교