Skip to content

Commit 1c4ecfc

Browse files
committed
feat: add index optimisations to lines
1 parent dc535cc commit 1c4ecfc

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

src/index.spec.ts

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,72 @@
11
import { describe, expect, it } from 'bun:test'
22

3+
import { LibSQLDatabase } from 'drizzle-orm/libsql'
4+
import { SQLiteRelationalQuery } from 'drizzle-orm/sqlite-core/query-builders/query'
35
import { parse } from 'smol-toml'
46

57
import { LineGroups } from '#collections-types/line-groups'
68

79
import createDbClient from '.'
10+
import { getQueryPlan } from './query-utils'
11+
import relations from './relations'
12+
import * as schema from './schema'
813

914
const getCollectionDoc = async <Schema>(document: string) =>
1015
parse(await Bun.file(`collections/${document}.toml`).text()) as Schema
1116

17+
// Generic function to test query plan optimization
18+
const expectOptimizedQueryPlan = async <Q extends SQLiteRelationalQuery<'async', unknown>>(
19+
db: LibSQLDatabase<typeof schema, typeof relations>,
20+
query: Q,
21+
{
22+
maxTableScans = 0,
23+
expectedIndexes = [],
24+
}: { maxTableScans?: number; expectedIndexes?: string[] } = {},
25+
) => {
26+
const queryPlan = await getQueryPlan(db, query)
27+
const planText = queryPlan.join(' ')
28+
29+
const tableScans = queryPlan.filter(
30+
(step) => typeof step === 'string' && step.includes('SCAN') && !step.includes('USING INDEX'),
31+
).length
32+
33+
expect(tableScans, planText).toBeLessThanOrEqual(maxTableScans)
34+
expectedIndexes.forEach((indexName) => expect(planText).toContain(indexName))
35+
36+
return queryPlan
37+
}
38+
1239
describe('Database', () => {
13-
it('should return lines in order', async () => {
14-
const db = createDbClient()
15-
const id = 'DMP'
16-
17-
const lineGroup = await db.query.lineGroups.findFirst({
18-
where: { id },
19-
with: {
20-
author: true,
21-
lines: {
22-
orderBy: {
23-
lineGroupOrder: 'asc',
40+
describe('line groups', () => {
41+
it('should return, optimized, lines in order', async () => {
42+
const db = createDbClient()
43+
const id = 'DMP'
44+
45+
const lineGroupQuery = db.query.lineGroups.findFirst({
46+
where: { id },
47+
with: {
48+
author: true,
49+
lines: {
50+
orderBy: {
51+
lineGroupOrder: 'asc',
52+
},
2453
},
2554
},
26-
},
27-
})
55+
})
56+
const lineGroup = await lineGroupQuery
2857

29-
const collectionDoc = await getCollectionDoc<LineGroups>(`line-groups/D/${id}`)
30-
expect(lineGroup?.lines.map((line) => line.id)).toEqual(collectionDoc.lines)
31-
expect(lineGroup?.author.id).toEqual(collectionDoc.author)
58+
const collectionDoc = await getCollectionDoc<LineGroups>(`line-groups/D/${id}`)
59+
60+
expect(lineGroup?.lines.map((line) => line.id)).toEqual(collectionDoc.lines)
61+
expect(lineGroup?.author.id).toEqual(collectionDoc.author)
62+
await expectOptimizedQueryPlan(db, lineGroupQuery, {
63+
maxTableScans: 2,
64+
expectedIndexes: [
65+
'sqlite_autoindex_line_groups_1',
66+
'sqlite_autoindex_authors_1',
67+
'line_group_id_order_index',
68+
],
69+
})
70+
})
3271
})
3372
})

src/schema.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const lines = sqliteTable(
5555
lineGroupId: text(),
5656
lineGroupOrder: integer(),
5757
},
58-
(t) => [index('line_group_order_index').on(t.lineGroupOrder)],
58+
(t) => [index('line_group_id_order_index').on(t.lineGroupId, t.lineGroupOrder)],
5959
)
6060

6161
type LinePayload = DistributedOmit<Lines['content'][number], 'asset' | 'data'>
@@ -70,7 +70,10 @@ export const assetLines = sqliteTable(
7070
additional: json().$type<LinePayload>(),
7171
priority: integer(),
7272
},
73-
(t) => [index('priority_index').on(t.priority)],
73+
(t) => [
74+
index('priority_index').on(t.priority),
75+
index('line_id_priority_index').on(t.lineId, t.priority),
76+
],
7477
)
7578

7679
export const banis = sqliteTable('banis', {

0 commit comments

Comments
 (0)