티스토리 뷰
MongoDB Hybrid Search 완전 정리: $vectorSearch, $rankFusion, $scoreFusion 공식 문서 기반 분석
ebson 2026. 2. 14. 08:36
MongoDB Hybrid Search를 이해하며 정리한 몇 가지 포인트
최근 MongoDB Atlas에서 Hybrid Search를 적용하면서 $vectorSearch, $rankFusion, $scoreFusion 동작을 공식 문서를 기준으로 다시 정리해보았습니다. 기능 자체는 비교적 단순해 보이지만, 실제 동작 순서를 정확히 이해하지 않으면 의도와 다른 결과가 나올 수 있어 정리해둡니다.
본 글의 내용은 MongoDB 공식 매뉴얼과 Atlas Hybrid Search 가이드, 그리고 RRF 논문을 기준으로 확인한 내용입니다.
$vectorSearch와 limit의 실제 동작
$vectorSearch는 Atlas Vector Search 인덱스를 기반으로 ANN(Approximate Nearest Neighbor) 검색을 수행하는 stage입니다.
공식 문서에 따르면 내부적으로는 다음과 같은 흐름으로 동작합니다.
- numCandidates: 내부 후보군을 먼저 수집
- limit: 최종적으로 파이프라인에 전달될 문서 수 결정
중요한 점은 limit이 적용된 이후의 문서 집합만 다음 stage로 전달된다는 점입니다. 따라서 이후에 $addFields, $match, $sort 등을 추가하더라도 이미 잘려 나간 문서를 복원할 수는 없습니다.
이 부분은 단순하지만, 점수 보정이나 후처리를 설계할 때 상당히 중요한 전제 조건이 됩니다.
또한 Vector Search에서 사용하는 filter는 인덱스에 정의된 필드만 사용 가능하다는 제약이 있습니다. 반면 일반 find() 쿼리는 이러한 제약이 없습니다. Hybrid 구성을 설계할 때 인덱스 설계와 필터 전략을 함께 고려해야 하는 이유입니다.
$rankFusion과 RRF 공식
MongoDB 8.0부터 $rankFusion이 도입되었습니다. 이는 Reciprocal Rank Fusion(RRF) 알고리즘을 기반으로 여러 검색 결과를 결합하는 stage입니다.
RRF 공식은 다음 논문에서 제안되었습니다.
Reciprocal rank fusion outperforms condorcet and individual rank learning methods
공식은 다음과 같습니다.
RRF_score(d) = Σ w_r × (1 / (k + rank_r(d)))
여기서
- rank_r(d)는 각 검색 결과에서의 순위
- w_r는 가중치
- k는 smoothing 상수입니다.
논문에서는 k=60을 권장 상수로 사용합니다. MongoDB에서도 기본값(default)으로 60을 사용하며, 필요에 따라 조정 가능합니다. “고정값”이라기보다는 기본 설정이라고 이해하는 것이 정확합니다.
예를 들어, k=60일 때:
- 1위 문서: 1 / (60 + 1) ≈ 0.0164
- 60위 문서: 1 / (60 + 60) ≈ 0.0083
상위권 간 점수 차이가 크지 않도록 완만하게 만드는 구조입니다. 이는 서로 다른 검색 전략(예: 텍스트 검색과 벡터 검색)을 결합할 때 특정 하나가 과도하게 지배하지 않도록 하기 위한 설계로 보입니다.
MongoDB 8.0에서 $rankFusion이 도입되었고, 8.1 이후에는 Vector Search와의 통합 사용이 보다 자연스럽게 지원됩니다.
$scoreFusion과 점수 기반 결합
MongoDB 8.2에서는 $scoreFusion이 Public Preview로 추가되었습니다.
$rankFusion이 “순위 기반” 결합이라면, $scoreFusion은 “점수 기반” 결합입니다. 입력 점수를 정규화한 뒤 가중 평균 방식으로 결합합니다.
공식 문서 기준으로 다음과 같은 normalization 옵션이 제공됩니다.
- none
- sigmoid
- minMaxScaler
이처럼 내부적으로 정규화 전략을 제공한다는 점이 특징입니다. 점수 스케일이 다른 검색 결과를 결합할 때 별도의 수작업 없이 비교적 안정적인 합성을 할 수 있습니다.
$dateDiff와 $exp를 활용한 점수 보정
벡터 검색 점수에 시간 가중치를 곱하는 방식도 종종 사용합니다. MongoDB 집계 연산자만으로도 이를 구현할 수 있습니다.
$dateDiff는 MongoDB 5.0부터 지원되며, 다음과 같은 형태로 사용할 수 있습니다.
- startDate
- endDate
- unit
예를 들어:
- startDate: $published_at
- endDate: $$NOW
- unit: "day"
이렇게 계산된 일 수를 기반으로 감쇠 함수를 적용할 수 있습니다.
$exp는 MongoDB 4.2부터 지원되며, e^x 값을 계산합니다. 이를 이용해 다음과 같은 형태의 감쇠를 적용할 수 있습니다.
exp(-λ × days)
이 방식은 개념적으로는 점수 기반 결합과 유사한 효과를 낼 수 있습니다. 다만 $scoreFusion과 동일한 기능을 완전히 재현한다고 보기는 어렵습니다.
$scoreFusion은 다중 입력 파이프라인을 직접 받아 내부에서 정규화와 결합을 수행하지만, $addFields 기반 구현은 단일 파이프라인 내에서 수동으로 점수를 조정하는 방식이기 때문입니다. 구조적 차이는 존재합니다.
Hybrid Search를 구성하며 느낀 점
Hybrid Search는 단순히 “텍스트 + 벡터를 같이 쓰는 것” 이상으로, 각 stage의 동작 순서와 점수 스케일을 정확히 이해하는 것이 중요하다고 느꼈습니다.
특히 다음과 같은 부분이 인상적이었습니다.
- $vectorSearch의 limit은 후속 stage 이전에 이미 결과를 잘라냅니다.
- $rankFusion은 순위 기반으로 안정적인 결합을 제공합니다.
- $scoreFusion은 점수 정규화를 내장하여 보다 정교한 결합을 지원합니다.
- $dateDiff와 $exp 같은 기본 연산자만으로도 충분히 의미 있는 점수 보정이 가능합니다.
공식 문서를 다시 읽으며 정리해보니, 각 기능이 독립적으로 설계되었다기보다는 점진적으로 Hybrid Search를 확장해온 과정으로 보였습니다.
실제 서비스에 적용할 때는 단순히 기능을 사용하는 것보다, 검색 전략의 의도와 점수 흐름을 먼저 설계하는 것이 더 중요하다는 점을 다시 한 번 확인하게 되었습니다. 앞으로도 공식 문서를 기반으로 동작을 하나씩 검증하며 적용 범위를 넓혀본다면 좋을 것 같다고 생각합니다.
'TECH AND AI' 카테고리의 다른 글
| RAG 아키텍처 발전사 2편 - Advanced RAG: 검색 품질을 높이기 위한 네 가지 접근 (0) | 2026.03.06 |
|---|---|
| /RAG 아키텍처 발전사 1편 - Naive RAG: 검색 증강 생성의 시작점 (0) | 2026.03.06 |
| Vector Search의 최신 문서 누락 문제 해결: MongoDB 8.0에서 RRF 기반 Score Fusion 구현 전략 (0) | 2026.02.14 |
| MongoDB Vector Search 한계 극복하기: Exponential Decay 기반 최신성 점수와 Score Fusion 적용 경험 (1) | 2026.02.14 |
| MongoDB Aggregation Pipeline으로 구현하는 서버사이드 텍스트 분석 전략 (0) | 2026.02.06 |
- Total
- Today
- Yesterday
- DB 트랜잭션
- mybatis
- 백엔드 아키텍처
- Hot Key 문제
- Java Performance
- Cache Aside
- Redis 성능 개선
- TTL 설계
- 스레드 생명주기
- 캐시 성능 비교
- 백엔드 성능 튜닝
- 동시성처리
- 캐시 장애
- Spring Batch
- Eager Initialization
- Cache Avalanche
- 백엔드 성능 설계
- spring batch 5
- DB 인덱스 성능
- Cache Penetration
- 캐시와 인덱스
- Double-Checked Locking
- InterruptedException
- 트랜잭션 관리
- 트래픽 처리
- Initialization-on-Demand Holder Idiom
- 백엔드 성능
- Redis 캐시 전략
- Enum 기반 싱글톤
- Redis vs DB
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |

