Query
Query 카테고리의 모든 포스트 - 한국어
3개의 포스트
📉 쿼리 설계 실수 예시 10가지
Elasticsearch에서 쿼리 성능과 정확도에 영향을 주는 대표적인 실수 10가지를 정리한 문서입니다.
실전 검색 엔진에서 흔히 발생하며, 사전에 방지하거나 리팩터링 시 주의해야 할 포인트입니다.
1. ❌ filter
없이 must
만 사용하는 경우
must
는 relevance score 계산 포함 → 성능 저하- 단순 조건은 반드시
filter
사용
"bool": {
"must": [ { "term": { "status": "active" } } ] // ❌
}
✅ 개선 예시:
"bool": {
"filter": [ { "term": { "status": "active" } } ]
}
2. ❌ from
값을 너무 크게 사용하는 경우
from: 10000
이상은 성능 문제 + 기본 max_result_window 초과 오류- deep pagination 비효율적
✅ 해결 방법:
Query DSL 정리 (실무 중심)
🔍 1. term 쿼리
{ "term": { "status": "active" } }
- 정확히 일치하는 값을 찾을 때 사용
- 형태소 분석되지 않은 필드 (
keyword
,int
,boolean
)에 적합 text
필드에는text.keyword
로 사용해야 정확히 일치- 실무: 상태, ID, 카테고리 코드 등
🔍 2. terms 쿼리
{ "terms": { "status": ["active", "ready"] } }
- 여러 값 중 하나라도 일치하면 검색
IN
조건처럼 사용- 65,536개 이상의 값은 사용 불가 (성능 저하)
🔍 3. match 쿼리
{ "match": { "title": "엘라스틱 서치 강의" } }
- text 필드에 사용
- 형태소 분석됨 (자연어 검색)
- 완전 일치 아님 → 관련된 문서도 나올 수 있음
🔍 4. match_phrase 쿼리
{ "match_phrase": { "title": "엘라스틱 서치 강의" } }
- 정확한 문장 일치 (순서 유지)
- 중간 단어 빠지면 매칭되지 않음
🔍 5. bool 쿼리
{
"bool": {
"must": [ ... ], // 모두 만족 (AND)
"should": [ ... ], // 하나 이상 만족하면 OK (OR 느낌)
"must_not": [ ... ], // 만족하면 제외
"filter": [ ... ] // 점수 영향 없음, 빠름
}
}
✔️ 실전 사용 예
{
"bool": {
"must": [
{ "term": { "status": "active" } },
{ "match": { "title": "엘라스틱" } }
],
"filter": [
{ "range": { "created_date": { "gte": "now-7d/d" } } }
],
"must_not": [
{ "term": { "is_deleted": true } }
]
}
}
🔍 6. range 쿼리
{ "range": { "price": { "gte": 1000, "lte": 5000 } } }
- 숫자나 날짜 필드에 범위 조건 적용
- 실무: 가격, 날짜 필터링
🔍 7. exists 쿼리
{ "exists": { "field": "thumbnail_url" } }
- 필드가 존재하는지 확인
🔍 8. wildcard / regexp / prefix
{ "wildcard": { "title": "엘라*" } }
wildcard
,regexp
,prefix
는 느릴 수 있음- analyzed 되지 않은
keyword
필드에 사용 추천
🔍 9. multi_match
{
"multi_match": {
"query": "검색어",
"fields": ["title", "description"]
}
}
- 여러 필드에 match 쿼리 적용
🔍 10. script 쿼리
{
"script_score": {
"query": { "match_all": {} },
"script": {
"source": "doc['click_count'].value * 0.5 + doc['like_count'].value"
}
}
}
- 스크립트 기반 동적 스코어
- 캐시 안 되고 느릴 수 있음 → 최소 사용 권장
✅ 실무 패턴 정리
목적 | 쿼리 |
---|---|
필터링 + 검색 | bool + must + filter |
정확 조건 | term , terms |
자연어 검색 | match , multi_match |
날짜 조건 | range (filter 사용 권장) |
제외 조건 | must_not |
가중치 적용 | should , script_score |
추천 기능 | should + minimum_should_match |
📌 추천 실전 예시
1. 필터 조건 + 본문 검색
{
"bool": {
"must": [
{ "match": { "content": "엘라스틱서치 설정법" } }
],
"filter": [
{ "term": { "status": "published" } },
{ "range": { "created_date": { "gte": "now-1M/M" } } }
]
}
}
2. 추천어 기반 검색
{
"bool": {
"must": [
{ "term": { "status": "active" } }
],
"should": [
{ "match": { "title": "엘라스틱" } },
{ "match": { "tags": "검색엔진" } }
],
"minimum_should_match": 1
}
}
⚠️ 주의할 점
text
필드에는term
❌ →.keyword
사용match
는 유사도 기반 → 정확 검색은term
filter
는 점수 계산 안 함 → 빠름should
만 있을 경우minimum_should_match
설정 안 하면 모두 미일치해도 통과 가능script
는 느림wildcard
,regexp
는 느림 → 되도록ngram
대체 고려
🛠️ 매핑 + Analyzer + 검색 쿼리 종합 실습
이 문서는 Elasticsearch에서
posts
인덱스를 대상으로
매핑 설계, custom analyzer 구성, 검색 쿼리 실습을 종합적으로 연습하기 위한 실전 예제입니다.
1. 🎯 목표 요약
- 커스텀 analyzer를 적용한
posts
인덱스 생성 title
,tags
,content
필드를 적절히 text/keyword로 구성- 검색 쿼리에서 match, filter, sort, search_after 등 실제 활용
2. ⚙️ 커스텀 Analyzer + 매핑 예제
PUT http://localhost:9200/posts
{
"settings": {
"analysis": {
"tokenizer": {
"my_nori_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed"
}
},
"filter": {
"my_pos_filter": {
"type": "nori_part_of_speech",
"stoptags": ["E", "IC", "J"]
},
"my_stop_filter": {
"type": "stop",
"stopwords": ["은", "는", "이", "가"]
}
},
"analyzer": {
"my_korean_analyzer": {
"type": "custom",
"tokenizer": "my_nori_tokenizer",
"filter": [
"lowercase",
"my_pos_filter",
"my_stop_filter"
]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "my_korean_analyzer",
"fields": {
"raw": { "type": "keyword" }
}
},
"tags": {
"type": "keyword"
},
"content": {
"type": "text",
"analyzer": "my_korean_analyzer"
},
"author": {
"type": "keyword"
},
"created_at": {
"type": "date"
}
}
}
}
3. 📦 예시 문서 삽입 (Bulk API)
POST http://localhost:9200/posts/_bulk
{ "index": {} }
{ "title": "카카오페이 서비스 출시", "tags": ["fintech", "kakao"], "content": "카카오의 새로운 간편결제 서비스가 시작되었습니다.", "author": "alice", "created_at": "2024-01-01T10:00:00Z" }
{ "index": {} }
{ "title": "네이버 지도 업그레이드", "tags": ["map", "naver"], "content": "지도 검색이 더 빠르고 정확해졌습니다.", "author": "bob", "created_at": "2024-02-15T12:30:00Z" }
{ "index": {} }
{ "title": "구글 검색 AI 도입", "tags": ["ai", "google"], "content": "AI로 더 똑똑한 검색이 가능해졌습니다.", "author": "charlie", "created_at": "2024-03-10T15:00:00Z" }
4. 🔍 검색 쿼리 예제
🎯 4-1. 카카오
를 포함하고 fintech 태그를 가진 문서 검색
POST http://localhost:9200/posts/_search
{
"query": {
"bool": {
"must": [
{ "match": { "content": "카카오" } }
],
"filter": [
{ "term": { "tags": "fintech" } }
]
}
}
}
🧭 4-2. 정렬 + search_after를 사용한 페이징 검색
POST http://localhost:9200/posts/_search
{
"size": 2,
"query": {
"match_all": {}
},
"sort": [
{ "created_at": "desc" },
{ "_id": "asc" }
],
"search_after": ["2024-01-01T10:00:00Z", "doc_id_2"]
}
🔍 4-3. title 정렬 (multi-field 사용)
POST http://localhost:9200/posts/_search
{
"query": {
"match": {
"title": "검색"
}
},
"sort": [
{ "title.raw": "asc" }
]
}
✅ 요약
항목 | 설명 |
---|---|
custom analyzer 설정 | nori tokenizer + 불용어 + 품사 필터 적용 |
multi-field 구성 | title은 text + keyword 함께 사용 (raw 정렬용) |
검색 쿼리 활용 | match, filter, sort, search_after 조합 |
Bulk 삽입 | 대량 데이터 테스트를 위한 빠른 삽입 |