Skip to content

Commit 8a4a0e1

Browse files
authored
improvement(search): speed up organization integration and source queries (#7830)
1 parent 7730fd3 commit 8a4a0e1

33 files changed

Lines changed: 27108 additions & 223 deletions

apps/sim/app/api/files/authorization.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,21 +436,28 @@ describe('KB file live source authorization', () => {
436436
async (allowed) => {
437437
const scope = { kind: 'user' as const, userId: USER_ID, tokens: ['reader-token'] }
438438
const getForConnectors = vi.fn().mockResolvedValue(scope)
439+
const liveSources = { type: 'live-sources' }
439440
const access: KnowledgeAccessProvider = {
440441
get: async () => scope,
441442
getForConnectors,
442443
getForDocuments: async () => scope,
444+
liveSourceConnectorCondition: async () => liveSources as never,
443445
}
444446
queueTableRows(schemaMock.document, [])
445-
queueTableRows(schemaMock.document, [{ connectorId: 'confluence-source' }])
447+
queueTableRows(schemaMock.knowledgeConnector, [{ connectorId: 'confluence-source' }])
446448
queueTableRows(schemaMock.document, allowed ? [{ id: 'doc-1' }] : [])
447449
await expect(
448450
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', false, {
449451
knowledgeAccess: access,
450452
})
451453
).resolves.toBe(allowed)
452454
expect(getForConnectors).toHaveBeenCalledExactlyOnceWith(['confluence-source'], undefined)
455+
const discovery = dbChainMockFns.where.mock.calls.filter(([condition]) =>
456+
hasMockCondition(condition, (node) => node === liveSources)
457+
)
458+
expect(discovery).toHaveLength(1)
453459
for (const [condition] of dbChainMockFns.where.mock.calls) {
460+
if (discovery.some(([live]) => live === condition)) continue
454461
expect(
455462
hasMockCondition(
456463
condition,

apps/sim/app/api/knowledge/sim-search/sources/route.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const source = {
5959
isSyncing: false,
6060
lastSyncAt: null,
6161
hasSyncError: false,
62-
viewerDocumentCount: 0,
62+
hasViewerDocuments: false,
6363
viewerFailedDocumentCount: 0,
6464
viewerEmailVerified: true,
6565
viewerAccounts: [],

apps/sim/app/o/[organizationId]/integrations/integrations.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ const memberSource: SearchSourceSummary = {
149149
isSyncing: false,
150150
lastSyncAt: null,
151151
hasSyncError: false,
152-
viewerDocumentCount: 0,
152+
hasViewerDocuments: false,
153153
viewerFailedDocumentCount: 0,
154154
viewerEmailVerified: true,
155155
viewerAccounts: [],

apps/sim/app/o/[organizationId]/settings/integrations/providers/[connectorType]/provider-detail.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ const source = {
136136
lastSyncAt: '2026-09-08T12:00:00.000Z',
137137
connectionRequired: true,
138138
viewerMembership: 'needs_reauth',
139-
viewerDocumentCount: 0,
139+
hasViewerDocuments: false,
140140
}
141141
const credentialGroup = {
142142
id: 'accounts-one',
@@ -429,7 +429,6 @@ describe('organization provider management', () => {
429429
)
430430
expect(container.textContent).not.toContain('Connect account')
431431
expect(container.textContent).not.toContain('Reconnect')
432-
expect(container.textContent).not.toContain('0 searchable documents')
433432
})
434433

435434
it('loads sources for a nonpersonal provider even when an accounts view URL is supplied', async () => {

apps/sim/hooks/queries/kb/search-source-progress.test.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function Probe() {
2323
const sync = useTriggerSync()
2424
return (
2525
<div>
26-
<span>{result.data?.[0]?.viewerDocumentCount ?? 0}</span>
26+
<span>{String(result.data?.[0]?.hasViewerDocuments ?? false)}</span>
2727
<button
2828
disabled={sync.isPending}
2929
onClick={() => sync.mutate({ knowledgeBaseId: 'kb', connectorId: 'source' })}
@@ -83,7 +83,7 @@ beforeEach(() => {
8383
isSyncing: syncing,
8484
hasSyncError: false,
8585
viewerFailedDocumentCount: 0,
86-
viewerDocumentCount: syncing ? 0 : 1,
86+
hasViewerDocuments: !syncing,
8787
},
8888
],
8989
nextCursor: null,
@@ -127,7 +127,7 @@ describe('source progress polling', () => {
127127
await advance(1)
128128
expect(
129129
client.getQueryData(searchSourceKeys.pages('workspace', { search: '', mine: false }))
130-
).toMatchObject({ pages: [{ sources: [{ isSyncing: false, viewerDocumentCount: 1 }] }] })
130+
).toMatchObject({ pages: [{ sources: [{ isSyncing: false, hasViewerDocuments: true }] }] })
131131
const total = mocks.requestJson.mock.calls.length
132132
await advance(60_000)
133133
expect(mocks.requestJson).toHaveBeenCalledTimes(total)
@@ -157,7 +157,9 @@ describe('source progress polling', () => {
157157
expect(
158158
client.getQueryData(searchSourceKeys.pages('workspace', { search: '', mine: false }))
159159
).toMatchObject({
160-
pages: [{ sources: [expect.objectContaining({ isSyncing: false, viewerDocumentCount: 1 })] }],
160+
pages: [
161+
{ sources: [expect.objectContaining({ isSyncing: false, hasViewerDocuments: true })] },
162+
],
161163
})
162164
const total = mocks.requestJson.mock.calls.length
163165
await advance(60_000)
@@ -173,7 +175,9 @@ describe('source progress polling', () => {
173175
expect(
174176
client.getQueryData(searchSourceKeys.pages('workspace', { search: '', mine: false }))
175177
).toMatchObject({
176-
pages: [{ sources: [expect.objectContaining({ isSyncing: false, viewerDocumentCount: 1 })] }],
178+
pages: [
179+
{ sources: [expect.objectContaining({ isSyncing: false, hasViewerDocuments: true })] },
180+
],
177181
})
178182
})
179183
it('lets a slow summary refresh finish while progress keeps polling', async () => {
@@ -201,7 +205,9 @@ describe('source progress polling', () => {
201205
expect(
202206
client.getQueryData(searchSourceKeys.pages('workspace', { search: '', mine: false }))
203207
).toMatchObject({
204-
pages: [{ sources: [expect.objectContaining({ isSyncing: false, viewerDocumentCount: 1 })] }],
208+
pages: [
209+
{ sources: [expect.objectContaining({ isSyncing: false, hasViewerDocuments: true })] },
210+
],
205211
})
206212
const total = mocks.requestJson.mock.calls.length
207213
await advance(60_000)

apps/sim/lib/api/contracts/knowledge/connectors.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,11 @@ const searchSourceSummaryFields = {
359359
isSyncing: z.boolean(),
360360
lastSyncAt: z.string().datetime().nullable(),
361361
hasSyncError: z.boolean(),
362-
viewerDocumentCount: z.number().int().nonnegative(),
362+
/**
363+
* Whether the viewer can search at least one indexed document from this source. An
364+
* existence flag rather than a count: counting means access-checking every visible document.
365+
*/
366+
hasViewerDocuments: z.boolean(),
363367
viewerFailedDocumentCount: z.number().int().nonnegative().default(0),
364368
viewerEmailVerified: z.boolean(),
365369
viewerAccounts: z

apps/sim/lib/api/contracts/knowledge/personal-integrations.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export const personalSearchIntegrationSchema = z.object({
2323
.max(100),
2424
connectionStatus: z.enum(['connected', 'reconnect_needed', 'not_connected', 'unavailable']),
2525
indexingStatus: z.enum(['indexing', 'indexed', 'not_indexed', 'sync_failed', 'paused']),
26-
searchableDocuments: z.number().int().nonnegative(),
2726
action: searchConnectionTargetSchema.nullable(),
2827
})
2928

apps/sim/lib/copilot/application/load-search-integrations.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ describe('loadCopilotSearchIntegrations', () => {
8484
],
8585
connectionStatus: 'reconnect_needed',
8686
indexingStatus: 'indexed',
87-
searchableDocuments: 7,
8887
action: null,
8988
}
9089
listIntegrations

apps/sim/lib/knowledge/__integration__/github-member.integration.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ describe('fixture-backed GitHub member search in PostgreSQL', () => {
886886
{
887887
connectorId,
888888
viewerMembership: 'connected',
889-
viewerDocumentCount: userId === ids.aliceId ? 1 : 0,
889+
hasViewerDocuments: userId === ids.aliceId,
890890
},
891891
])
892892
}
@@ -919,7 +919,7 @@ describe('fixture-backed GitHub member search in PostgreSQL', () => {
919919
})
920920
).sources[0]
921921
expect(await summary(ids.aliceId)).toMatchObject({
922-
viewerDocumentCount: 1,
922+
hasViewerDocuments: true,
923923
viewerFailedDocumentCount: 0,
924924
hasSyncError: false,
925925
})
@@ -977,7 +977,7 @@ describe('fixture-backed GitHub member search in PostgreSQL', () => {
977977
)
978978
}
979979
expect(await summary(userId)).toMatchObject({
980-
viewerDocumentCount: userId === ids.aliceId ? 1 : 0,
980+
hasViewerDocuments: userId === ids.aliceId,
981981
viewerFailedDocumentCount: userId === ids.aliceId ? 1 : 0,
982982
})
983983
}
@@ -1327,13 +1327,13 @@ describe('fixture-backed GitHub member search in PostgreSQL', () => {
13271327
expect(
13281328
summaries.sources.map((source) => ({
13291329
connectorId: source.connectorId,
1330-
viewerDocumentCount: source.viewerDocumentCount,
1330+
hasViewerDocuments: source.hasViewerDocuments,
13311331
}))
13321332
).toEqual(
13331333
expect.arrayContaining([
1334-
{ connectorId: enrolled.connectorId, viewerDocumentCount: 1 },
1335-
{ connectorId: privateId, viewerDocumentCount: userId === ids.aliceId ? 1 : 0 },
1336-
{ connectorId: blockedId, viewerDocumentCount: 0 },
1334+
{ connectorId: enrolled.connectorId, hasViewerDocuments: true },
1335+
{ connectorId: privateId, hasViewerDocuments: userId === ids.aliceId },
1336+
{ connectorId: blockedId, hasViewerDocuments: false },
13371337
])
13381338
)
13391339
}

apps/sim/lib/knowledge/__integration__/organization-search-overview.integration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ describe('organization operational overview with real SQL', () => {
206206
input: { ...input, connectorType: 'google_drive' },
207207
})
208208
expect(visible.sources).toHaveLength(2)
209-
expect(visible.sources.every((source) => source.viewerDocumentCount === 0)).toBe(true)
209+
expect(visible.sources.every((source) => !source.hasViewerDocuments)).toBe(true)
210210
})
211211
it('keeps explicit approvals and deactivations visible before source creation', async () => {
212212
await db.insert(organizationSearchIntegration).values([

0 commit comments

Comments
 (0)