Skip to main content
Glama
n-r-w

KnowledgeGraph MCP Server

by n-r-w
cross-database-comparison.test.ts10.1 kB
import { describe, test, expect, beforeAll, afterAll } from '@jest/globals'; import { Pool as PgPool } from 'pg'; import Database from 'better-sqlite3'; import { PostgreSQLFuzzyStrategy } from '../../search/strategies/postgresql-strategy.js'; import { SQLiteFuzzyStrategy } from '../../search/strategies/sqlite-strategy.js'; import { SearchConfig } from '../../search/types.js'; import { Entity } from '../../core.js'; import { PerformanceUtils } from '../performance/performance-utils.js'; import { checkPostgreSQLAvailability } from '../utils/multi-backend-runner.js'; describe('Cross-Database Search Comparison', () => { let pgPool: PgPool | null = null; let sqliteDb: Database.Database | null = null; let pgStrategy: PostgreSQLFuzzyStrategy | null = null; let sqliteStrategy: SQLiteFuzzyStrategy | null = null; const testProject = 'cross_db_test'; const config: SearchConfig = { threshold: 0.3, useDatabaseSearch: true, clientSideFallback: true, fuseOptions: { threshold: 0.3, distance: 100, includeScore: true, keys: ['name', 'entityType', 'observations', 'tags'] } }; const testEntities: Entity[] = [ { name: 'JavaScript_Developer', entityType: 'person', observations: ['Experienced in React and Node.js', 'Works at tech startup'], tags: ['developer', 'frontend', 'backend'] }, { name: 'React_Framework', entityType: 'technology', observations: ['Popular frontend library', 'Component-based architecture'], tags: ['frontend', 'library', 'javascript'] }, { name: 'Node_Runtime', entityType: 'technology', observations: ['Server-side JavaScript runtime', 'Built on V8 engine'], tags: ['backend', 'runtime', 'javascript'] }, { name: 'Python_Developer', entityType: 'person', observations: ['Expert in Django and Flask', 'Data science background'], tags: ['developer', 'backend', 'data'] }, { name: 'Django_Framework', entityType: 'technology', observations: ['Python web framework', 'Batteries included approach'], tags: ['backend', 'framework', 'python'] } ]; beforeAll(async () => { // Setup PostgreSQL if available try { const isPostgreSQLAvailable = await checkPostgreSQLAvailability(); if (isPostgreSQLAvailable) { pgPool = new PgPool({ connectionString: 'postgresql://postgres:password@localhost:5432/knowledgegraph_test' }); // Test connection const client = await pgPool.connect(); client.release(); pgStrategy = new PostgreSQLFuzzyStrategy(config, pgPool, testProject); // Setup test data in PostgreSQL await setupPostgreSQLTestData(); } } catch (error) { console.log('PostgreSQL not available for cross-database testing:', error); } // Setup SQLite sqliteDb = new Database(':memory:'); sqliteStrategy = new SQLiteFuzzyStrategy(config, sqliteDb, testProject); // Setup test data in SQLite await setupSQLiteTestData(); }); afterAll(async () => { if (pgPool) { await pgPool.end(); } if (sqliteDb) { sqliteDb.close(); } }); async function setupPostgreSQLTestData() { if (!pgPool) return; const client = await pgPool.connect(); try { // Create tables await client.query(` CREATE TABLE IF NOT EXISTS entities ( name TEXT PRIMARY KEY, entity_type TEXT NOT NULL, observations JSONB DEFAULT '[]', tags JSONB DEFAULT '[]', project TEXT NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Clear existing test data await client.query('DELETE FROM entities WHERE project = $1', [testProject]); // Insert test entities for (const entity of testEntities) { await client.query(` INSERT INTO entities (name, entity_type, observations, tags, project) VALUES ($1, $2, $3, $4, $5) `, [ entity.name, entity.entityType, JSON.stringify(entity.observations), JSON.stringify(entity.tags), testProject ]); } } finally { client.release(); } } async function setupSQLiteTestData() { if (!sqliteDb) return; // Create tables sqliteDb.exec(` CREATE TABLE IF NOT EXISTS entities ( name TEXT PRIMARY KEY, entity_type TEXT NOT NULL, observations TEXT DEFAULT '[]', tags TEXT DEFAULT '[]', project TEXT NOT NULL, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ) `); // Clear existing test data sqliteDb.prepare('DELETE FROM entities WHERE project = ?').run(testProject); // Insert test entities const stmt = sqliteDb.prepare(` INSERT INTO entities (name, entity_type, observations, tags, project) VALUES (?, ?, ?, ?, ?) `); for (const entity of testEntities) { stmt.run( entity.name, entity.entityType, JSON.stringify(entity.observations), JSON.stringify(entity.tags), testProject ); } } test('should compare single query performance between PostgreSQL and SQLite', async () => { if (!pgStrategy || !sqliteStrategy) { console.log('Skipping cross-database test - databases not available'); return; } const query = 'JavaScript'; // PostgreSQL fuzzy search benchmark const pgBenchmark = await PerformanceUtils.runBenchmark(async () => { return pgStrategy!.searchDatabase(query, 0.3, testProject); }, 5); // Load SQLite entities for client-side search const entities = await sqliteStrategy.getAllEntities(testProject); // SQLite client-side search benchmark const sqliteBenchmark = await PerformanceUtils.runBenchmark(async () => { return sqliteStrategy!.searchClientSide(entities, query); }, 5); console.log('\nSingle Query Performance Comparison:'); console.log('PostgreSQL (Database):'); console.log(PerformanceUtils.formatBenchmarkResults(pgBenchmark)); console.log('\nSQLite (Client-side):'); console.log(PerformanceUtils.formatBenchmarkResults(sqliteBenchmark)); // Both should find relevant results expect(pgBenchmark.results[0].length).toBeGreaterThan(0); expect(sqliteBenchmark.results[0].length).toBeGreaterThan(0); // Results should be reasonably similar (within 2 entities difference) expect(Math.abs(pgBenchmark.results[0].length - sqliteBenchmark.results[0].length)).toBeLessThanOrEqual(2); }); test('should compare multiple query performance between PostgreSQL and SQLite', async () => { if (!pgStrategy || !sqliteStrategy) { console.log('Skipping cross-database test - databases not available'); return; } const queries = ['JavaScript', 'React', 'Node.js']; // PostgreSQL multiple query benchmark const pgBenchmark = await PerformanceUtils.runBenchmark(async () => { return pgStrategy!.searchDatabase(queries, 0.3, testProject); }, 5); // Load SQLite entities for client-side search const entities = await sqliteStrategy.getAllEntities(testProject); // SQLite multiple query benchmark const sqliteBenchmark = await PerformanceUtils.runBenchmark(async () => { return sqliteStrategy!.searchClientSide(entities, queries); }, 5); console.log('\nMultiple Query Performance Comparison:'); console.log('PostgreSQL (Database):'); console.log(PerformanceUtils.formatBenchmarkResults(pgBenchmark)); console.log('\nSQLite (Client-side):'); console.log(PerformanceUtils.formatBenchmarkResults(sqliteBenchmark)); // Both should find relevant results expect(pgBenchmark.results[0].length).toBeGreaterThan(0); expect(sqliteBenchmark.results[0].length).toBeGreaterThan(0); // Results should be reasonably similar (within 3 entities difference for multiple queries) expect(Math.abs(pgBenchmark.results[0].length - sqliteBenchmark.results[0].length)).toBeLessThanOrEqual(3); }); test('should handle error conditions consistently', async () => { if (!pgStrategy || !sqliteStrategy) { console.log('Skipping error handling test - databases not available'); return; } // Test with invalid project (should both handle gracefully) const invalidProject = 'nonexistent_project_12345'; const pgResults = await pgStrategy.searchDatabase('test', 0.3, invalidProject); const sqliteResults = await sqliteStrategy.searchClientSide([], 'test'); // Both should return empty arrays for invalid/empty data expect(pgResults).toEqual([]); expect(sqliteResults).toEqual([]); }); test('should compare exact search performance for SQLite', async () => { if (!sqliteStrategy) { console.log('Skipping SQLite exact search test - SQLite not available'); return; } const query = 'JavaScript'; const queries = ['JavaScript', 'React']; // Single exact search benchmark const singleBenchmark = await PerformanceUtils.runBenchmark(async () => { return sqliteStrategy!.searchExact(query, testProject); }, 5); // Multiple exact search benchmark const multipleBenchmark = await PerformanceUtils.runBenchmark(async () => { return sqliteStrategy!.searchExact(queries, testProject); }, 5); console.log('\nSQLite Exact Search Performance:'); console.log('Single Query:'); console.log(PerformanceUtils.formatBenchmarkResults(singleBenchmark)); console.log('\nMultiple Queries:'); console.log(PerformanceUtils.formatBenchmarkResults(multipleBenchmark)); // Both should find results expect(singleBenchmark.results[0].length).toBeGreaterThan(0); expect(multipleBenchmark.results[0].length).toBeGreaterThan(0); // Multiple queries should find at least as many results as single query expect(multipleBenchmark.results[0].length).toBeGreaterThanOrEqual(singleBenchmark.results[0].length); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/n-r-w/knowledgegraph-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server