feat: 검색 사전·관련도를 실측으로 튜닝하고 필터 건수 집계를 추가한다 (#230) - #233
Merged
Merged
Conversation
기준선 문서의 검색어 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
|
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. |
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
배경
PR #232 로 Elasticsearch 검색 이관을 마쳤으나, 사전이 비어 있고 관련도 값이 기본값이었다.
이번 PR 에서 사전을 실데이터로 채우고 값을 실측으로 정했으며, 필터 UI 용 건수 집계를 추가한다.
결과
남은 0건 2종(
청년도약계좌·도약계좌)은 검색 문제가 아니라 해당 정책이2025-12-05로 마감되어 노출 대상이 아니다.서울 청년 취업0 → 440건, 상위 3건이 모두 서울 정책으로 교정일자리를 찾고 있어요0 → 87건,월세 지원 청년0 → 764건지원금92 → 50건 — 사라진 42건은 전부 "지원금액" 오탐(nori 가[지원, 금액]으로 끊는다)조용한 실패 두 건
둘 다 에러가 나지 않아 토큰을 직접 찍기 전에는 알 수 없었다.
synonym_graph는 사전 단어를 같은 분석기로 돌려보는데, 다중 토큰(스타트업 → [스타트업, 스타트, 업])이 나오면 구(phrase)가 되어 단일 토큰과 묶이지 못한다. 한 멤버가 실패하면 그룹 전체가 무효가 되어학자금, 등록금, 장학금까지 죽어 있었다.취준생 → [취, 생]—생이 학생·대학생 등 어디에나 있어 관계없는 정책이 걸리고 있었다.둘 다
userdict_ko.txt에 단일 토큰으로 등록하고 재색인해 해결했다. 사전 두 개가 물려 있다 — userdict 가 만든 토큰 위에서만 synonym 이 동작한다.실측으로 정한 값 4개
cross_fieldsbest_fields서울 청년 취업291 → 440건2<70%3<70%는 자연어가 87 → 2건 · 상위 5개 결과는 동일비교 도구는
es/helper/에 남겼다.패싯 집계
GET /api/v1/policies/facets— 목록과 같은 파라미터를 받아 카테고리·지역별 건수를 준다.각 항목은 자기 자신의 필터를 뺀 조건에서 센다.
category=주거상태에서 카테고리 건수까지 주거로 걸러 세면 나머지 넷이 응답에서 사라져 사용자가 다른 분류로 옮겨갈 수 없다(막다른 골목).terms집계 기본값은 상위 10개라 시도 17개를 그냥 두면 7개가 말없이 잘린다 — 버킷 크기를 명시했다.ES 장애 시에는 빈 목록을 준다(= 0건이 아니라 "집계 불가"). 목록은 폴백으로 계속 뜨고 건수 배지만 사라진다.
테스트
./gradlew test전체 통과. 기존 테스트는 건드리지 않았고 패싯 관련 2건을 추가했다.남은 것
측정 문서:
docs/2026-09-06-검색-전환-후-측정.mdRefs #230
🤖 Generated with Claude Code
https://claude.ai/code/session_0124NHgetrZtfPf1jrRd3kko