ELK & Elastic Cloud

Query DSL

meellon 2026. 7. 5. 18:00

학습 목표

Query DSL JSON 구조와 _search 요청 형태를 읽고 작성할 수 있다.

match·term·rangetext·keyword mapping에 맞게 선택할 수 있다.

boolmust·filter·should·must_not 조합으로 로그 검색 쿼리를 짤 수 있다.

query contextfilter context 차이(점수·캐시)를 설명할 수 있다.

from/size 한계와 search_after·PIT 기반 deep pagination을 설명할 수 있다.

문제 상황

  • 3편 mapping 후 level: ERRORmatch에 넣었더니 WARN도 섞임 — keyword는 term
  • messagepayment timeout 구문 검색이 안 됨 — match_phrase 필요
  • bool에 조건을 나열했는데 점수가 이상하고 느림 — filter vs must 구분 없음
  • Kibana에서 2페이지 넘기면 느리거나 결과가 중복from: 10000 deep pagination
  • sort 없이 search_after 쓰다 순서가 바뀜 — tie-breaker _shard_doc 필요
  • 2편 match 맛보기만으로는 운영 로그 쿼리 패턴이 안 잡힘

앞 편에서 mapping·analyzer로 필드 타입을 고정했다. 이번 편은 검색 언어 — Query DSL. aggregation·shard는 5편, Kibana KQL은 10편이다.

1. Query DSL이란?

Query DSL — Elasticsearch 검색·필터를 표현하는 JSON 트리.

POST /app-logs/_search
{
  "query": { ... },
  "sort": [ ... ],
  "from": 0,
  "size": 20
}
최상위 키 역할
query 어떤 document를 고를지
sort 정렬 (시간·필드·_score)
from / size offset 페이지 (얕은 페이징)
search_after 커서 페이징 (deep)
_source 반환 필드 제한
aggs 집계 — 5편
  • Kibana Discover — UI 필터가 내부적으로 bool + filter DSL (또는 KQL → DSL 변환)
  • GET _search + source URL 인코딩 가능하지만 POST + JSON이 일반적

2. match · term · range

3편 text vs keyword에 맞는 쿼리 선택.

쿼리 필드 동작
match text analyzer 적용 후 term 매칭 + 관련도 점수
match_phrase text 구문 순서 유지
term keyword·숫자·date 정확 값 (analyze 안 함)
range 숫자·date gte·lte 구간
// code/log-search-examples.json (발췌)
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "level": "ERROR" } },
        { "term": { "service": "order-api" } },
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ],
      "must": [
        { "match": { "message": "payment timeout" } }
      ]
    }
  }
}
  • term 값은 mapping과 동일ERROR (대문자 keyword)
  • matchmessage text — analyzer가 payment·timeout term으로 검색
  • range on date@timestampdate mapping 필수

자주 하는 실수

증상 원인 수정
level 필터 안 먹음 match on keyword term
대소문자 mismatch term에 error keyword 저장값과 일치
구문 검색 실패 match만 사용 match_phrase
날짜 range 0건 text 필드에 range date mapping

3. Query context vs Filter context

  Query context Filter context
match, must 안의 query filter, term, range
_score 계산 0 (무시)
캐시 매번 평가 bitset 캐시 가능
용도 관련도 순위 예/아니오 필터
  • bool의 filter 절에 넣으면 — 점수 없이 빠르게 걸러짐
  • 관련도 필요 없는 로그 조회 — 전부 filter로도 가능 (constant_score는 생략)
  • Kibana 필터 pillfilter context

4. bool 쿼리

여러 조건을 조합하는 컨테이너.

의미 점수
must 반드시 만족 영향 (query context)
filter 반드시 만족 영향 없음 (filter context)
should 가산 (선택) 만족 시 boost
must_not 제외 영향 없음
// code/bool-filter-query.json
{
  "query": {
    "bool": {
      "filter": [
        { "range": { "@timestamp": { "gte": "now-24h" } } },
        { "terms": { "level": ["ERROR", "WARN"] } }
      ],
      "must": [
        { "match": { "message": "timeout" } }
      ],
      "must_not": [
        { "term": { "service": "health-check" } }
      ],
      "should": [
        { "match": { "message": "payment" } }
      ],
      "minimum_should_match": 0
    }
  }
}
  • 로그 tail 패턴 — filter시간·level·service (캐시·빠름), must본문 검색
  • terms — keyword 배열 OR (level ERROR or WARN)
  • minimum_should_match — should만 있을 때 최소 개수

5. 정렬과 페이지네이션

from · size (얕은 페이징)

{
  "from": 0,
  "size": 50,
  "sort": [{ "@timestamp": "desc" }],
  "query": { "match_all": {} }
}
  • from + size ≤ 10,000 (기본 index.max_result_window) — 그 이상은 거부
  • UI 1~N 페이지 — 보통 충분
  • deep offset — shard마다 정렬·skip 비용 큼

search_after (deep pagination)

// code/search-after.example.json
{
  "size": 50,
  "sort": [
    { "@timestamp": "desc" },
    { "_shard_doc": "desc" }
  ],
  "query": {
    "bool": {
      "filter": [
        { "range": { "@timestamp": { "gte": "now-7d" } } }
      ]
    }
  },
  "search_after": [1704067200000, 12345]
}
  from / size search_after
방식 offset 이전 페이지 마지막 sort 값 커서
deep 비권장 (10k 제한) 권장
sort 선택 필수·유일해야 함
일관성 index 변경 시 중복/누락 PIT와 함께 쓰면 안정
  • Point in Time (PIT)open_point_in_time으로 스냅샷 뷰 — 대량 export·scroll 대체
  • scroll — legacy bulk export; 신규는 PIT + search_after 권장
  • 응답 hits.hits[-1].sort 값을 다음 요청 search_after에 전달

6. 로그 검색 패턴 모음

최근 ERROR + 본문

{
  "size": 100,
  "sort": [{ "@timestamp": "desc" }],
  "query": {
    "bool": {
      "filter": [
        { "term": { "level": "ERROR" } },
        { "range": { "@timestamp": { "gte": "now-15m" } } }
      ],
      "must": [
        { "match_phrase": { "message": "connection refused" } }
      ]
    }
  }
}

trace_id로 한 요청 추적

{
  "query": {
    "bool": {
      "filter": [
        { "term": { "trace_id": "a1b2c3d4e5f6" } }
      ]
    }
  },
  "sort": [{ "@timestamp": "asc" }]
}
  • keyword exact — term only
  • 시간순 asc — 요청 흐름 재구성

_source 제한

{
  "_source": ["@timestamp", "level", "service", "message"],
  "query": { "match_all": {} }
}
  • 대용량 필드 제외 — 네트워크·Kibana 부담 감소

7. 실무 체크리스트

항목 습관
keyword 필터 term / terms in filter
본문 검색 match / match_phrase in must
시간 범위 range on @timestamp in filter
정렬 로그는 @timestamp desc 기본
deep page search_after + PIT, not from: 50000
mapping 확인 쿼리 전 필드 타입 (3편)
  • Slow query — filter 과다·wildcard·leading * — 21편 slow log
  • 10편 — Kibana에서 동일 조건 KQL로 표현

정리

  • Query DSL — JSON query 트리; bool로 조건 조합
  • match (text) vs term (keyword) — mapping과 으로 기억
  • filter — 점수 없음·캐시; 로그 차원 필터에 적합
  • from/size — 얕은 페이징; search_after — deep·export
  • 5편 — aggregation·shard·routing

다음에 다룰 것

  • Aggregation과 클러스터
  • terms·date histogram, node·shard·replica, routing

해당 내용은 Elasticsearch Guide (Query DSL, Search your data) 를 기반으로 합니다.

'ELK & Elastic Cloud' 카테고리의 다른 글

Beats  (0) 2026.07.11
Aggregation과 클러스터  (0) 2026.07.08
Mapping과 Analyzer  (0) 2026.07.03
Elasticsearch란  (0) 2026.07.02
ELK · Elastic Cloud 오리엔테이션  (0) 2026.06.30