Elasticsearch: 범위 쿼리 (Range Query)

Elasticsearch에서는 숫자 및 날짜 데이터를 효과적으로 검색할 수 있도록 **범위 쿼리(Range Query)**를 제공합니다.
이 쿼리는 특정 범위 안에 포함되는 데이터를 찾을 때 사용되며, 가격, 날짜, 온도, 점수 등 연속적인 값(Continuous Data)을 검색하는 데 유용합니다.

이번 글에서는 Range Query의 기본 개념, 사용법, 그리고 날짜 검색을 포함한 실전 예제까지 살펴보겠습니다.


1. Range Query란?

🔹 기본 개념

  • 특정 필드의 값이 특정 범위에 포함되는 문서만 검색할 때 사용됩니다.
  • 숫자(Number), 날짜(Date), IP 주소(IP Address) 등의 필드에서 주로 사용됩니다.
  • 점수(score)를 계산하지 않으며, 오로지 True/False 조건만으로 필터링합니다.

2. Range Query의 기본 문법

Range Query는 아래와 같은 4가지 조건을 지원합니다.

연산자 설명
gte (Greater Than or Equal) 이상 (≥) - 특정 값보다 크거나 같은 데이터 검색
gt (Greater Than) 초과 (>) - 특정 값보다 큰 데이터 검색
lte (Less Than or Equal) 이하 (≤) - 특정 값보다 작거나 같은 데이터 검색
lt (Less Than) 미만 (<) - 특정 값보다 작은 데이터 검색

📌 예제 데이터 (phones 인덱스)

POST phones/_bulk
{"index":{"_id":1}}
{"model":"Samsung GalaxyS 5","price":475,"date":"2014-02-24"}
{"index":{"_id":2}}
{"model":"Samsung GalaxyS 6","price":795,"date":"2015-03-15"}
{"index":{"_id":3}}
{"model":"Samsung GalaxyS 7","price":859,"date":"2016-02-21"}
{"index":{"_id":4}}
{"model":"Samsung GalaxyS 8","price":959,"date":"2017-03-29"}
{"index":{"_id":5}}
{"model":"Samsung GalaxyS 9","price":1059,"date":"2018-02-25"}

(📌 위 데이터는 실제 상품과 무관한 예제 데이터입니다.)


3. 숫자 검색 (Number Range Query)

📌 예제 1: 가격(price)이 700 이상, 900 미만인 스마트폰 검색

GET phones/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 700,
        "lt": 900
      }
    }
  }
}

🔹 결과:

  • Samsung GalaxyS 6 (price: 795) ✅
  • Samsung GalaxyS 7 (price: 859) ✅

📌 설명:

  • gte: 700 → 700 이상
  • lt: 900 → 900 미만

4. 날짜 검색 (Date Range Query)

📌 예제 2: 2016년 1월 1일 이후 출시된 스마트폰 검색

GET phones/_search
{
  "query": {
    "range": {
      "date": {
        "gt": "2016-01-01"
      }
    }
  }
}

🔹 결과:

  • Samsung GalaxyS 7 (date: 2016-02-21) ✅
  • Samsung GalaxyS 8 (date: 2017-03-29) ✅
  • Samsung GalaxyS 9 (date: 2018-02-25) ✅

📌 설명:

  • gt: "2016-01-01" → 2016년 1월 1일보다 이후(>)인 데이터 검색

5. 날짜 포맷 변환 (Format 옵션 활용)

Elasticsearch는 기본적으로 **ISO8601 날짜 형식 (yyyy-MM-dd 또는 yyyy-MM-dd'T'HH:mm:ss)**을 사용합니다.
하지만 format 옵션을 사용하면 다른 날짜 형식도 검색할 수 있습니다.

📌 예제 3: 2016년 1월 1일 ~ 2018년 1월 1일 사이 출시된 스마트폰 검색

