2 분 소요

[ELK Stack]Elasticsearch 검색 2편 - 고급 검색 기법과 성능 튜닝

Kibana 콘솔에서 실습했던 고급 검색 쿼리들에 대해 하나도 빠짐없이 정리함. 분석기, 스코어 계산, 페이징, 정렬, 와일드카드, 정규식 등 검색 최적화와 표현력을 위한 기능들을 나열함. 서적 중심으로 자주 마주치는 조건들에 대해 직접 테스트하며 기록한 내용을 기반으로 정리함.


1. 스코어(_score)의 정체와 조작 기법

스코어는 어떻게 계산되는가?

Elasticsearch는 기본적으로 BM25 알고리즘을 통해 _score를 계산함. 이는 단순히 키워드 포함 여부만 따지는 게 아니라, 문서 내에서의 등장 빈도와 해당 단어의 희귀도까지 고려한 계산임.

핵심 구성 요소:

  1. tf (term frequency): 해당 문서에서 키워드가 얼마나 자주 등장했는지
  2. idf (inverse document frequency): 전체 문서 중 해당 키워드를 포함한 문서가 얼마나 드문지
  3. 문서 길이 보정: 짧은 문서일수록 높은 점수를 받도록 보정
  4. boost: 필드나 쿼리에서 직접 주는 가중치 값

최종 공식 (단순화): _score = boost × tf × idf / 문서 길이 조정값

이 알고리즘을 이해하면 왜 특정 문서가 상위에 노출되는지 판단할 수 있고, 우리가 원하는 순서를 맞추기 위한 튜닝도 가능함.

🔹 _score 분석: explain

GET kibana_sample_data_ecommerce/_search
{
  "query": {
    "match": {
      "products.product_name": "Pants"
    }
  },
  "explain": true
}
  • explain: true를 통해 각 문서가 어떤 기준으로 점수를 받았는지 자세히 확인 가능함.
  • 실무에서는 특정 문서가 왜 높은 순위에 있는지 또는 왜 안 보이는지 디버깅할 때 유용함.

🔹 minimum_should_match

{
  "bool": {
    "should": [
      { "match": {"title": "Elastic"} },
      { "match": {"title": "Search"} },
      { "match": {"title": "Query"} }
    ],
    "minimum_should_match": 2
  }
}
  • should 조건 중 몇 개 이상 일치해야 문서를 반환할지를 지정할 수 있음.
  • 단순 OR 조합보다 더 정밀한 조건을 구성할 수 있음.

🔹 function_score 쿼리

{
  "function_score": {
    "query": {
      "match": {"category": "clothing"}
    },
    "field_value_factor": {
      "field": "products.base_price",
      "factor": 1.2,
      "modifier": "sqrt",
      "missing": 1
    }
  }
}
  • 쿼리 점수를 수학적으로 조정할 수 있음.
  • 예: base_price 값이 클수록 높은 점수를 부여하고 싶은 경우.
  • 실무 예시: 리뷰 개수, 평점, 판매량 등 다양한 수치를 기반으로 랭킹을 조정하는 데 활용 가능.

기타 사용 가능한 스코어 함수:

  • random_score: 랜덤하게 결과 섞기
  • script_score: 스크립트를 이용해 점수 계산

2. 정렬과 페이징 전략

🔹 기본 정렬

"sort": [
  { "products.base_price": "desc" },
  "_score"
]
  • 여러 필드 조합 가능 (가격, 등록일자 등)
  • _score 정렬도 가능하므로 relevance 우선 정렬도 가능

🔹 페이징: from / size 방식

"from": 10,
"size": 10
  • from: 몇 번째 문서부터 가져올지
  • size: 몇 개 가져올지
  • 문제점: 10,000 이상 넘어가면 성능 급격히 저하됨 (Lucene 구조 때문)

🔹 페이징: search_after 방식

"sort": ["timestamp", "_id"],
"search_after": ["2023-01-01T00:00:00", "abc123"]
  • 이전 쿼리의 마지막 정렬값을 기준으로 다음 페이지 탐색
  • search_after는 무한 스크롤 구현 시 성능상 유리함
  • 주의: _id 필드 반드시 포함되어야 함 (정렬 기준 안정성)

3. 고급 검색 - 패턴 기반 쿼리

🔹 와일드카드 쿼리

{
  "wildcard": {
    "customer_full_name.keyword": {
      "value": "M?r*"
    }
  }
}
  • ?: 임의의 문자 한 개
  • *: 0개 이상의 문자
  • 성능이 매우 낮기 때문에 접두어가 반드시 고정된 형태로 시작되어야 함 (예: Mar*은 OK, *ar은 매우 비효율적)
  • 대소문자 구분 있음 (keyword 필드로만 사용 가능)

🔹 정규식 쿼리

{
  "regexp": {
    "customer_full_name.keyword": {
      "value": "M[a-z]+(y|i)"
    }
  }
}
  • Java 정규식 문법과 동일함
  • 활용 예:
    • .*: 임의의 문자열
    • a[bcd]e: a로 시작해서 b/c/d 중 하나가 가운데, 마지막은 e
    • a.*b: a로 시작해서 b로 끝나는 모든 문자열
  • 굉장히 유연하지만 역시 성능상 위험이 있으므로 실무에서는 사전 필터링과 조합 필수

끄읕

댓글남기기