fix: add FTS5 search index to prevent 69% search failure rate (v2.18.5)

Fixes production search failures where 69% of user searches returned zero
results for critical nodes (webhook, merge, split batch) despite nodes
existing in database.

Root Cause:
- schema.sql missing nodes_fts FTS5 virtual table
- No validation to detect empty database or missing FTS5
- rebuild.ts used schema without search index
- Result: 9 of 13 searches failed in production

Changes:
1. Schema Updates (src/database/schema.sql):
   - Added nodes_fts FTS5 virtual table with full-text indexing
   - Added INSERT/UPDATE/DELETE triggers for auto-sync
   - Indexes: node_type, display_name, description, documentation, operations

2. Database Validation (src/scripts/rebuild.ts):
   - Added empty database detection (fails if zero nodes)
   - Added FTS5 existence and synchronization validation
   - Added searchability tests for critical nodes
   - Added minimum node count check (500+)

3. Runtime Health Checks (src/mcp/server.ts):
   - Database health validation on first access
   - Detects empty database with clear error
   - Detects missing FTS5 with actionable warning

4. Test Suite (53 new tests):
   - tests/integration/database/node-fts5-search.test.ts (14 tests)
   - tests/integration/database/empty-database.test.ts (14 tests)
   - tests/integration/ci/database-population.test.ts (25 tests)

5. Database Rebuild:
   - data/nodes.db rebuilt with FTS5 index
   - 535 nodes fully synchronized with FTS5

Impact:
-  All critical searches now work (webhook, merge, split, code, http)
-  FTS5 provides fast ranked search (< 100ms)
-  Clear error messages if database empty
-  CI validates committed database integrity
-  Runtime health checks detect issues immediately

Performance:
- FTS5 search: < 100ms for typical queries
- LIKE fallback: < 500ms (unchanged, still functional)

Testing: LIKE search investigation revealed it was perfectly functional,
only failed because database was empty. No changes needed.

Related: Issue #296 Part 2 (Part 1: v2.18.4 fixed adapter bypass)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-10-10 09:16:20 +02:00
parent 978347e8d0
commit cfd2325ca4
9 changed files with 1015 additions and 23 deletions

View File

@@ -201,6 +201,48 @@ export class N8NDocumentationMCPServer {
if (!this.db || !this.repository) {
throw new Error('Database not initialized');
}
// Validate database health on first access
if (!this.dbHealthChecked) {
await this.validateDatabaseHealth();
this.dbHealthChecked = true;
}
}
private dbHealthChecked: boolean = false;
private async validateDatabaseHealth(): Promise<void> {
if (!this.db) return;
try {
// Check if nodes table has data
const nodeCount = this.db.prepare('SELECT COUNT(*) as count FROM nodes').get() as { count: number };
if (nodeCount.count === 0) {
logger.error('CRITICAL: Database is empty - no nodes found! Please run: npm run rebuild');
throw new Error('Database is empty. Run "npm run rebuild" to populate node data.');
}
// Check if FTS5 table exists
const ftsExists = this.db.prepare(`
SELECT name FROM sqlite_master
WHERE type='table' AND name='nodes_fts'
`).get();
if (!ftsExists) {
logger.warn('FTS5 table missing - search performance will be degraded. Please run: npm run rebuild');
} else {
const ftsCount = this.db.prepare('SELECT COUNT(*) as count FROM nodes_fts').get() as { count: number };
if (ftsCount.count === 0) {
logger.warn('FTS5 index is empty - search will not work properly. Please run: npm run rebuild');
}
}
logger.info(`Database health check passed: ${nodeCount.count} nodes loaded`);
} catch (error) {
logger.error('Database health check failed:', error);
throw error;
}
}
private setupHandlers(): void {