GET phones/_search
{
  "query": {
    "range": {
      "date": {
        "gt": "31/12/2015",
        "lt": "2018",
        "format": "dd/MM/yyyy||yyyy"
      }
    }
  }
}

🔹 결과:

  • Samsung GalaxyS 7 (date: 2016-02-21) ✅
  • Samsung GalaxyS 8 (date: 2017-03-29) ✅

📌 설명:

  • format: "dd/MM/yyyy||yyyy" → 31/12/2015 또는 2018 형식 허용

6. 현재 시간(now)과 상대적 날짜 검색

Elasticsearch에서는 now 키워드를 사용하여 현재 시간을 기준으로 상대적인 날짜를 검색할 수 있습니다.

📌 예제 4: 2016년 1월 1일 이후 + 6개월 ~ 현재(now) 기준 1년 전 사이 검색

GET phones/_search
{
  "query": {
    "range": {
      "date": {
        "gt": "2016-01-01||+6M",
        "lt": "now-365d"
      }
    }
  }
}

🔹 결과:

  • Samsung GalaxyS 8 (date: 2017-03-29) ✅
  • Samsung GalaxyS 9 (date: 2018-02-25) ✅

📌 설명:

  • "2016-01-01||+6M" → 2016년 1월 1일 이후 6개월 후
  • "now-365d" → 현재 시간(now) 기준 1년 전

7. Range Query의 특징 및 제한사항

점수(score)를 계산하지 않음

  • range 쿼리는 정확성(True/False)만 확인하고 점수를 계산하지 않습니다.
  • 검색 결과는 _score: 1.0으로 동일합니다.

점수를 조정하고 싶다면 Function Score Query 활용

  • 예를 들어, 나이가 채용 조건(24~55세)과 가까울수록 높은 점수를 부여하고 싶다면 Function Score Query를 사용해야 합니다.

📌 Function Score Query 예제 (추후 학습 필요)

GET jobs/_search
{
  "query": {
    "function_score": {
      "query": {
        "range": {
          "age": {
            "gte": 24,
            "lte": 55
          }
        }
      },
      "functions": [
        {
          "gauss": {
            "age": {
              "origin": 35,
              "scale": 10
            }
          }
        }
      ]
    }
  }
}

🔹 설명: 35세를 기준으로 점수를 조정 (추후 학습 필요)


8. 정리

개념 설명
Range Query 숫자, 날짜, IP 주소 등의 범위를 검색하는 쿼리
gte / gt 이상(≥), 초과(>)
lte / lt 이하(≤), 미만(<)
Format 옵션 날짜 포맷 변경 가능
now 키워드 현재 시간 기준 검색 가능
Function Score Query 기준점에 따라 점수를 조정하고 싶을 때 사용

9. 학습 가이드

1️⃣ Range Query의 숫자 및 날짜 검색 실습
2️⃣ Format 옵션을 사용하여 다양한 날짜 포맷 테스트
3️⃣ now, +6M, -365d 같은 상대적 날짜 검색 연습
4️⃣ Function Score Query 학습 (추후 검색 점수 조정 시 활용 가능)


다음 학습에서는 Function Score Query를 활용한 검색 결과 가중치 조정 방법을 다루겠습니다! 🚀

Elasticsearch: 정확값 쿼리 (Exact Value Query)

Elasticsearch에서는 **풀 텍스트 검색(Full-Text Search)**과는 다르게 정확한 값(Exact Value)을 검색하는 방식도 지원합니다. 이 방식을 활용하면 특정 값과 정확히 일치하는 문서만 조회할 수 있으며, 검색 **점수(score)**가 계산되지 않기 때문에 빠른 검색 속도를 보장할 수 있습니다. 이번 글에서는 정확값 검색이란 무엇인지, 그리고 이를 구현하는 filter, keyword, range 쿼리에 대해 알아보겠습니다.


1. 정확값(Exact Value) 검색이란?

