Skip to main content
Glama
n-r-w

KnowledgeGraph MCP Server

by n-r-w
tag-search-investigation.test.ts9.95 kB
import { KnowledgeGraphManager } from '../core.js'; import { Entity } from '../core.js'; import { runTestsForAvailableBackends, createTestProject, getBackendTimeout } from './utils/multi-backend-runner.js'; import { StorageConfig } from '../storage/types.js'; // Test tag search functionality across multiple backends runTestsForAvailableBackends((config: StorageConfig, backendName: string) => { describe('Tag Search Investigation', () => { let manager: KnowledgeGraphManager; let testProject: string; beforeEach(async () => { testProject = createTestProject(backendName, 'tag_search_investigation'); manager = new KnowledgeGraphManager(config); }, getBackendTimeout(backendName)); afterEach(async () => { // Clean up test data try { const existingEntities = await manager.readGraph(testProject); if (existingEntities.entities.length > 0) { await manager.deleteEntities(existingEntities.entities.map(e => e.name), testProject); } } catch (error) { // Ignore errors if project doesn't exist } await manager.close(); }, getBackendTimeout(backendName)); describe('Tag Search Issue Investigation', () => { beforeEach(async () => { // Create test entities with various tag combinations await manager.createEntities([ { name: 'Entity_With_Tag_A', entityType: 'concept', observations: ['Has tag A'], tags: ['tagA', 'common'] }, { name: 'Entity_With_Tag_B', entityType: 'concept', observations: ['Has tag B'], tags: ['tagB', 'common'] }, { name: 'Entity_With_Both_Tags', entityType: 'concept', observations: ['Has both tags'], tags: ['tagA', 'tagB', 'special'] }, { name: 'Entity_No_Tags', entityType: 'concept', observations: ['Has no tags'], tags: [] }, { name: 'Entity_Different_Tags', entityType: 'concept', observations: ['Has different tags'], tags: ['other', 'different'] } ], testProject); }, getBackendTimeout(backendName)); it('should find entities with exactTags using empty query', async () => { console.log(`\n🔍 Testing tag search with backend: ${backendName}`); // Test 1: Search for entities with tagA const resultA = await manager.searchNodes('', { exactTags: ['tagA'] }, testProject); console.log(`Tag A search results: ${resultA.entities.length} entities found`); console.log(`Entity names: ${resultA.entities.map(e => e.name).join(', ')}`); expect(resultA.entities.length).toBe(2); // Entity_With_Tag_A and Entity_With_Both_Tags expect(resultA.entities.map(e => e.name)).toContain('Entity_With_Tag_A'); expect(resultA.entities.map(e => e.name)).toContain('Entity_With_Both_Tags'); }, getBackendTimeout(backendName)); it('should find entities with exactTags using tagMatchMode any', async () => { const result = await manager.searchNodes('', { exactTags: ['tagA', 'tagB'], tagMatchMode: 'any' }, testProject); console.log(`Tag A OR B search results: ${result.entities.length} entities found`); console.log(`Entity names: ${result.entities.map(e => e.name).join(', ')}`); expect(result.entities.length).toBe(3); // All entities with either tagA or tagB expect(result.entities.map(e => e.name)).toContain('Entity_With_Tag_A'); expect(result.entities.map(e => e.name)).toContain('Entity_With_Tag_B'); expect(result.entities.map(e => e.name)).toContain('Entity_With_Both_Tags'); }, getBackendTimeout(backendName)); it('should find entities with exactTags using tagMatchMode all', async () => { const result = await manager.searchNodes('', { exactTags: ['tagA', 'tagB'], tagMatchMode: 'all' }, testProject); console.log(`Tag A AND B search results: ${result.entities.length} entities found`); console.log(`Entity names: ${result.entities.map(e => e.name).join(', ')}`); expect(result.entities.length).toBe(1); // Only Entity_With_Both_Tags expect(result.entities[0].name).toBe('Entity_With_Both_Tags'); }, getBackendTimeout(backendName)); it('should return empty results for non-existent tags', async () => { const result = await manager.searchNodes('', { exactTags: ['nonexistent'] }, testProject); console.log(`Non-existent tag search results: ${result.entities.length} entities found`); expect(result.entities.length).toBe(0); }, getBackendTimeout(backendName)); it('should work with common tags', async () => { const result = await manager.searchNodes('', { exactTags: ['common'] }, testProject); console.log(`Common tag search results: ${result.entities.length} entities found`); console.log(`Entity names: ${result.entities.map(e => e.name).join(', ')}`); expect(result.entities.length).toBe(2); // Entity_With_Tag_A and Entity_With_Tag_B expect(result.entities.map(e => e.name)).toContain('Entity_With_Tag_A'); expect(result.entities.map(e => e.name)).toContain('Entity_With_Tag_B'); }, getBackendTimeout(backendName)); it('should debug the search flow step by step', async () => { console.log('\n🔬 DEBUGGING SEARCH FLOW:'); // Step 1: Verify entities exist const allEntities = await manager.readGraph(testProject); console.log(`Total entities in project: ${allEntities.entities.length}`); allEntities.entities.forEach(e => { console.log(` - ${e.name}: tags=[${e.tags?.join(', ') || 'none'}]`); }); // Step 2: Test direct tag filtering logic const entitiesWithTagA = allEntities.entities.filter(entity => { const entityTags = entity.tags || []; return entityTags.includes('tagA'); }); console.log(`Direct filter for tagA: ${entitiesWithTagA.length} entities`); // Step 3: Test the actual search const searchResult = await manager.searchNodes('', { exactTags: ['tagA'] }, testProject); console.log(`Search result for tagA: ${searchResult.entities.length} entities`); // They should match expect(searchResult.entities.length).toBe(entitiesWithTagA.length); }, getBackendTimeout(backendName)); }); describe('Paginated Tag Search', () => { beforeEach(async () => { // Create more entities for pagination testing const entities = []; for (let i = 1; i <= 25; i++) { entities.push({ name: `Paginated_Entity_${i}`, entityType: 'concept', observations: [`Entity number ${i}`], tags: i % 2 === 0 ? ['even'] : ['odd'] }); } await manager.createEntities(entities, testProject); }, getBackendTimeout(backendName)); it('should handle paginated tag search', async () => { const result = await manager.searchNodesPaginated( '', { page: 0, pageSize: 5 }, { exactTags: ['even'] }, testProject ); console.log(`\n🔍 PAGINATED TAG SEARCH DEBUG (${backendName}):`); console.log(`Paginated even tag search: ${result.entities.length} entities on page 1`); console.log(`Total count: ${result.pagination.totalCount}`); console.log(`Entity names: ${result.entities.map(e => e.name).join(', ')}`); console.log(`Entity tags: ${result.entities.map(e => e.tags?.join(',') || 'none').join(' | ')}`); // Verify that all returned entities actually have the 'even' tag result.entities.forEach(entity => { console.log(` - ${entity.name}: tags=[${entity.tags?.join(', ') || 'none'}]`); expect(entity.tags).toContain('even'); }); expect(result.entities.length).toBe(5); expect(result.pagination.totalCount).toBe(12); // 12 even numbers from 1-25 expect(result.pagination.hasNextPage).toBe(true); }, getBackendTimeout(backendName)); it('should verify tag search works with different tag combinations', async () => { console.log(`\n🧪 COMPREHENSIVE TAG SEARCH TEST (${backendName}):`); // Test 1: Search for 'odd' tags const oddResult = await manager.searchNodesPaginated( '', { page: 0, pageSize: 10 }, { exactTags: ['odd'] }, testProject ); console.log(`Odd tag search: ${oddResult.entities.length} entities, total: ${oddResult.pagination.totalCount}`); expect(oddResult.pagination.totalCount).toBe(13); // 13 odd numbers from 1-25 // Test 2: Search for non-existent tag const nonExistentResult = await manager.searchNodesPaginated( '', { page: 0, pageSize: 10 }, { exactTags: ['nonexistent'] }, testProject ); console.log(`Non-existent tag search: ${nonExistentResult.entities.length} entities, total: ${nonExistentResult.pagination.totalCount}`); expect(nonExistentResult.pagination.totalCount).toBe(0); // Test 3: Compare with non-paginated search const nonPaginatedEven = await manager.searchNodes('', { exactTags: ['even'] }, testProject); console.log(`Non-paginated even search: ${nonPaginatedEven.entities.length} entities`); // The counts should match expect(oddResult.pagination.totalCount).toBe(13); // Total odd entities should be 13 expect(nonPaginatedEven.entities.length).toBe(12); // This should match paginated total count }, getBackendTimeout(backendName)); }); }); });

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