@@ -30,9 +30,69 @@ const CHUNK_SIZE = 500;
3030
3131// Database initialization
3232async function initializeDatabase ( db : D1Database ) : Promise < void > {
33+ const dropSession = db . withSession ( 'first-primary' ) ;
34+
3335 try {
34- const session = db . withSession ( 'first-unconstrained' ) ;
35- const initCheck = await session . prepare (
36+ await dropSession . prepare ( `
37+ CREATE TABLE IF NOT EXISTS system_config (
38+ key TEXT PRIMARY KEY,
39+ value TEXT NOT NULL,
40+ created_at INTEGER DEFAULT (strftime('%s', 'now'))
41+ )
42+ ` ) . run ( ) ;
43+ } catch ( _ ) { }
44+
45+ const cleanupDone = await dropSession . prepare (
46+ "SELECT value FROM system_config WHERE key = 'cleanup_v1' LIMIT 1"
47+ ) . first ( ) . catch ( ( ) => null ) ;
48+
49+ if ( ! cleanupDone || cleanupDone . value !== '1' ) {
50+ const dropIndexes = [
51+ 'idx_events_pubkey' ,
52+ 'idx_events_kind' ,
53+ 'idx_events_created_at_kind' ,
54+ 'idx_events_authors_kinds' ,
55+ 'idx_events_tag_p_created_at' ,
56+ 'idx_events_tag_e_created_at' ,
57+ 'idx_events_tag_a_created_at' ,
58+ 'idx_events_tag_t_created_at' ,
59+ 'idx_events_tag_d_created_at' ,
60+ 'idx_events_tag_r_created_at' ,
61+ 'idx_events_tag_L_created_at' ,
62+ 'idx_events_tag_s_created_at' ,
63+ 'idx_events_tag_u_created_at' ,
64+ 'idx_events_kind_tag_p' ,
65+ 'idx_events_kind_tag_e' ,
66+ 'idx_events_kind_tag_a' ,
67+ 'idx_events_kind_tag_t' ,
68+ 'idx_events_kind_tag_L' ,
69+ 'idx_events_kind_tag_s' ,
70+ 'idx_events_reply_to' ,
71+ 'idx_events_root_thread' ,
72+ 'idx_events_kind_created_at_covering' ,
73+ 'idx_events_pubkey_kind_created_at_covering' ,
74+ 'idx_events_created_at_covering' ,
75+ 'idx_events_kind_pubkey_created_at_covering' ,
76+ 'idx_tags_name_value' ,
77+ 'idx_tags_value' ,
78+ 'idx_tags_name_value_event_created' ,
79+ ] ;
80+ for ( const idx of dropIndexes ) {
81+ await dropSession . prepare ( `DROP INDEX IF EXISTS ${ idx } ` ) . run ( ) ;
82+ }
83+
84+ const dropTables = [ 'event_tags_cache' , 'mv_follow_graph' , 'mv_recent_notes' , 'mv_timeline_cache' ] ;
85+ for ( const tbl of dropTables ) {
86+ await dropSession . prepare ( `DROP TABLE IF EXISTS ${ tbl } ` ) . run ( ) ;
87+ }
88+
89+ await dropSession . prepare (
90+ "INSERT OR REPLACE INTO system_config (key, value) VALUES ('cleanup_v1', '1')"
91+ ) . run ( ) ;
92+ }
93+
94+ try {
95+ const initCheck = await dropSession . prepare (
3696 "SELECT value FROM system_config WHERE key = 'db_initialized' LIMIT 1"
3797 ) . first ( ) . catch ( ( ) => null ) ;
3898
@@ -94,8 +154,6 @@ async function initializeDatabase(db: D1Database): Promise<void> {
94154 `CREATE INDEX IF NOT EXISTS idx_tags_name_value_event ON tags(tag_name, tag_value, event_id)` ,
95155 `CREATE INDEX IF NOT EXISTS idx_tags_event_id ON tags(event_id)` ,
96156
97- `DROP TABLE IF EXISTS event_tags_cache` ,
98-
99157 `CREATE TABLE IF NOT EXISTS event_tags_cache_multi (
100158 event_id TEXT NOT NULL,
101159 pubkey TEXT NOT NULL,
@@ -126,52 +184,13 @@ async function initializeDatabase(db: D1Database): Promise<void> {
126184 )` ,
127185 `CREATE INDEX IF NOT EXISTS idx_content_hashes_pubkey ON content_hashes(pubkey)` ,
128186 `CREATE INDEX IF NOT EXISTS idx_content_hashes_created_at ON content_hashes(created_at DESC)` ,
129- `CREATE INDEX IF NOT EXISTS idx_content_hashes_pubkey_created ON content_hashes(pubkey, created_at DESC)` ,
130-
131- `DROP TABLE IF EXISTS mv_follow_graph` ,
132- `DROP TABLE IF EXISTS mv_recent_notes` ,
133- `DROP TABLE IF EXISTS mv_timeline_cache`
187+ `CREATE INDEX IF NOT EXISTS idx_content_hashes_pubkey_created ON content_hashes(pubkey, created_at DESC)`
134188 ] ;
135189
136190 for ( const statement of statements ) {
137191 await session . prepare ( statement ) . run ( ) ;
138192 }
139193
140- // Drop unused indexes from existing database
141- const dropIndexes = [
142- 'idx_events_pubkey' ,
143- 'idx_events_kind' ,
144- 'idx_events_created_at_kind' ,
145- 'idx_events_authors_kinds' ,
146- 'idx_events_tag_p_created_at' ,
147- 'idx_events_tag_e_created_at' ,
148- 'idx_events_tag_a_created_at' ,
149- 'idx_events_tag_t_created_at' ,
150- 'idx_events_tag_d_created_at' ,
151- 'idx_events_tag_r_created_at' ,
152- 'idx_events_tag_L_created_at' ,
153- 'idx_events_tag_s_created_at' ,
154- 'idx_events_tag_u_created_at' ,
155- 'idx_events_kind_tag_p' ,
156- 'idx_events_kind_tag_e' ,
157- 'idx_events_kind_tag_a' ,
158- 'idx_events_kind_tag_t' ,
159- 'idx_events_kind_tag_L' ,
160- 'idx_events_kind_tag_s' ,
161- 'idx_events_reply_to' ,
162- 'idx_events_root_thread' ,
163- 'idx_events_kind_created_at_covering' ,
164- 'idx_events_pubkey_kind_created_at_covering' ,
165- 'idx_events_created_at_covering' ,
166- 'idx_events_kind_pubkey_created_at_covering' ,
167- 'idx_tags_name_value' ,
168- 'idx_tags_value' ,
169- 'idx_tags_name_value_event_created' ,
170- ] ;
171- for ( const idx of dropIndexes ) {
172- await session . prepare ( `DROP INDEX IF EXISTS ${ idx } ` ) . run ( ) ;
173- }
174-
175194 await session . prepare ( "PRAGMA foreign_keys = ON" ) . run ( ) ;
176195 await session . prepare (
177196 "INSERT OR REPLACE INTO system_config (key, value) VALUES ('db_initialized', '1')"
@@ -1148,7 +1167,6 @@ function buildQuery(filter: NostrFilter): { sql: string; params: any[] } {
11481167 const tagFilter = directTags [ 0 ] ;
11491168 const hasKinds = filter . kinds && filter . kinds . length > 0 ;
11501169
1151- // Choose optimal index based on query pattern
11521170 let indexHint = "" ;
11531171 if ( hasKinds && filter . kinds ! . length <= 10 ) {
11541172 indexHint = " INDEXED BY idx_cache_multi_kind_type_value" ;
@@ -1161,12 +1179,10 @@ function buildQuery(filter: NostrFilter): { sql: string; params: any[] } {
11611179 WHERE m.tag_type = ? AND m.tag_value IN (${ tagFilter . values . map ( ( ) => '?' ) . join ( ',' ) } )` ;
11621180 params . push ( tagFilter . name , ...tagFilter . values ) ;
11631181 } else {
1164- // Multiple tag types - need to match ALL tag conditions
11651182 const hasKindsMulti = filter . kinds && filter . kinds . length > 0 ;
11661183 const tagConditions = directTags . map ( ( t , i ) => {
11671184 const alias = `m${ i } ` ;
11681185 const placeholders = t . values . map ( ( ) => '?' ) . join ( ',' ) ;
1169- // Add index hint to first alias for better query planning
11701186 const hint = i === 0
11711187 ? ( hasKindsMulti && filter . kinds ! . length <= 10
11721188 ? " INDEXED BY idx_cache_multi_kind_type_value"
0 commit comments