🔹 기본 개념

  • 풀 텍스트 검색은 **검색어와 문서의 연관성(relevancy)**을 계산하여 가장 관련성이 높은 결과를 반환합니다.
  • 하지만 정확값 검색정확히 일치하는 값을 찾는 방식이며, 점수(score)를 계산하지 않습니다.
  • 대표적인 쿼리로는 term, range 쿼리가 있으며, bool 쿼리의 filter 절에서 주로 사용됩니다.

🔹 RDBMS와 비교

RDBMS에서 WHERE column = '값' 같은 조건을 사용하여 정확히 일치하는 데이터만 조회하는 것과 비슷합니다.

📌 예제

검색어 풀 텍스트 검색 (match) 정확값 검색 (term)
fox "The quick brown fox" ✅ "fox" 단독으로 포함된 문서만 ✅
Brown fox brown dog "brown"과 "fox"를 포함한 모든 문서 "Brown fox brown dog"와 정확히 일치하는 문서 ✅

2. Bool 쿼리의 Filter 사용

🔹 Filter의 특징

  • 검색 점수(score)에 영향을 주지 않음
  • 쿼리 캐싱을 활용하여 성능 최적화
  • 정확한 값을 필터링할 때 주로 사용

📌 예제 1: match 쿼리 vs filter 쿼리

① match 쿼리 (연관성 기반 검색)

GET my_index/_search
{
  "query": {
    "match": {
      "message": "fox"
    }
  }
}

🔹 결과:

  • "The quick brown fox jumps over the lazy dog" 🆙 (연관성이 높은 문서가 우선 노출됨)
  • "The quick brown fox"

② match 쿼리 + bool must (여러 조건 검색, 점수 적용됨)

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "fox" } },
        { "match": { "message": "quick" } }
      ]
    }
  }
}

🔹 결과:

  • "fox"와 "quick"이 포함된 문서만 검색되며, 점수가 반영됨

③ must + filter 조합 (정확값 필터링, 점수 미반영)

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "fox" } }
      ],
      "filter": [
        { "match": { "message": "quick" } }
      ]
    }
  }
}

🔹 결과 비교

  • "fox"를 포함한 모든 문서 중 "quick"이 포함된 문서만 반환됨
  • 하지만 점수(score)는 변화 없음

3. Filter 내부에서 must_not 사용

  • 특정 단어가 포함되지 않은 문서를 찾을 때 사용
  • 점수에 영향을 주지 않으며, 빠르게 검색 가능

📌 예제 2: "fox" 포함 & "dog" 제외

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "fox" } }
      ],
      "filter": [
        {
          "bool": {
            "must_not": [
              { "match": { "message": "dog" } }
            ]
          }
        }
      ]
    }
  }
}

🔹 결과:

  • "fox"가 포함된 문서 중 "dog"가 포함되지 않은 문서만 검색됨
  • 점수(score)는 변화 없음

4. Keyword 타입을 활용한 정확값 검색

🔹 Keyword 타입이란?

  • keyword 필드는 분석되지 않는(Analyzed X) 문자열 필드
  • 검색어가 정확히 일치하는 데이터만 반환
  • 대소문자, 공백까지 동일해야 검색됨
  • filter 절과 함께 사용하면 빠르게 검색 가능

📌 예제 3: keyword 필드 검색

GET my_index/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "message.keyword": "Brown fox brown dog"
          }
        }
      ]
    }
  }
}

🔹 결과:

  • "Brown fox brown dog"와 완벽하게 일치하는 문서만 반환됨
  • 점수(score)는 _score: 0.0 (점수 계산 없음)

📌 Keyword 필드의 특성
빠른 검색 속도
정확한 값 비교
대소문자 및 공백 차이까지 구분


5. Filter를 활용한 성능 최적화

filter는 점수를 계산하지 않기 때문에 성능이 우수합니다.
filter 내부의 조건들은 캐싱되므로 반복적인 검색 시 더 빠르게 실행됩니다.
숫자 범위(range) 검색이나 정확한 값(term) 검색은 filter를 사용하는 것이 유리합니다.

