Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions migrations/0136_ai_ready_route_order_index.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Match the AI Ready listing filter and route order so LIMIT stops early.
-- Existing status indexes still require sorting every eligible page.
CREATE INDEX IF NOT EXISTS idx_ai_ready_pages_status_route
ON ai_ready_pages(indexed, is_error, route);
1 change: 1 addition & 0 deletions scripts/lib/d1-schema-verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export const APPLIED_DUPLICATE_MIGRATION_NUMBERS: Readonly<Record<string, readon
export const RESERVED_MIGRATION_NUMBERS = {
'0128': '#320 feat(indexnow-curated) claims 0128_indexnow.sql',
'0129': '#322 feat(seo-trending-indexable) claims 0129_skill_trending_admissions.sql',
'0135': '#404 fix(classifier-accuracy) claims 0135_relaunch_classifications.sql',
} satisfies Readonly<Record<string, string>>

/**
Expand Down
37 changes: 37 additions & 0 deletions test/unit/ai-ready-route-cost.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { readFileSync } from 'node:fs'
import Database from 'better-sqlite3'
import { describe, expect, it } from 'vitest'

describe('ai-ready route listing cost', () => {
it('returns ordered eligible pages without scanning and sorting every eligible route', () => {
const db = new Database(':memory:')
db.exec(`
CREATE TABLE ai_ready_pages (
route TEXT PRIMARY KEY, title TEXT, description TEXT, headings TEXT,
keywords TEXT, updated_at INTEGER, is_error INTEGER, locale TEXT,
indexed INTEGER, indexnow_synced_at INTEGER, indexed_at INTEGER
);
CREATE INDEX idx_ai_ready_pages_indexed_is_error ON ai_ready_pages(indexed, is_error);
CREATE INDEX idx_ai_ready_pages_indexnow_scan ON ai_ready_pages(indexed, is_error, indexnow_synced_at, indexed_at);
`)
const insert = db.prepare('INSERT INTO ai_ready_pages(route,title,indexed,is_error) VALUES(?,?,?,?)')
db.transaction(() => {
for (let i = 0; i < 20_000; i++) {
const route = `/page/${String(i).padStart(5, '0')}`
insert.run(route, `Page ${i}`, i % 2, i % 5 === 0 ? 1 : 0)
}
})()
const migration = readFileSync('migrations/0136_ai_ready_route_order_index.sql', 'utf8')
db.exec(migration)
db.exec(migration)
const sql = 'SELECT route, title, description, headings, keywords, updated_at, is_error, locale FROM ai_ready_pages WHERE indexed = ? AND is_error = 0 ORDER BY route LIMIT ?'
const plan = db.prepare(`EXPLAIN QUERY PLAN ${sql}`).all(1, 3) as Array<{ detail: string }>
expect(plan.map(row => row.detail).join('\n')).not.toContain('TEMP B-TREE')
expect(db.prepare(sql).all(1, 3)).toMatchObject([
{ route: '/page/00001', title: 'Page 1' },
{ route: '/page/00003', title: 'Page 3' },
{ route: '/page/00007', title: 'Page 7' },
])
db.close()
})
})