@@ -1757,7 +1757,8 @@ async function selectVectorResults(params: SearchParams): Promise<SearchResult[]
17571757 * rather than re-deriving permission across the whole index. Exact ranking also honours
17581758 * `statement_timeout`, which a traversal cannot.
17591759 */
1760- const rankPermittedExactly = async ( documentIds : string [ ] ) => {
1760+ /** Ranks the set's chunks exactly; `read` are chunks a pool already holds, ranked past. */
1761+ const rankPermittedExactly = async ( documentIds : string [ ] , read ?: readonly string [ ] ) => {
17611762 annotateSearchDiagnostics ( { vectorRanking : 'exact-candidates' } )
17621763 if ( ! documentIds . length ) return [ ]
17631764 return runSearchQuery ( params . budget , 'vector.exact_candidates' , ( executor ) =>
@@ -1768,6 +1769,9 @@ async function selectVectorResults(params: SearchParams): Promise<SearchResult[]
17681769 inArray ( embeddingSearch . knowledgeBaseId , params . knowledgeBaseIds ) ,
17691770 eq ( embeddingSearch . enabled , true ) ,
17701771 sql `${ embeddingSearch . documentId } = ANY(${ textArrayLiteral ( documentIds ) } )` ,
1772+ read ?. length
1773+ ? sql `NOT (${ embeddingSearch . id } = ANY(${ textArrayLiteral ( [ ...read ] ) } ))`
1774+ : undefined ,
17711775 candidateTagCondition ,
17721776 excludedOnRow
17731777 ) }
@@ -1844,16 +1848,18 @@ async function selectVectorResults(params: SearchParams): Promise<SearchResult[]
18441848 * The walk decides readability on the projection row, which is broader than the
18451849 * document predicate hydration applies, so a pool it filled can still run short of
18461850 * readable rows. That shortfall is what refills a pool: the refill is the exact ranking,
1847- * complete over the set, placed behind the rows already read so the pages keep their
1848- * offsets.
1851+ * complete over the set, ranked past the rows already read and placed behind them, so
1852+ * the pages keep their offsets and every refill is a full window of fresh rows .
18491853 */
18501854 const permittedIds = params . permitted . documents . map ( ( entry ) => entry . id )
18511855 const previous =
18521856 candidatePool ?. excludedKey === excludedKey ? candidatePool . ids : undefined
18531857 if ( previous ) {
1854- const exact = await rankPermittedExactly ( permittedIds )
1855- const read = new Set ( previous . map ( ( candidate ) => candidate . id ) )
1856- selected = [ ...previous , ...exact . filter ( ( candidate ) => ! read . has ( candidate . id ) ) ]
1858+ const exact = await rankPermittedExactly (
1859+ permittedIds ,
1860+ previous . map ( ( candidate ) => candidate . id )
1861+ )
1862+ selected = [ ...previous , ...exact ]
18571863 exhausted = exact . length < candidateLimit
18581864 } else {
18591865 selected = await walkGraph ( )
0 commit comments