📌 filter 절을 사용해야 하는 경우
1️⃣ 상품 검색에서 제조사, 브랜드, 가격 필터링
2️⃣ 로그 검색에서 날짜 범위 필터링
3️⃣ ID 기반 검색에서 정확한 ID 값을 찾을 때


6. 정리

개념 설명
풀 텍스트 검색 (match) 연관성을 기준으로 검색 결과 정렬
정확값 검색 (Exact Value) 값이 정확히 일치하는 문서만 검색
Bool Query - Filter 검색 점수를 계산하지 않고 문서를 필터링
Must vs Filter must는 점수를 반영하지만, filter는 점수를 무시
Keyword 필드 분석되지 않는 문자열 필드, 정확한 검색 가능
Must Not (필터 내부 사용) 특정 단어가 포함되지 않은 문서 검색

7. 학습 가이드

1️⃣ Bool Query의 filter 절을 실습하여 정확값 검색 연습
2️⃣ Match vs Term 쿼리의 차이점을 비교하며 성능 테스트
3️⃣ Keyword 타입을 사용하여 필터링 적용
4️⃣ Range, Term, Exists 쿼리를 활용한 검색 최적화


8. 마무리

Elasticsearch에서 정확한 값을 검색할 때는 풀 텍스트 검색이 아닌 filter 기반 검색을 사용하는 것이 성능적으로 더 유리합니다.
특히 상품 검색, 로그 분석, ID 조회 등의 경우 filter와 keyword 필드를 적극 활용하면 더 빠르고 정확한 검색을 할 수 있습니다.

다음 학습에서는 Range Query를 활용한 숫자 및 날짜 범위 검색 방법을 다루겠습니다! 🚀

Bool Query (복합 쿼리) 개요

  • 여러 개의 검색 조건을 조합하여 복합적인 검색을 수행할 수 있는 강력한 쿼리 방식입니다.
  • SQL의 AND, OR, NOT과 유사하지만 완전히 동일하지 않음 → 단항 연산자로 적용됨.

Bool Query 구성 요소

키워드 설명
must 쿼리가 **참(True)**인 문서를 검색 (AND 조건)
must_not 쿼리가 **거짓(False)**인 문서를 검색 (NOT 조건)
should 일치하는 문서의 점수(Score)를 증가 (OR 조건과 유사)
filter 쿼리가 **참(True)**인 문서를 검색하되, 점수를 계산하지 않음 (빠름 & 캐싱 가능)
  • SQL과 비교
    • must ≈ AND
    • must_not ≈ NOT
    • should ≈ OR (점수 영향)
    • filter ≈ WHERE (점수 없음, 성능 최적화)

Bool Query 기본 문법

GET <인덱스명>/_search
{
  "query": {
    "bool": {
      "must": [
        { <쿼리> }
      ],
      "must_not": [
        { <쿼리> }
      ],
      "should": [
        { <쿼리> }
      ],
      "filter": [
        { <쿼리> }
      ]
    }
  }
}
  • 각 키워드(must, must_not, should, filter) 안에는 여러 개의 조건을 배열 형식으로 입력 가능.

Bool Query 사용법

예제 1: "quick" 포함 + "lazy dog" 문장 포함 (AND 조건)

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "quick" } },
        { "match_phrase": { "message": "lazy dog" } }
      ]
    }
  }
}
  • "quick" 단어가 포함된 문서 + "lazy dog" 문장이 포함된 문서 검색
  • 결과: "quick"과 "lazy dog"을 포함하는 문서 검색됨

예제 2: "quick" 또는 "lazy dog" (OR 조건 유사)

GET my_index/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "message": "quick" } },
        { "match_phrase": { "message": "lazy dog" } }
      ],
      "minimum_should_match": 1
    }
  }
}
  • should 사용 → "quick" 또는 "lazy dog" 중 하나라도 포함하면 검색됨
  • minimum_should_match: 1 → 최소 하나의 조건을 만족하는 문서 포함
  • 결과: "quick" 또는 "lazy dog" 중 하나라도 포함된 문서 검색됨

