Skip to content

feat: 검색 사전·관련도를 실측으로 튜닝하고 필터 건수 집계를 추가한다 (#230) - #233

Merged
myh7754 merged 2 commits into
devfrom
feat/230-policy-search-elasticsearch
Sep 6, 2026
Merged

myh7754 merged 2 commits into
devfrom
feat/230-policy-search-elasticsearch

Conversation

@myh7754

@myh7754 myh7754 commented Sep 6, 2026

Copy link
Copy Markdown
Member

배경

PR #232 로 Elasticsearch 검색 이관을 마쳤으나, 사전이 비어 있고 관련도 값이 기본값이었다.
이번 PR 에서 사전을 실데이터로 채우고 값을 실측으로 정했으며, 필터 UI 용 건수 집계를 추가한다.

결과

지표 before (LIKE) after (ES)
0건 검색어 9 / 28 2 / 28

남은 0건 2종(청년도약계좌·도약계좌)은 검색 문제가 아니라 해당 정책이 2025-12-05 로 마감되어 노출 대상이 아니다.

  • 서울 청년 취업 0 → 440건, 상위 3건이 모두 서울 정책으로 교정
  • 일자리를 찾고 있어요 0 → 87건, 월세 지원 청년 0 → 764건
  • 지원금 92 → 50건 — 사라진 42건은 전부 "지원금액" 오탐(nori 가 [지원, 금액] 으로 끊는다)

조용한 실패 두 건

둘 다 에러가 나지 않아 토큰을 직접 찍기 전에는 알 수 없었다.

  • 동의어 7그룹 중 3그룹이 무효 — synonym_graph 는 사전 단어를 같은 분석기로 돌려보는데, 다중 토큰(스타트업 → [스타트업, 스타트, 업])이 나오면 구(phrase)가 되어 단일 토큰과 묶이지 못한다. 한 멤버가 실패하면 그룹 전체가 무효가 되어 학자금, 등록금, 장학금 까지 죽어 있었다.
  • 취준생 → [취, 생] — 생 이 학생·대학생 등 어디에나 있어 관계없는 정책이 걸리고 있었다.

둘 다 userdict_ko.txt 에 단일 토큰으로 등록하고 재색인해 해결했다. 사전 두 개가 물려 있다 — userdict 가 만든 토큰 위에서만 synonym 이 동작한다.

실측으로 정한 값 4개

값 비교한 후보 근거
동의어 7그룹 후보 10그룹 겹침 50~72% 는 실익 없음 · 규모 5배 차는 잠식 → 4그룹 탈락
cross_fields best_fields 서울 청년 취업 291 → 440건
2<70% 후보 6개 3<70% 는 자연어가 87 → 2건 · 상위 5개 결과는 동일
지역 가산점 5 0 / 5 / 15 0이면 전국이 상위 독점 · 15는 과교정

비교 도구는 es/helper/ 에 남겼다.

패싯 집계

GET /api/v1/policies/facets — 목록과 같은 파라미터를 받아 카테고리·지역별 건수를 준다.

각 항목은 자기 자신의 필터를 뺀 조건에서 센다. category=주거 상태에서 카테고리 건수까지 주거로 걸러 세면 나머지 넷이 응답에서 사라져 사용자가 다른 분류로 옮겨갈 수 없다(막다른 골목).

자기 필터 포함(순진)   주거 139  (끝)
자기 필터 제외(구현)   일자리 427 / 금융·복지·문화 325 / 참여·기반 159 / 교육·직업훈련 152 / 주거 139

terms 집계 기본값은 상위 10개라 시도 17개를 그냥 두면 7개가 말없이 잘린다 — 버킷 크기를 명시했다.
ES 장애 시에는 빈 목록을 준다(= 0건이 아니라 "집계 불가"). 목록은 폴백으로 계속 뜨고 건수 배지만 사라진다.

테스트

./gradlew test 전체 통과. 기존 테스트는 건드리지 않았고 패싯 관련 2건을 추가했다.

남은 것

  • 색인 실패 누적 시 재시도 큐 (현재는 빈 인덱스 감지 후 전량 재색인만)

측정 문서: docs/2026-09-06-검색-전환-후-측정.md

Refs #230

🤖 Generated with Claude Code

https://claude.ai/code/session_0124NHgetrZtfPf1jrRd3kko

myh7754 and others added 2 commits September 6, 2026 03:59
기준선 문서의 검색어 28종을 같은 API 에 SEARCH_ES_ENABLED 만 토글해 두 번 측정했다.
raw SQL 과 ES 를 직접 비교하면 마감·노출 필터 적용 여부가 달라 숫자가 오염되므로,
필터 조건을 동일하게 맞춘 뒤 잰다.

0건 검색어 9/28 → 2/28. 남은 2종(청년도약계좌, 도약계좌)은 검색 문제가 아니라
해당 정책이 2025-12-05 로 이미 마감돼 노출 대상이 아니다.

감으로 정한 값이 없도록 후보를 여러 개 두고 결과를 비교했다. 비교 도구는 es/helper 에
남긴다(synonym_candidates.py, tune_msm.py, measure_search.py).

- 동의어 7그룹: 단어별 매칭 건수와 문서 집합의 겹침(자카드)으로 골랐다. 지원금/보조금은
  745 대 143 으로 규모가 5배라 묶으면 작은 쪽이 잠식돼 제외했고, 교육(전체의 44%)도
  같은 이유로 뺐다. 자격증/응시료는 이미 겹침 50~72% 라 실익이 없다.
- best_fields → cross_fields: best_fields 는 한 필드 안에서 조건을 센다.
  "서울 청년 취업" 은 서울이 sidoNames 에, 청년·취업이 title 에 있어 어떤 필드도 세 단어를
  다 갖지 못했다.
- minimum_should_match 는 2<70% 유지. 3<70% 가 건수는 정확하지만 자연어 검색이
  87 → 2건으로 무너진다. 상위 5개 결과는 두 설정이 동일해, 이 선택은 정확도가 아니라
  표시 건수의 문제였다.
- 지역 가산점 5: 전국 정책은 모든 시도에 지역 행이 걸려 지역명 검색에 항상 잡힌다.
  MySQL 이 정렬로 후순위 처리하던 규칙(#199 후속)을 옮기며 빠뜨렸다가 재측정에서 발견했다.
  없으면 "서울 청년 취업" 상위가 전부 전국 정책이고, 15 면 여러 시도를 걸친 정책이
  해당 지역 전용 정책을 밀어낸다.

조용한 실패 두 건을 토큰을 직접 찍어 발견했다(둘 다 에러가 나지 않았다).
- synonym_graph 는 사전의 단어를 같은 분석기로 돌려본 뒤 규칙을 만든다. 여러 토큰이 나오면
  구로 취급돼 단일 토큰과 묶이지 못한다. 스타트업이 [스타트업,스타트,업] 이라 창업과
  묶이지 않았고 같은 그룹의 학자금까지 무효가 됐다 → userdict 에 단일 토큰으로 등록.
- 취준생이 [취,생] 으로 쪼개져 '생'이 든 문서가 걸렸고, 응시료는 [시료] 로 우연히만
  동작했다 → userdict 로 교정.

건수가 줄어든 검색어는 오탐 제거다. 지원금 92 → 50 에서 사라진 42건은 전부 "지원금액"
으로, nori 가 [지원, 금액] 으로 끊는 게 맞다. LIKE 는 글자가 이어져 있다는 이유로 잡았다.

검증: 테스트 544건 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
목록과 같은 파라미터를 받는 GET /api/v1/policies/facets 를 추가한다.

각 항목은 자기 자신의 필터를 뺀 조건에서 센다. category=주거 상태에서
카테고리 건수까지 주거로 걸러 세면 나머지 넷이 응답에서 아예 사라져
사용자가 다른 분류로 옮겨갈 수 없다(막다른 골목). 기준 질의에서 카테고리·
지역 필터를 빼 두고 패싯별 filter 집계로 상대방 조건만 다시 씌운다.

terms 집계 기본값은 상위 10개라 시도 17개를 그냥 두면 7개가 말없이
잘린다 — 버킷 크기를 명시했다.

ES 장애 시에는 빈 목록을 준다. 같은 집계를 MySQL 로 하려면 조건 8개가
걸린 GROUP BY 를 두 벌 더 만들어야 하는데, 목록은 폴백으로 계속 뜨고
건수 배지만 사라지는 정도라 그 값을 하지 않았다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0124NHgetrZtfPf1jrRd3kko
@myh7754
myh7754 merged commit 410616a into dev Sep 6, 2026
0 of 2 checks passed
@kilo-code-bot

kilo-code-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

Kilo Code Review could not run — your account is out of credits.

Add credits or switch to a free model to enable reviews on this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant