Skip to content

Commit d2a4e47

Browse files
authored
fix(knowledge): keep the GIN ranking for the rest of a search once a page was handed to it (#8110)
1 parent c9d73bb commit d2a4e47

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,35 @@ describe('permitted-document planner', () => {
18671867
expect(ginStatements()).toHaveLength(0)
18681868
})
18691869

1870+
it('stays with the GIN ranking for the rest of a search once a page was handed to it', async () => {
1871+
/** Tin cannot fill the first page; GIN supplies it, and hydration keeps only half, so a second page follows. */
1872+
tinPages = [
1873+
{ ranked: 2000, candidates: [] },
1874+
{ ranked: 20_000, candidates: [] },
1875+
{ ranked: 2000, candidates: [hit('never', 'src-a')] },
1876+
]
1877+
const ginRows = Array.from({ length: 40 }, (_, index) => hit(`g-${index}`, 'src-a'))
1878+
const ginPages = [ginRows, []]
1879+
const execute = dbChainMockFns.execute.getMockImplementation()!
1880+
dbChainMockFns.execute.mockImplementation(async (query) =>
1881+
render(query).sql.includes('WITH matched_keyword_chunks')
1882+
? (ginPages.shift() ?? [])
1883+
: execute(query)
1884+
)
1885+
queueTableRows(
1886+
schemaMock.embedding,
1887+
ginRows.slice(0, 20).map((row) => ({ ...row, content: 'release notes' }))
1888+
)
1889+
const results = await keyword({
1890+
topK: 40,
1891+
permitted: { kind: 'bounded', documents: large },
1892+
accessPlan,
1893+
})
1894+
expect(results.map((row) => row.id)).not.toContain('never')
1895+
expect(tinStatements()).toHaveLength(2)
1896+
expect(ginStatements().length).toBeGreaterThanOrEqual(2)
1897+
})
1898+
18701899
it('keeps the bounded read for a set under the size', async () => {
18711900
mockResolveTinKeywordQuery.mockResolvedValue(null)
18721901
await keyword({ permitted: { kind: 'bounded', documents: large.slice(0, -1) }, accessPlan })

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2239,6 +2239,11 @@ export async function executeKeywordSearch(params: KeywordSearchParams): Promise
22392239
sql` || ' OR ' || `
22402240
)} || ') AND (' || ${tinQuery} || ')'`
22412241
: undefined
2242+
/**
2243+
* Tin and GIN order candidates differently, so a search that once handed a page to GIN stays
2244+
* with GIN: an offset advanced through one ranking cannot resume the other.
2245+
*/
2246+
let handedToGin = false
22422247
/** Keep readable identities and rank scalars separate so sorts never carry full text-search vectors. */
22432248
return selectAuthorizedSearchResults({
22442249
leg: 'keyword',
@@ -2259,9 +2264,10 @@ export async function executeKeywordSearch(params: KeywordSearchParams): Promise
22592264
? params.permitted.documents.map((entry) => entry.id)
22602265
: undefined
22612266
if (permittedIds?.length === 0) return { candidates: [], nextOffset: offset }
2262-
if (tinScope) {
2267+
if (tinScope && !handedToGin) {
22632268
const tinPage = await selectTinPage(tinScope, limit, offset, excludedSources)
22642269
if (tinPage) return tinPage
2270+
handedToGin = true
22652271
annotateSearchDiagnostics({ keywordRanking: 'gin' })
22662272
}
22672273
const baseScope = and(

0 commit comments

Comments
 (0)