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 삽입대량 데이터 테스트를 위한 빠른 삽입