예제 3: "quick" & "lazy dog" 제외 (NOT 조건)

GET my_index/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "message": "quick" } },
        { "match_phrase": { "message": "lazy dog" } }
      ]
    }
  }
}
  • "quick"과 "lazy dog"이 포함되지 않은 문서만 검색
  • 결과: "quick"과 "lazy dog"이 없는 문서 반환됨

예제 4: 특정 단어 포함 (must) + 특정 단어 제외 (must_not)

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "fox" } }
      ],
      "must_not": [
        { "match": { "message": "lazy" } }
      ]
    }
  }
}
  • "fox" 포함 & "lazy" 제외
  • 결과: "fox"가 있는 문서 중 "lazy"가 없는 문서 검색됨

예제 5: 특정 단어 포함 (filter) - 점수 계산 없음

GET my_index/_search
{
  "query": {
    "bool": {
      "filter": [
        { "match": { "message": "fox" } }
      ]
    }
  }
}
  • "fox"가 포함된 문서를 검색하지만, 점수(Score)를 계산하지 않음 → 빠름
  • must와 비교하면 성능이 더 우수 (검색 결과가 많을수록 차이 커짐)

Bool Query와 SQL 비교

SQL 조건 Bool Query 변환
A AND B must: [A, B]
A OR B should: [A, B], "minimum_should_match": 1
NOT A must_not: [A]
(A OR B) AND (NOT C) must: [should: [A, B]], must_not: [C]

예제 6: SQL 스타일 변환

  • SQL 스타일
    (A OR B) AND (NOT C)
    
  • Bool Query 변환
    GET my_index/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "bool": {
                "should": [
                  { "match": { "message": "A" } },
                  { "match": { "message": "B" } }
                ],
                "minimum_should_match": 1
              }
            }
          ],
          "must_not": [
            { "match": { "message": "C" } }
          ]
        }
      }
    }
    

Bool Query 정리

키워드 설명 예제
must AND 조건 "must": [{ "match": { "message": "quick" } }]
must_not NOT 조건 "must_not": [{ "match": { "message": "lazy" } }]
should OR 조건 "should": [{ "match": { "message": "quick" } }, { "match": { "message": "dog" } }], "minimum_should_match": 1
filter 점수 계산 없이 검색 "filter": [{ "match": { "message": "fox" } }]

🎯 추가 학습 추천

  1. Boosting (검색 결과 가중치 조절)
  2. Nested Query (중첩된 JSON 문서 검색)
  3. Function Score Query (점수 조작)
  4. Aggregations (데이터 그룹화 및 통계 분석)

 

Full Text Query 개요

  • Elasticsearch는 강력한 풀 텍스트 검색 기능을 제공하며, 다양한 쿼리 방식이 있습니다.
  • 주요 쿼리 종류
    • match_all: 전체 문서 검색
    • match: 부분 일치 검색 (OR / AND 조건 가능)
    • match_phrase: 순서 포함 정확한 구문 검색
    • query_string: 복잡한 검색 연산자 지원

예제 데이터 입력

  • my_index 인덱스에 _bulk API를 사용하여 데이터 입력
POST my_index/_bulk
{"index":{"_id":1}}
{"message":"The quick brown fox"}
{"index":{"_id":2}}
{"message":"The quick brown fox jumps over the lazy dog"}
{"index":{"_id":3}}
{"message":"The quick brown fox jumps over the quick dog"}
{"index":{"_id":4}}
{"message":"Brown fox brown dog"}
{"index":{"_id":5}}
{"message":"Lazy jumping dog"}
  • message 필드에 다양한 단어가 포함된 문서 5개 입력됨

match_all 쿼리 (모든 문서 검색)

쿼리 없이 실행하면 match_all 적용

GET my_index/_search

명시적으로 match_all 사용

GET my_index/_search
{
  "query": {
    "match_all": {}
  }
}
  • 모든 문서를 검색
  • hits.total.value에 검색된 문서 개수가 표시됨

match 쿼리 (부분 일치 검색)

"dog" 포함 문서 검색

GET my_index/_search
{
  "query": {
    "match": {
      "message": "dog"
    }
  }
}
  • "dog" 단어가 포함된 모든 문서 검색
  • 결과: 4개 문서 검색됨 (_id: 2, 3, 4, 5)

여러 개의 검색어 (OR 조건)

GET my_index/_search
{
  "query": {
    "match": {
      "message": "quick dog"
    }
  }
}
  • "quick" 또는 "dog" 중 하나라도 포함된 문서를 검색
  • 결과: 5개 문서 검색됨 (_id: 1, 2, 3, 4, 5)

여러 개의 검색어 (AND 조건)

  • operator: "and" 옵션을 사용
GET my_index/_search
{
  "query": {
    "match": {
      "message": {
        "query": "quick dog",
        "operator": "and"
      }
    }
  }
}
  • "quick"과 "dog"을 모두 포함하는 문서만 검색
  • 결과: 2개 문서 검색됨 (_id: 2, 3)

5. match_phrase 쿼리 (정확한 구문 검색)

"lazy dog" 검색

GET my_index/_search
{
  "query": {
    "match_phrase": {
      "message": "lazy dog"
    }
  }
}
  • **"lazy dog"**이라는 정확한 문장이 포함된 문서 검색
  • 결과: 1개 문서 검색됨 (_id: 2)

match_phrase + slop 옵션 (단어 간격 허용)

GET my_index/_search
{
  "query": {
    "match_phrase": {
      "message": {
        "query": "lazy dog",
        "slop": 1
      }
    }
  }
}
  • 설명
    • "lazy dog" 사이에 다른 단어가 1개까지 허용
    • "lazy jumping dog" 문장도 검색됨
  • 결과: 2개 문서 검색됨 (_id: 2, 5)
  • 추가 설정 가능
    • slop: 2 → "lazy jumping brown dog" 같은 문장도 검색 가능

query_string 쿼리 (복잡한 검색)

  • 루씬(Lucene) 문법을 활용한 고급 검색
  • 연산자 사용 가능 (AND, OR, NOT)
  • 필드 지정 가능 (default_field)

"lazy" AND "jumping" OR "quick dog" 검색

GET my_index/_search
{
  "query": {
    "query_string": {
      "default_field": "message",
      "query": "(jumping AND lazy) OR \"quick dog\""
    }
  }
}
  • "lazy"와 "jumping"을 모두 포함하거나 "quick dog" 문장을 포함하는 문서 검색
  • 결과: 2개 문서 검색됨 (_id: 3, 5)

Full Text Query 정리

쿼리 유형 설명 예제
match_all 모든 문서 검색 { "query": { "match_all": {} } }
match 부분 일치 검색 (OR) { "query": { "match": { "message": "dog" } } }
match (AND 조건) "quick"과 "dog"을 모두 포함 { "query": { "match": { "message": { "query": "quick dog", "operator": "and" } } } }
match_phrase "lazy dog" 문장 검색 { "query": { "match_phrase": { "message": "lazy dog" } } }
match_phrase + slop "lazy dog" 사이에 1개 단어 허용 { "query": { "match_phrase": { "message": { "query": "lazy dog", "slop": 1 } } } }
query_string 복잡한 검색 연산자 지원 { "query": { "query_string": { "default_field": "message", "query": "(jumping AND lazy) OR \"quick dog\"" } } }

🎯 추가 학습 추천

  1. bool 쿼리 (복합 조건 검색)
  2. term 쿼리 (정확한 단어 검색)
  3. wildcard & regex 검색
  4. fuzzy 검색 (오타 교정 검색)

 

+ Recent posts