@@ -89,6 +89,18 @@ const CANDIDATE_HNSW_MAX_SCAN_TUPLES = '20000'
8989 * left with what the neighbourhood happened to hold.
9090 */
9191const ON_ROW_WALK_SCAN_TUPLES = 100_000
92+
93+ /**
94+ * How far a walk may go when readability is on the row: the on-row cap, unless the walk still
95+ * has to ask the document about each tuple — a tag or date filter — in which case a tuple costs
96+ * what it did before the columns were mirrored, and the default cap keeps a walk through a
97+ * mostly-excluded neighbourhood at a short answer rather than a missed deadline.
98+ */
99+ function onRowWalkScanTuples ( documentCondition : SQL | undefined ) : number {
100+ return documentCondition === undefined
101+ ? ON_ROW_WALK_SCAN_TUPLES
102+ : Number ( CANDIDATE_HNSW_MAX_SCAN_TUPLES )
103+ }
92104/**
93105 * Beam width per iteration. A beam is the granularity of cancellation: pgvector calls
94106 * `CHECK_FOR_INTERRUPTS` only while building an index, never inside `hnswgettuple`, so neither
@@ -1475,7 +1487,7 @@ async function selectSourceVectorCandidates(input: {
14751487 ORDER BY ${ input . candidateDistance } LIMIT ${ input . candidateLimit } ` ) ,
14761488 input . budget ,
14771489 'vector.source_walk' ,
1478- ON_ROW_WALK_SCAN_TUPLES
1490+ onRowWalkScanTuples ( input . documentCondition )
14791491 )
14801492 const walks : Array < ( ) => RankedChunks > = sources . walked . map ( ( connectorId ) =>
14811493 walk ( eq ( embeddingSearch . connectorId , connectorId ) )
@@ -1594,7 +1606,9 @@ async function selectVectorResults(params: SearchParams): Promise<SearchResult[]
15941606 * date filter, which the row does not carry. A bounded set never walks, so this only runs when
15951607 * the filtered documents were too many to enumerate.
15961608 */
1597- const documentCondition = and ( documentTagCondition , dateFilterCondition ( params . filters ) )
1609+ const dateCondition = dateFilterCondition ( params . filters )
1610+ const documentCondition =
1611+ documentTagCondition || dateCondition ? and ( documentTagCondition , dateCondition ) : undefined
15981612 /**
15991613 * Candidate selection ignores the page offset — only the rerank pages over the pool — so a
16001614 * refill reuses the pool it already has. Excluding another source is the only thing that
@@ -1754,7 +1768,7 @@ async function selectVectorResults(params: SearchParams): Promise<SearchResult[]
17541768 ) ,
17551769 params . budget ,
17561770 'vector.candidate_search' ,
1757- plan ? ON_ROW_WALK_SCAN_TUPLES : undefined
1771+ plan ? onRowWalkScanTuples ( documentCondition ) : undefined
17581772 )
17591773 /**
17601774 * A full traversal is already the nearest permitted chunks, so nothing else is worth
0 commit comments