Elasticsearch 마스터 — Full-text Search·Relevance

2026-05-03확률과 통계 마스터 노트

Elasticsearch 마스터 노트 시리즈 5편. Full-text Search의 BM25 점수 계산 알고리즘, Relevance Tuning(boost·tie_breaker), Highlighting으로 매칭 부분 강조, Suggester(Term·Phrase·Completion)로 자동완성·오타 교정, Synonym 검색, 검색 품질 측정까지.

이 글은 Elasticsearch 마스터 노트 시리즈의 다섯 번째 편입니다. 4편(Query DSL)에서 검색 표현을 봤다면, 이번엔 검색의 본질 — Full-text Search.

match 쿼리가 어떻게 결과를 정렬? BM25 점수. 사용자에게 매칭 부분 보여주기? Highlighting. 오타 교정? Suggester. 검색 엔진의 핵심 기능들.

처음 Full-text Search가 어렵게 느껴지는 이유

처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, 점수가 어떻게 계산되는지 막연합니다. 둘째, Suggester 종류가 헷갈립니다.

해결법은 한 가지예요. "BM25 = 단어 빈도 + 희귀도 + 길이 정규화" 한 줄. 단어가 자주·희귀할수록·문서 짧을수록 점수 ↑. 이 직감만 잡으면 끝.

BM25 — 점수 계산

score(D, Q) = Σ IDF(qi) * (TF(qi, D) * (k1+1)) / (TF + k1 * (1-b + b*|D|/avgdl))

TF (Term Frequency): 문서 안 단어 빈도
IDF (Inverse Document Frequency): 전체 문서 중 단어 희귀도
|D|: 문서 길이
avgdl: 평균 문서 길이

핵심:

  • TF 높음 → 점수 ↑ (해당 단어 자주)
  • IDF 높음 → 점수 ↑ (희귀 단어 = 의미 있음)
  • 문서 길수록 → 점수 ↓ (정규화)

여기서 정말 중요한 시험 함정 — BM25 (Lucene 8+) = TF-IDF의 발전형. 이전엔 TF-IDF, 지금은 BM25 표준. 둘 다 같은 원리·세부 차이만.

explain으로 점수 분석

GET /products/_search
{
  "explain": true,
  "query": {
    "match": { "description": "Spring Framework" }
  }
}

각 문서별 점수 계산 과정 보임. 디버깅·튜닝.

Boost — 가중치

필드별 boost

"multi_match": {
  "query": "Spring",
  "fields": ["title^3", "description^1", "tags^2"]
}

title이 3배 중요. 매칭 시 점수 ↑.

쿼리별 boost

"bool": {
  "should": [
    { "match": { "name": { "query": "spring", "boost": 3 } } },
    { "match": { "description": "spring" } }
  ]
}

Index-time boost (deprecated)

"name": {
  "type": "text",
  "boost": 2.0    # X — Lucene 5+ 사용 안 함
}

여기서 시험 함정이 하나 있어요. Index-time boost는 deprecated. Query-time boost만 사용. 변경 유연.

tie_breaker — multi_match 점수 합치기

"multi_match": {
  "query": "Spring",
  "fields": ["name", "description"],
  "type": "best_fields",
  "tie_breaker": 0.3
}

기본 best_fields = 가장 높은 필드 점수만. tie_breaker = 다른 필드도 일부 반영.

Highlighting — 매칭 부분 강조

GET /products/_search
{
  "query": {
    "match": { "description": "Spring Framework" }
  },
  "highlight": {
    "fields": {
      "description": {
        "pre_tags": ["<em>"],
        "post_tags": ["</em>"]
      }
    }
  }
}

# 결과
{
  "hits": [
    {
      "_source": { ... },
      "highlight": {
        "description": [
          "Java <em>Spring</em> <em>Framework</em> 입문서"
        ]
      }
    }
  ]
}

검색 결과 페이지에서 표시. 사용자 친화 ↑.

Highlighter 종류

"highlight": {
  "type": "unified",        # 기본 (BM25 기반)
  "type": "plain",          # 단순
  "type": "fvh"             # Fast Vector Highlighter (큰 텍스트)
}

여기서 시험 함정이 하나 있어요. fvhterm_vector: with_positions_offsets 매핑 필요. 큰 텍스트 highlighting 빠름.

Suggester — 자동완성·오타 교정

1. Completion Suggester — 자동완성

PUT /products
{
  "mappings": {
    "properties": {
      "name_suggest": {
        "type": "completion"
      }
    }
  }
}

POST /products/_doc
{
  "name": "Spring Boot Book",
  "name_suggest": "Spring Boot Book"
}

# 자동완성 검색
GET /products/_search
{
  "suggest": {
    "product-suggest": {
      "prefix": "spr",
      "completion": {
        "field": "name_suggest",
        "size": 5
      }
    }
  }
}

매우 빠름. 자동완성 표준.

2. Term Suggester — 오타 교정 (단어)

GET /products/_search
{
  "suggest": {
    "fix-suggest": {
      "text": "javea",
      "term": {
        "field": "name"
      }
    }
  }
}

# 결과
{
  "suggest": {
    "fix-suggest": [
      {
        "options": [
          { "text": "java", "score": 0.75, "freq": 100 }
        ]
      }
    ]
  }
}

단어 단위 오타 교정.

