Skip to content

fix(hybrid): propagate SearchOpts filters to inner keyword/vector searches#282

Open
x64Nika wants to merge 1 commit intogarrytan:masterfrom
x64Nika:fix/hybrid-searchopts-drop
Open

fix(hybrid): propagate SearchOpts filters to inner keyword/vector searches#282
x64Nika wants to merge 1 commit intogarrytan:masterfrom
x64Nika:fix/hybrid-searchopts-drop

Conversation

@x64Nika
Copy link
Copy Markdown

@x64Nika x64Nika commented Apr 20, 2026

Summary

hybridSearch() silently drops caller-provided type and exclude_slugs filter options when building searchOpts for inner searchKeyword / searchVector calls:

https://github.com/garrytan/gbrain/blob/master/src/core/search/hybrid.ts#L71

const searchOpts: SearchOpts = { limit: innerLimit, detail };

Any caller using hybridSearch(engine, query, { type: 'person' }) or { exclude_slugs: [...] } gets unfiltered results while believing the filter is applied — the outer hybridSearch never reads those fields again, and the inner searches never see them.

Fix

One-line spread before overriding limit / offset / detail:

const searchOpts: SearchOpts = { ...opts, limit: innerLimit, offset: 0, detail };

This preserves every caller-provided filter field (current and future — any field added to SearchOpts becomes automatically propagated, no additional plumbing).

offset is explicitly set to 0 on the inner searches because the outer hybridSearch slices the final result set at [offset, offset+limit] itself — propagating the caller's offset into the inner searches would double-apply it.

Test plan

Added test/hybrid-search-opts.test.ts with 4 regression tests covering:

  • type filter propagates to searchKeyword
  • exclude_slugs propagates
  • limit is overridden to innerLimit (outer limit * 2) and offset is forced to 0
  • undefined opts don't throw

All existing tests still pass (bun test test/search.test.ts test/search-limit.test.ts — 42 pass, 0 fail).

Happy to add similar tests on the vector path or integration-level tests if that would help. Also flagged as draft in case you'd prefer a different fix shape (e.g., explicit per-field spread instead of full ...opts).

…rches

hybridSearch() constructed its inner searchOpts with only { limit, detail },
silently dropping caller-provided `type` and `exclude_slugs`. Any caller using
hybridSearch(engine, query, { type: 'person' }) got unfiltered results while
believing the filter was applied.

Fix: spread caller opts before overriding limit/offset/detail. This preserves
every filter field (current and future) without individual plumbing per field.

Also adds regression tests covering type, exclude_slugs, limit/offset override,
and undefined-opts cases.
@x64Nika x64Nika marked this pull request as ready for review April 20, 2026 22:39
x64Nika added a commit to x64Nika/gbrain that referenced this pull request Apr 22, 2026
Add `gbrain doctor --strict` mode with two extra check families:

1. Typed-edge bidirectionality — for configured inverse pairs like
   refutes↔refuted_by, verify every forward edge has its reciprocal.
   Default pair list: refutes↔refuted_by. Extend INVERSE_EDGE_PAIRS
   in src/commands/doctor-strict.ts to add new pairs.

2. Tag-prefix uniqueness — for user-configured prefixes (set via env
   GBRAIN_DOCTOR_TAG_PREFIXES=stance-,priority-), verify no page
   carries >1 tag matching the prefix. Empty env = check skipped.

Both checks emit WARN not FAIL — drift is content-side and doesn't
break the runtime. FAIL stays reserved for infra breakage.

Files:
- src/commands/doctor-strict.ts (new, ~110 LOC)
- src/commands/doctor.ts (+18 LOC conditional invocation)
- src/cli.ts (+1 help line)
- test/doctor-strict.test.ts (new, 6 tests gated on DATABASE_URL)

Tests:
- bun test: 1756 pass, 0 fail (0 regression)
- DATABASE_URL=... bun test test/doctor-strict.test.ts: 6/6 pass
- Live smoke: gbrain doctor --strict runs and adds 2 OK checks

Upstream PR track: both check families are generic (inverse-pair graph
invariants, tag-prefix uniqueness via env config). Good candidates for
upstream PR after Patch 0 (garrytan#282) lands — provides value to any gbrain
user with typed edges or tag naming conventions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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