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 (큰 텍스트)
}
여기서 시험 함정이 하나 있어요. fvh는 term_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 점수 합치기- Highlighting —
pre_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 테스트
시리즈 다른 편
- 1편 — 기본 개념·Cluster·Shard
- 2편 — Mapping·데이터 타입
- 3편 — Analyzer·Tokenizer·한국어
- 4편 — Query DSL
- 5편 — Full-text Search·Relevance (현재 글)
- 6편 — Aggregations
- 7편 — Bulk API·Reindex
- 8편 — Spring Data Elasticsearch
- 9편 — 검색 엔진 프로젝트 설계
- 10편 — Security·인증·인가·TLS
공식 문서: Full Text Queries / Suggesters 에서 더 깊이.
다음 글(6편)에서는 Aggregations — Metrics·Bucket·Pipeline 3 종, 통계·그룹화·시계열 분석까지 풀어 갑니다.