fix(p10-1a-1): apply code_lang + repo filters in lexical SQL and filter_chunks (dogfood) #144

Merged
altair823 merged 1 commits from fix/p10-1a-1-code-lang-repo-filter-sql into main 2026-05-20 03:34:55 +00:00
Owner

요약

--code-lang LANG / --repo NAME CLI filter 가 lexical + vector retriever SQL 에서 무시되어 모든 doc (markdown / pdf / image 포함) 이 결과에 섞여 반환되던 production bug. p10-1A-1 (PR #139) 머지부터 잠복 — 1A-2 / 1B 회차에서도 발견 못 함 (회귀 테스트는 SearchFilters 필드 유무만 검증, SQL 적용은 미검증). p10-1B dogfooding (httpx + zod + lodash clone) 으로 표면화.

진단

  • CLI: --code-lang / --repo parse 정상, SearchFilters { code_lang: Vec<String>, repo: Vec<String> } 에 set 됨.
  • SearchFilters (kebab-core): 두 필드 1A-1 추가됨.
  • kebab-search/src/lexical.rs: filters.code_lang / filters.repoSQL WHERE 절에 반영 안 함.
  • kebab-store-sqlite/src/filters.rs (vector 가 post-filter 로 사용): 동일 누락.
  • 결과: filter 무시 → 모든 doc 반환 → markdown hit 우선 노출.

Fix

두 retriever 의 SQL 에 동일 IN-list filter 추가:

AND json_extract(d.metadata_json, '$.code_lang') IN (?, ?, ...)
AND json_extract(d.metadata_json, '$.repo') IN (?, ?, ...)

documents.metadata_json 의 JSON path 는 Task 9 의 code_lang_breakdown 쿼리와 동일 source-of-truth 사용. media filter (1A-1 도 추가했던) 와 동일 패턴.

회귀 테스트

4건 추가 (모두 신규):

  • kebab-search/tests/lexical.rs::lexical_filter_by_code_lang
  • kebab-search/tests/lexical.rs::lexical_filter_by_repo
  • kebab-store-sqlite::filters::tests::filter_chunks_code_lang_keeps_matching_lang
  • kebab-store-sqlite::filters::tests::filter_chunks_repo_keeps_matching_repo

검증

per-crate 테스트 + dogfooding 재실행:

cargo test -p kebab-search --lib            # 30 passed, 0 failed
cargo test -p kebab-search --test lexical   # 23 passed, 0 failed
cargo test -p kebab-store-sqlite --lib      # 19 passed, 0 failed
cargo clippy -p kebab-search -p kebab-store-sqlite --all-targets -- -D warnings  # clean

Dogfood (httpx + zod + lodash clone):

  • search 'AsyncClient' --code-lang pythonCitation::Code { lang: "python", symbol: "httpx.tests.client.test_auth.test_auth_invalid_type" }, repo: "httpx", code_lang: "python". (이전: markdown hit 만 반환.)
  • search 'ZodObject' --code-lang typescriptsymbol: "zod/packages/zod/src/v4/classic/schemas.ZodObject", repo: "zod". design §3.4 path 형식 그대로.
  • search '...' --repo zod → repo filter 도 정확히 동작.

범위

  • crates/kebab-search/src/lexical.rs: SQL block 2개 추가
  • crates/kebab-store-sqlite/src/filters.rs: SQL block 2개 추가 + 1 test helper
  • crates/kebab-search/tests/lexical.rs: insert helper refactor + 2 새 테스트
  • 다른 crate / 다른 doc 미수정. wire schema / surface 무영향 (이미 노출된 flag 의 정상 동작 복원).

버전

bug fix only → 0.8.0 유지 (patch bump 트리거 아님; user-visible surface 변경 없음 — 이미 advertised flag 의 실제 동작 복원).

🤖 Generated with Claude Code

## 요약 `--code-lang LANG` / `--repo NAME` CLI filter 가 **lexical + vector retriever SQL 에서 무시되어** 모든 doc (markdown / pdf / image 포함) 이 결과에 섞여 반환되던 production bug. p10-1A-1 (PR #139) 머지부터 잠복 — 1A-2 / 1B 회차에서도 발견 못 함 (회귀 테스트는 SearchFilters 필드 유무만 검증, SQL 적용은 미검증). p10-1B dogfooding (httpx + zod + lodash clone) 으로 표면화. ## 진단 - **CLI**: `--code-lang` / `--repo` parse 정상, `SearchFilters { code_lang: Vec<String>, repo: Vec<String> }` 에 set 됨. - **`SearchFilters`** (kebab-core): 두 필드 1A-1 추가됨. - **`kebab-search/src/lexical.rs`**: `filters.code_lang` / `filters.repo` 를 **SQL WHERE 절에 반영 안 함**. - **`kebab-store-sqlite/src/filters.rs`** (vector 가 post-filter 로 사용): 동일 누락. - 결과: filter 무시 → 모든 doc 반환 → markdown hit 우선 노출. ## Fix 두 retriever 의 SQL 에 동일 IN-list filter 추가: ```sql AND json_extract(d.metadata_json, '$.code_lang') IN (?, ?, ...) AND json_extract(d.metadata_json, '$.repo') IN (?, ?, ...) ``` `documents.metadata_json` 의 JSON path 는 Task 9 의 `code_lang_breakdown` 쿼리와 동일 source-of-truth 사용. media filter (1A-1 도 추가했던) 와 동일 패턴. ## 회귀 테스트 4건 추가 (모두 신규): - `kebab-search/tests/lexical.rs::lexical_filter_by_code_lang` - `kebab-search/tests/lexical.rs::lexical_filter_by_repo` - `kebab-store-sqlite::filters::tests::filter_chunks_code_lang_keeps_matching_lang` - `kebab-store-sqlite::filters::tests::filter_chunks_repo_keeps_matching_repo` ## 검증 per-crate 테스트 + dogfooding 재실행: ``` cargo test -p kebab-search --lib # 30 passed, 0 failed cargo test -p kebab-search --test lexical # 23 passed, 0 failed cargo test -p kebab-store-sqlite --lib # 19 passed, 0 failed cargo clippy -p kebab-search -p kebab-store-sqlite --all-targets -- -D warnings # clean ``` Dogfood (httpx + zod + lodash clone): - `search 'AsyncClient' --code-lang python` → `Citation::Code { lang: "python", symbol: "httpx.tests.client.test_auth.test_auth_invalid_type" }`, `repo: "httpx"`, `code_lang: "python"`. (이전: markdown hit 만 반환.) - `search 'ZodObject' --code-lang typescript` → `symbol: "zod/packages/zod/src/v4/classic/schemas.ZodObject"`, `repo: "zod"`. design §3.4 path 형식 그대로. - `search '...' --repo zod` → repo filter 도 정확히 동작. ## 범위 - `crates/kebab-search/src/lexical.rs`: SQL block 2개 추가 - `crates/kebab-store-sqlite/src/filters.rs`: SQL block 2개 추가 + 1 test helper - `crates/kebab-search/tests/lexical.rs`: insert helper refactor + 2 새 테스트 - 다른 crate / 다른 doc 미수정. wire schema / surface 무영향 (이미 노출된 flag 의 정상 동작 복원). ## 버전 bug fix only → `0.8.0` 유지 (patch bump 트리거 아님; user-visible surface 변경 없음 — 이미 advertised flag 의 실제 동작 복원). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
altair823 added 1 commit 2026-05-20 03:30:46 +00:00
p10-1A-1 (PR #139) added SearchFilters.code_lang + .repo fields and the CLI
--code-lang / --repo flags propagate them correctly into SearchFilters, but
neither the lexical retriever's FTS SQL nor the shared filter_chunks helper
(used by the vector retriever) ever applied them — so a code-lang-filtered
search returned all-doc hits (markdown / pdf / code mixed).

Discovered while dogfooding p10-1B with httpx + zod + lodash clones:
`kebab search 'AsyncClient' --code-lang python --json` returned markdown
hits from httpx/docs/ first.

Fix: add IN-list filters on json_extract(d.metadata_json, '$.code_lang')
and '$.repo' to both lexical.rs and filters.rs, mirroring the existing
media filter pattern. Two regression tests added in each crate covering
the new filter behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-reviewer-01 approved these changes 2026-05-20 03:32:58 +00:00
claude-reviewer-01 left a comment
Member

회차 1 — 로직 정확, 테스트 4건 전부 통과, 신규 dep 없음. APPROVE.

SQL 수정이 두 곳(lexical.rs + filters.rs) 모두 올바르게 적용되었습니다. json_extract 경로('.code_lang', '.repo')가 1A-2 PR #140의 code_lang_breakdown 쿼리와 정확히 일치하여 스키마 드리프트 없음. IN-list placeholder 방식이 media 필터 패턴과 동일하게 구현되어 일관성 있습니다. 빈 Vec → 필터 미적용(all-pass) 컨벤션도 올바르게 유지.

NULL 처리도 올바릅니다: metadata_json에 code_lang 키가 없는 구형 문서는 json_extract → NULL, NULL IN (...) → NULL(false)로 평가되어 코드 필터 적용 시 제외됨 — '--code-lang python' 요청에서 마크다운 문서가 섞이지 않아야 한다는 사양과 일치.

테스트 헬퍼 insert_doc_full_with_metadata(lexical) + seed_committed_with_metadata(filters)가 각각 독립적인 TempDir을 사용하여 테스트 간 상태 공유 없음. 회귀 위험 없이 머지 가능합니다.

회차 1 — 로직 정확, 테스트 4건 전부 통과, 신규 dep 없음. APPROVE. SQL 수정이 두 곳(lexical.rs + filters.rs) 모두 올바르게 적용되었습니다. json_extract 경로('$.code_lang', '$.repo')가 1A-2 PR #140의 code_lang_breakdown 쿼리와 정확히 일치하여 스키마 드리프트 없음. IN-list placeholder 방식이 media 필터 패턴과 동일하게 구현되어 일관성 있습니다. 빈 Vec → 필터 미적용(all-pass) 컨벤션도 올바르게 유지. NULL 처리도 올바릅니다: metadata_json에 code_lang 키가 없는 구형 문서는 json_extract → NULL, NULL IN (...) → NULL(false)로 평가되어 코드 필터 적용 시 제외됨 — '--code-lang python' 요청에서 마크다운 문서가 섞이지 않아야 한다는 사양과 일치. 테스트 헬퍼 insert_doc_full_with_metadata(lexical) + seed_committed_with_metadata(filters)가 각각 독립적인 TempDir을 사용하여 테스트 간 상태 공유 없음. 회귀 위험 없이 머지 가능합니다.

칭찬: media 필터 블록 직후, ingested_after 직전에 배치한 위치 선택이 기존 컨벤션과 일관성 있습니다. 두 파일(lexical.rs + filters.rs) 모두 동일 순서를 유지해 향후 필터 추가 시 기준점이 명확합니다.

칭찬: `media` 필터 블록 직후, `ingested_after` 직전에 배치한 위치 선택이 기존 컨벤션과 일관성 있습니다. 두 파일(lexical.rs + filters.rs) 모두 동일 순서를 유지해 향후 필터 추가 시 기준점이 명확합니다.

칭찬: insert_doc_fullinsert_doc_full_with_metadata 델리게이트 분리 패턴이 깔끔합니다. 기존 호출부를 건드리지 않고 메타데이터 파라미터를 추가한 후 기존 함수가 "{}" 로 위임하는 구조 — 하위 호환 확장의 좋은 예.

칭찬: `insert_doc_full` → `insert_doc_full_with_metadata` 델리게이트 분리 패턴이 깔끔합니다. 기존 호출부를 건드리지 않고 메타데이터 파라미터를 추가한 후 기존 함수가 `"{}"` 로 위임하는 구조 — 하위 호환 확장의 좋은 예.

insert_code_doc 헬퍼: MediaType::Markdown을 하드코딩하고 있는데, 코드 문서를 삽입하는 헬퍼치고는 MediaType::Code (또는 Code variant가 있다면) 쪽이 더 자기 서술적입니다. 기능상 버그는 아니지만, 이후 media-type 별 필터 테스트가 붙을 경우 헷갈릴 수 있어 nit으로 남깁니다.

`insert_code_doc` 헬퍼: `MediaType::Markdown`을 하드코딩하고 있는데, 코드 문서를 삽입하는 헬퍼치고는 `MediaType::Code` (또는 `Code` variant가 있다면) 쪽이 더 자기 서술적입니다. 기능상 버그는 아니지만, 이후 media-type 별 필터 테스트가 붙을 경우 헷갈릴 수 있어 nit으로 남깁니다.

칭찬: seed_committed_with_metadata가 asset → document → chunk → embedding_record → commit 전 단계를 모두 밟아 실제 filter_chunks 입력 조건을 재현하는 방식이 탄탄합니다. 이후 ingested_after, media 등 다른 필터 테스트를 추가할 때도 이 헬퍼를 재사용할 수 있는 범용성이 좋습니다.

칭찬: `seed_committed_with_metadata`가 asset → document → chunk → embedding_record → commit 전 단계를 모두 밟아 실제 `filter_chunks` 입력 조건을 재현하는 방식이 탄탄합니다. 이후 `ingested_after`, `media` 등 다른 필터 테스트를 추가할 때도 이 헬퍼를 재사용할 수 있는 범용성이 좋습니다.
altair823 merged commit 74f1b0571b into main 2026-05-20 03:34:55 +00:00
altair823 deleted branch fix/p10-1a-1-code-lang-repo-filter-sql 2026-05-20 03:34:56 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#144