Skip to content

Commit fd65d98

Browse files
committed
fix(knowledge): rank a large bounded set's refill past the rows already read
The refill's exact ranking excludes the chunks the pool already holds inside the statement, so every refill is a full window of fresh rows rather than a window thinned by the rows the walk found first.
1 parent e09b725 commit fd65d98

2 files changed

Lines changed: 13 additions & 6 deletions

File tree

‎apps/sim/lib/knowledge/search/queries.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2502,6 +2502,7 @@ describe('filters on a resolved scope', () => {
25022502
expect(exact).toHaveLength(1)
25032503
/** The refill ranks past the rows already read, so nothing already rejected is read twice. */
25042504
expect(JSON.stringify(exact[0])).toContain('doc-4999')
2505+
expect(JSON.stringify(exact[0])).toContain('w-199')
25052506
})
25062507

25072508
it('ranks a set under the size exactly, without a walk', async () => {

‎apps/sim/lib/knowledge/search/queries.ts‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)