Skip to content

Commit a35915d

Browse files
committed
Correct hnsw.ef_search and relaxed_order comments
Flooring ef_search at 40 is a product choice (never worse than the GUC default); pgvector itself accepts 1..1000. relaxed_order can slightly disorder dense-channel ranks that feed RRF — that trade-off is intentional for selective-filter recall, not a free re-sort.
1 parent 86f49f2 commit a35915d

2 files changed

Lines changed: 7 additions & 4 deletions

File tree

‎src/services/search.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ describe("deriveHybridEvidence", () => {
242242
});
243243

244244
describe("hnswEfSearch", () => {
245-
it("clamps to pgvector's [40, 1000] bounds", () => {
245+
it("clamps to the product floor of 40 and the GUC max of 1000", () => {
246246
expect(hnswEfSearch(0)).toBe(40);
247247
expect(hnswEfSearch(1)).toBe(40);
248248
expect(hnswEfSearch(40)).toBe(40);

‎src/services/search.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,9 @@ const iterativeScanSupport = new WeakMap<RawSql, boolean>();
479479

480480
/**
481481
* The hnsw.ef_search value for a dense query: the overfetch limit clamped
482-
* to pgvector's bounds — never below the GUC's default (40), never above
483-
* its hard maximum (1000). Non-finite input falls back to the default.
482+
* into [40, 1000]. pgvector accepts 1..1000 (default 40); flooring at 40 is
483+
* a product choice so we never run worse than the GUC default, and 1000 is
484+
* the GUC hard max. Non-finite input falls back to the default.
484485
*/
485486
export function hnswEfSearch(overfetchLimit: number): number {
486487
const limit = Number.isFinite(overfetchLimit) ? Math.floor(overfetchLimit) : 40;
@@ -573,7 +574,9 @@ export async function fetchDenseCandidates(
573574
// exist. ef_search widens the fixed candidate pool; iterative_scan
574575
// (pgvector >= 0.8) additionally keeps scanning until the limit is
575576
// satisfied. Both are SET LOCAL, so nothing leaks into pooled session
576-
// state. relaxed_order is safe downstream: RRF fuses by rank and re-sorts.
577+
// state. relaxed_order can slightly disorder the dense channel's neighbor
578+
// ranks (and those ranks ARE the dense RRF input) — intentional trade-off
579+
// for selective-filter recall over strict distance order.
577580
const rows = await rawSql.begin(async (tx) => {
578581
await tx.unsafe(`SET LOCAL hnsw.ef_search = ${efSearch}`);
579582
const support = iterativeScanSupport.get(rawSql);

0 commit comments

Comments
 (0)