카테고리 없음

ElasticSearch korean_nori 분석기와 토큰화 문제

heesoohi 2025. 5. 1. 21:19

🧩 문제 상황: 형용사 키워드 검색 실패

  • 현상: description = "아늑한" 검색 시, 결과가 빈 배열로 반환됨
  • 원인 분석:
    • korean_nori 분석기는 텍스트를 형태소 단위로 분해
    • "아늑한" → "아늑", "하", "ㄴ"으로 과도하게 분해
    • Criteria.contains()는 내부적으로 아늑한* 형식의 wildcard 쿼리를 생성하지만, 이 쿼리는 분해된 토큰과 매칭되지 않음 → 검색 실패

🧪 토큰화 예시 (curl 테스트 결과)

POST _analyze
{
  "analyzer": "korean_nori",
  "text": "아늑한 분위기의 카페입니다."
}

 

결과 토큰들:

  • "아늑", "하", "ᆫ", "분위기", "의", "카페", "이", "ᄇ니다"

→ "아늑한"이라는 전체 단어는 인덱스에 존재하지 않음

📌 필드별 영향도

필드명 내용 예시 영향도
name "가 카페" 낮음
address "서울시 강남구" 낮음
description "맛있는 커피가 있는 아늑한 카페" 높음
  • name, address: 명사 위주의 짧은 문자열 → korean_nori와 호환성 양호
  • description: 문장형 텍스트 → 세밀한 토큰화로 인해 형용사 검색에서 문제 발생

💡 해결 방안

1. Criteria.contains() → Criteria.matches() 변경

  • contains()는 wildcard 쿼리 생성 → 토큰화된 텍스트와 매칭 어려움
  • matches()는 match 쿼리 사용 → 형태소 기반 토큰과 유연한 매칭 가능

조치 후 "아늑한" 검색 시, 적절한 결과 반환됨

2. 추가로 고려한 사항들: 사용자 정의 사전, 동의어 사전, nGram 분석기 도입 고려

도입은 보류, 아래 이유로 필요성 낮음

 

장점

  • 부분 문자열 검색 유연성 확보 가능
  • "아늑한" 같은 단어를 분리 없이 유지
  • "포근한", "따뜻한" 등 동의어 매핑 → 검색 포괄성 증가
  • 도메인 키워드("맛있는", "분위기 좋은") 포함 가능
  • LIKE 검색과 유사한 유연성 제공

단점

  • 사전 변경 시 인덱스 재생성 필요
  • 사전 관리에 지속적인 리소스 요구
  • 과도한 통합 시, 다른 키워드 검색 정확도 저하 가능
  • 동의어 매핑은 검색 시 추가 연산 → 성능 이슈

⚖️ 최종 판단

  • 실사용 시나리오에서, "아늑한"처럼 형용사보다는 "카페", "고깃집" 등 명사 기반 검색이 주를 이룸
  • 현재 상태에서 명사 키워드 검색 정확도는 높음
  • matches()로 변경한 후 형용사 검색도 보완됨
  • 관련성 높은 검색 결과만 노출하려면 min_score 설정을 추가해도 좋을듯
  • ex) 
Query query = new CriteriaQuery(criteria) .setPageable(pageable) .addSort(Sort.by(Sort.Order.desc("_score"))) .setMinScore(1.0f);

 

-> 따라서 사용자 정의 사전 도입은 보류하고, 상황에 따라 선택적으로 도입하는 방향으로 결정

✅ 결론

  • korean_nori 분석기와 형용사 검색 문제는 Criteria.matches()로 전환하여 해결
  • 현재 사용 흐름에서는 명사 위주 검색 패턴이 주를 이루기 때문에, 현상 유지로도 실용성 충분
  • 사용자 정의 사전 도입은 향후 필요 시 고려