3. Phrase Suggester — 오타 교정 (구문)

GET /products/_search
{
  "suggest": {
    "phrase-suggest": {
      "text": "spring framwork",
      "phrase": {
        "field": "name",
        "size": 1
      }
    }
  }
}

전체 구문 교정 — "spring framework" 추천.

여기서 정말 중요한 시험 함정 — 3 Suggester 사용처:

  • Completion = 자동완성 (입력 중)
  • Term = 단어 교정 (검색 후)
  • Phrase = "Did you mean?" (검색 후)

"Did you mean?" 패턴

GET /products/_search
{
  "query": {
    "match": { "name": "spring framwork" }
  },
  "suggest": {
    "did-you-mean": {
      "text": "spring framwork",
      "phrase": {
        "field": "name",
        "max_errors": 0.5
      }
    }
  }
}

# 결과 검색 적음 + 추천 표시
# "Did you mean: spring framework?"

검색 결과 X 또는 적을 때 추천 표시. 사용자 경험 ↑.

Synonym — 동의어 검색

PUT /products
{
  "settings": {
    "analysis": {
      "filter": {
        "synonym_filter": {
          "type": "synonym",
          "synonyms": [
            "스마트폰, 휴대폰, 핸드폰",
            "노트북, 랩탑"
          ]
        }
      },
      "analyzer": {
        "synonym_analyzer": {
          "tokenizer": "nori_tokenizer",
          "filter": ["lowercase", "synonym_filter"]
        }
      }
    }
  }
}

스마트폰 검색 → 휴대폰·핸드폰도 포함. 사용자 친화.

query_string — 자유 검색

GET /products/_search
{
  "query": {
    "query_string": {
      "query": "name:Spring AND price:>10000",
      "default_field": "description"
    }
  }
}

Lucene 쿼리 문법. 자유롭지만 위험 (사용자 입력 직접 X).

# 더 안전한 simple_query_string
GET /products/_search
{
  "query": {
    "simple_query_string": {
      "query": "spring +boot -kotlin \"exact phrase\"",
      "fields": ["name", "description"]
    }
  }
}

여기서 시험 함정이 하나 있어요. query_string은 Lucene 문법 노출 = 보안 위험. 사용자 입력 = simple_query_string 또는 match로.

Score 조정 — boosting query

"query": {
  "boosting": {
    "positive": {
      "match": { "name": "spring" }
    },
    "negative": {
      "match": { "status": "deprecated" }
    },
    "negative_boost": 0.2
  }
}

deprecated인 문서 점수 80% 감소. 매칭은 OK·우선 순위 ↓.

Constant Score — 점수 고정

"query": {
  "constant_score": {
    "filter": {
      "term": { "category": "IT" }
    },
    "boost": 1.2
  }
}

필터링 + 고정 점수. 점수 계산 비용 X.

검색 품질 측정

Precision (정확도): 검색 결과 중 관련 결과 비율
Recall (재현율):   전체 관련 결과 중 검색된 비율
F1 Score:           Precision과 Recall의 조화 평균
NDCG:               순위 품질 지표

운영 환경에선 사용자 클릭·CTR 측정.

부분 검색 한계

match: 토큰 단위 매칭 (분석된 단어)
prefix: 접두어
wildcard: 와일드카드 (느림)

부분 검색 = N-Gram·Edge N-Gram (3편)

검색 튜닝 워크플로우

1. 기본 match·multi_match 시작
2. 필드별 boost 조정
3. Synonym 추가 (도메인 용어)
4. Analyzer 최적화 (한국어 = Nori)
5. Highlighting 추가
6. Completion·Phrase Suggester
7. Function Score (인기·날짜 등)
8. A/B 테스트로 검증

시험 직전 한 번 더 — 자주 헷갈리는 함정 모음

여기까지가 5편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.

  • BM25 = TF-IDF 발전형, 점수 계산 표준
  • TF·IDF·문서 길이 정규화
  • explain: true = 점수 계산 과정
  • Boost = 가중치 (^3 또는 boost: N)
  • Index-time boost는 deprecated → Query-time만
  • tie_breaker = multi_match 점수 합치기
  • Highlightingpre_tags·post_tags
  • Highlighter — unified·plain·fvh (큰 텍스트)
  • Suggester 3종:
  • Completion = 자동완성 (입력 중)
  • Term = 단어 오타 교정
  • Phrase = "Did you mean?" 구문 교정
  • Synonym = 동의어 검색 (스마트폰=휴대폰)
  • 동의어 분석기 + Nori 결합
  • query_string = 보안 위험 (Lucene 노출)
  • 사용자 입력 = simple_query_string 또는 match
  • Boosting Query = positive·negative_boost (점수 감소)
  • Constant Score = 필터 + 고정 점수
  • 검색 품질 — Precision·Recall·F1·NDCG
  • 운영 = 사용자 클릭·CTR
  • 부분 검색 = N-Gram·Edge N-Gram (3편)
  • 튜닝 워크플로우 — match → boost → synonym → analyzer → highlight → suggester → function score → A/B 테스트

시리즈 다른 편

공식 문서: Full Text Queries / Suggesters 에서 더 깊이.

다음 글(6편)에서는 Aggregations — Metrics·Bucket·Pipeline 3 종, 통계·그룹화·시계열 분석까지 풀어 갑니다.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!