@danielsimonjr/memory-mcp 0.48.0 → 9.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +2000 -194
- package/dist/__tests__/file-path.test.js +7 -11
- package/dist/__tests__/knowledge-graph.test.js +3 -8
- package/dist/core/EntityManager.d.ts +266 -0
- package/dist/core/EntityManager.d.ts.map +1 -0
- package/dist/core/EntityManager.js +85 -133
- package/dist/core/GraphEventEmitter.d.ts +202 -0
- package/dist/core/GraphEventEmitter.d.ts.map +1 -0
- package/dist/core/GraphEventEmitter.js +346 -0
- package/dist/core/GraphStorage.d.ts +395 -0
- package/dist/core/GraphStorage.d.ts.map +1 -0
- package/dist/core/GraphStorage.js +643 -31
- package/dist/core/GraphTraversal.d.ts +141 -0
- package/dist/core/GraphTraversal.d.ts.map +1 -0
- package/dist/core/GraphTraversal.js +573 -0
- package/dist/core/HierarchyManager.d.ts +111 -0
- package/dist/core/HierarchyManager.d.ts.map +1 -0
- package/dist/{features → core}/HierarchyManager.js +14 -9
- package/dist/core/ManagerContext.d.ts +72 -0
- package/dist/core/ManagerContext.d.ts.map +1 -0
- package/dist/core/ManagerContext.js +118 -0
- package/dist/core/ObservationManager.d.ts +85 -0
- package/dist/core/ObservationManager.d.ts.map +1 -0
- package/dist/core/ObservationManager.js +51 -57
- package/dist/core/RelationManager.d.ts +131 -0
- package/dist/core/RelationManager.d.ts.map +1 -0
- package/dist/core/RelationManager.js +31 -7
- package/dist/core/SQLiteStorage.d.ts +354 -0
- package/dist/core/SQLiteStorage.d.ts.map +1 -0
- package/dist/core/SQLiteStorage.js +917 -0
- package/dist/core/StorageFactory.d.ts +45 -0
- package/dist/core/StorageFactory.d.ts.map +1 -0
- package/dist/core/StorageFactory.js +64 -0
- package/dist/core/TransactionManager.d.ts +464 -0
- package/dist/core/TransactionManager.d.ts.map +1 -0
- package/dist/core/TransactionManager.js +490 -13
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +12 -2
- package/dist/features/AnalyticsManager.d.ts +44 -0
- package/dist/features/AnalyticsManager.d.ts.map +1 -0
- package/dist/features/AnalyticsManager.js +3 -2
- package/dist/features/ArchiveManager.d.ts +133 -0
- package/dist/features/ArchiveManager.d.ts.map +1 -0
- package/dist/features/ArchiveManager.js +221 -14
- package/dist/features/CompressionManager.d.ts +117 -0
- package/dist/features/CompressionManager.d.ts.map +1 -0
- package/dist/features/CompressionManager.js +189 -20
- package/dist/features/IOManager.d.ts +225 -0
- package/dist/features/IOManager.d.ts.map +1 -0
- package/dist/features/IOManager.js +1041 -0
- package/dist/features/StreamingExporter.d.ts +123 -0
- package/dist/features/StreamingExporter.d.ts.map +1 -0
- package/dist/features/StreamingExporter.js +203 -0
- package/dist/features/TagManager.d.ts +147 -0
- package/dist/features/TagManager.d.ts.map +1 -0
- package/dist/features/index.d.ts +12 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +5 -6
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -10
- package/dist/memory.jsonl +1 -26
- package/dist/search/BasicSearch.d.ts +51 -0
- package/dist/search/BasicSearch.d.ts.map +1 -0
- package/dist/search/BasicSearch.js +9 -3
- package/dist/search/BooleanSearch.d.ts +98 -0
- package/dist/search/BooleanSearch.d.ts.map +1 -0
- package/dist/search/BooleanSearch.js +156 -9
- package/dist/search/EmbeddingService.d.ts +178 -0
- package/dist/search/EmbeddingService.d.ts.map +1 -0
- package/dist/search/EmbeddingService.js +358 -0
- package/dist/search/FuzzySearch.d.ts +118 -0
- package/dist/search/FuzzySearch.d.ts.map +1 -0
- package/dist/search/FuzzySearch.js +241 -25
- package/dist/search/QueryCostEstimator.d.ts +111 -0
- package/dist/search/QueryCostEstimator.d.ts.map +1 -0
- package/dist/search/QueryCostEstimator.js +355 -0
- package/dist/search/RankedSearch.d.ts +71 -0
- package/dist/search/RankedSearch.d.ts.map +1 -0
- package/dist/search/RankedSearch.js +54 -6
- package/dist/search/SavedSearchManager.d.ts +79 -0
- package/dist/search/SavedSearchManager.d.ts.map +1 -0
- package/dist/search/SearchFilterChain.d.ts +120 -0
- package/dist/search/SearchFilterChain.d.ts.map +1 -0
- package/dist/search/SearchFilterChain.js +2 -4
- package/dist/search/SearchManager.d.ts +326 -0
- package/dist/search/SearchManager.d.ts.map +1 -0
- package/dist/search/SearchManager.js +148 -0
- package/dist/search/SearchSuggestions.d.ts +27 -0
- package/dist/search/SearchSuggestions.d.ts.map +1 -0
- package/dist/search/SearchSuggestions.js +1 -1
- package/dist/search/SemanticSearch.d.ts +149 -0
- package/dist/search/SemanticSearch.d.ts.map +1 -0
- package/dist/search/SemanticSearch.js +323 -0
- package/dist/search/TFIDFEventSync.d.ts +85 -0
- package/dist/search/TFIDFEventSync.d.ts.map +1 -0
- package/dist/search/TFIDFEventSync.js +133 -0
- package/dist/search/TFIDFIndexManager.d.ts +151 -0
- package/dist/search/TFIDFIndexManager.d.ts.map +1 -0
- package/dist/search/TFIDFIndexManager.js +232 -17
- package/dist/search/VectorStore.d.ts +235 -0
- package/dist/search/VectorStore.d.ts.map +1 -0
- package/dist/search/VectorStore.js +311 -0
- package/dist/search/index.d.ts +21 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +12 -0
- package/dist/server/MCPServer.d.ts +21 -0
- package/dist/server/MCPServer.d.ts.map +1 -0
- package/dist/server/MCPServer.js +4 -4
- package/dist/server/responseCompressor.d.ts +94 -0
- package/dist/server/responseCompressor.d.ts.map +1 -0
- package/dist/server/responseCompressor.js +127 -0
- package/dist/server/toolDefinitions.d.ts +27 -0
- package/dist/server/toolDefinitions.d.ts.map +1 -0
- package/dist/server/toolDefinitions.js +188 -17
- package/dist/server/toolHandlers.d.ts +41 -0
- package/dist/server/toolHandlers.d.ts.map +1 -0
- package/dist/server/toolHandlers.js +467 -75
- package/dist/types/index.d.ts +13 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -1
- package/dist/types/types.d.ts +1654 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/types.js +9 -0
- package/dist/utils/compressedCache.d.ts +192 -0
- package/dist/utils/compressedCache.d.ts.map +1 -0
- package/dist/utils/compressedCache.js +309 -0
- package/dist/utils/compressionUtil.d.ts +214 -0
- package/dist/utils/compressionUtil.d.ts.map +1 -0
- package/dist/utils/compressionUtil.js +247 -0
- package/dist/utils/constants.d.ts +245 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +124 -0
- package/dist/utils/entityUtils.d.ts +321 -0
- package/dist/utils/entityUtils.d.ts.map +1 -0
- package/dist/utils/entityUtils.js +434 -4
- package/dist/utils/errors.d.ts +95 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +24 -0
- package/dist/utils/formatters.d.ts +145 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/{paginationUtils.js → formatters.js} +54 -3
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +69 -31
- package/dist/utils/indexes.d.ts +270 -0
- package/dist/utils/indexes.d.ts.map +1 -0
- package/dist/utils/indexes.js +526 -0
- package/dist/utils/logger.d.ts +24 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/operationUtils.d.ts +124 -0
- package/dist/utils/operationUtils.d.ts.map +1 -0
- package/dist/utils/operationUtils.js +175 -0
- package/dist/utils/parallelUtils.d.ts +72 -0
- package/dist/utils/parallelUtils.d.ts.map +1 -0
- package/dist/utils/parallelUtils.js +169 -0
- package/dist/utils/schemas.d.ts +374 -0
- package/dist/utils/schemas.d.ts.map +1 -0
- package/dist/utils/schemas.js +302 -2
- package/dist/utils/searchAlgorithms.d.ts +99 -0
- package/dist/utils/searchAlgorithms.d.ts.map +1 -0
- package/dist/utils/searchAlgorithms.js +167 -0
- package/dist/utils/searchCache.d.ts +108 -0
- package/dist/utils/searchCache.d.ts.map +1 -0
- package/dist/utils/taskScheduler.d.ts +290 -0
- package/dist/utils/taskScheduler.d.ts.map +1 -0
- package/dist/utils/taskScheduler.js +466 -0
- package/dist/workers/index.d.ts +12 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +9 -0
- package/dist/workers/levenshteinWorker.d.ts +60 -0
- package/dist/workers/levenshteinWorker.d.ts.map +1 -0
- package/dist/workers/levenshteinWorker.js +98 -0
- package/package.json +17 -4
- package/dist/__tests__/edge-cases/edge-cases.test.js +0 -406
- package/dist/__tests__/integration/workflows.test.js +0 -449
- package/dist/__tests__/performance/benchmarks.test.js +0 -413
- package/dist/__tests__/unit/core/EntityManager.test.js +0 -334
- package/dist/__tests__/unit/core/GraphStorage.test.js +0 -205
- package/dist/__tests__/unit/core/RelationManager.test.js +0 -274
- package/dist/__tests__/unit/features/CompressionManager.test.js +0 -350
- package/dist/__tests__/unit/search/BasicSearch.test.js +0 -311
- package/dist/__tests__/unit/search/BooleanSearch.test.js +0 -432
- package/dist/__tests__/unit/search/FuzzySearch.test.js +0 -448
- package/dist/__tests__/unit/search/RankedSearch.test.js +0 -379
- package/dist/__tests__/unit/utils/levenshtein.test.js +0 -77
- package/dist/core/KnowledgeGraphManager.js +0 -423
- package/dist/features/BackupManager.js +0 -311
- package/dist/features/ExportManager.js +0 -305
- package/dist/features/ImportExportManager.js +0 -50
- package/dist/features/ImportManager.js +0 -328
- package/dist/memory-saved-searches.jsonl +0 -0
- package/dist/memory-tag-aliases.jsonl +0 -0
- package/dist/types/analytics.types.js +0 -6
- package/dist/types/entity.types.js +0 -7
- package/dist/types/import-export.types.js +0 -7
- package/dist/types/search.types.js +0 -7
- package/dist/types/tag.types.js +0 -6
- package/dist/utils/dateUtils.js +0 -89
- package/dist/utils/filterUtils.js +0 -155
- package/dist/utils/levenshtein.js +0 -62
- package/dist/utils/pathUtils.js +0 -115
- package/dist/utils/responseFormatter.js +0 -55
- package/dist/utils/tagUtils.js +0 -107
- package/dist/utils/tfidf.js +0 -90
- package/dist/utils/validationHelper.js +0 -99
- package/dist/utils/validationUtils.js +0 -109
|
@@ -1,432 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BooleanSearch Unit Tests
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
-
import { BooleanSearch } from '../../../search/BooleanSearch.js';
|
|
6
|
-
import { EntityManager } from '../../../core/EntityManager.js';
|
|
7
|
-
import { RelationManager } from '../../../core/RelationManager.js';
|
|
8
|
-
import { GraphStorage } from '../../../core/GraphStorage.js';
|
|
9
|
-
import { promises as fs } from 'fs';
|
|
10
|
-
import { join } from 'path';
|
|
11
|
-
import { tmpdir } from 'os';
|
|
12
|
-
describe('BooleanSearch', () => {
|
|
13
|
-
let storage;
|
|
14
|
-
let booleanSearch;
|
|
15
|
-
let entityManager;
|
|
16
|
-
let relationManager;
|
|
17
|
-
let testDir;
|
|
18
|
-
let testFilePath;
|
|
19
|
-
beforeEach(async () => {
|
|
20
|
-
// Create unique temp directory for each test
|
|
21
|
-
testDir = join(tmpdir(), `boolean-search-test-${Date.now()}-${Math.random()}`);
|
|
22
|
-
await fs.mkdir(testDir, { recursive: true });
|
|
23
|
-
testFilePath = join(testDir, 'test-graph.jsonl');
|
|
24
|
-
storage = new GraphStorage(testFilePath);
|
|
25
|
-
booleanSearch = new BooleanSearch(storage);
|
|
26
|
-
entityManager = new EntityManager(storage);
|
|
27
|
-
relationManager = new RelationManager(storage);
|
|
28
|
-
// Create test data
|
|
29
|
-
await entityManager.createEntities([
|
|
30
|
-
{
|
|
31
|
-
name: 'Alice',
|
|
32
|
-
entityType: 'person',
|
|
33
|
-
observations: ['Software engineer', 'Loves Python programming', 'Works on AI projects'],
|
|
34
|
-
tags: ['engineering', 'python', 'ai'],
|
|
35
|
-
importance: 9,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: 'Bob',
|
|
39
|
-
entityType: 'person',
|
|
40
|
-
observations: ['Product manager', 'Leads roadmap planning'],
|
|
41
|
-
tags: ['product', 'management'],
|
|
42
|
-
importance: 8,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: 'Charlie',
|
|
46
|
-
entityType: 'person',
|
|
47
|
-
observations: ['Designer', 'Creates beautiful UIs', 'Expert in Figma'],
|
|
48
|
-
tags: ['design', 'ui'],
|
|
49
|
-
importance: 7,
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'Project_X',
|
|
53
|
-
entityType: 'project',
|
|
54
|
-
observations: ['Internal automation tool', 'Built with Python'],
|
|
55
|
-
tags: ['engineering', 'automation', 'python'],
|
|
56
|
-
importance: 10,
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: 'Company',
|
|
60
|
-
entityType: 'organization',
|
|
61
|
-
observations: ['Tech startup', 'AI-focused company'],
|
|
62
|
-
tags: ['business', 'ai'],
|
|
63
|
-
importance: 10,
|
|
64
|
-
},
|
|
65
|
-
]);
|
|
66
|
-
await relationManager.createRelations([
|
|
67
|
-
{ from: 'Alice', to: 'Project_X', relationType: 'works_on' },
|
|
68
|
-
{ from: 'Bob', to: 'Project_X', relationType: 'manages' },
|
|
69
|
-
{ from: 'Alice', to: 'Bob', relationType: 'reports_to' },
|
|
70
|
-
]);
|
|
71
|
-
});
|
|
72
|
-
afterEach(async () => {
|
|
73
|
-
// Clean up test files
|
|
74
|
-
try {
|
|
75
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// Ignore cleanup errors
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
describe('AND Operator', () => {
|
|
82
|
-
it('should find entities matching all AND terms', async () => {
|
|
83
|
-
const result = await booleanSearch.booleanSearch('Python AND engineer');
|
|
84
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
85
|
-
const names = result.entities.map(e => e.name);
|
|
86
|
-
expect(names).toContain('Alice');
|
|
87
|
-
});
|
|
88
|
-
it('should support explicit AND operator', async () => {
|
|
89
|
-
const result = await booleanSearch.booleanSearch('person AND manager');
|
|
90
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
91
|
-
const names = result.entities.map(e => e.name);
|
|
92
|
-
expect(names).toContain('Bob');
|
|
93
|
-
});
|
|
94
|
-
it('should support implicit AND (adjacent terms)', async () => {
|
|
95
|
-
const result = await booleanSearch.booleanSearch('engineer python');
|
|
96
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
97
|
-
const names = result.entities.map(e => e.name);
|
|
98
|
-
expect(names).toContain('Alice');
|
|
99
|
-
});
|
|
100
|
-
it('should return empty results when no entities match all terms', async () => {
|
|
101
|
-
const result = await booleanSearch.booleanSearch('engineer AND designer');
|
|
102
|
-
// Alice is engineer, Charlie is designer, but none is both
|
|
103
|
-
expect(result.entities).toHaveLength(0);
|
|
104
|
-
});
|
|
105
|
-
it('should handle multiple AND operators', async () => {
|
|
106
|
-
const result = await booleanSearch.booleanSearch('person AND engineer AND Python');
|
|
107
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
108
|
-
const names = result.entities.map(e => e.name);
|
|
109
|
-
expect(names).toContain('Alice');
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
describe('OR Operator', () => {
|
|
113
|
-
it('should find entities matching any OR term', async () => {
|
|
114
|
-
const result = await booleanSearch.booleanSearch('engineer OR designer');
|
|
115
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
116
|
-
const names = result.entities.map(e => e.name);
|
|
117
|
-
expect(names).toContain('Alice');
|
|
118
|
-
expect(names).toContain('Charlie');
|
|
119
|
-
});
|
|
120
|
-
it('should support multiple OR operators', async () => {
|
|
121
|
-
const result = await booleanSearch.booleanSearch('Alice OR Bob OR Charlie');
|
|
122
|
-
expect(result.entities).toHaveLength(3);
|
|
123
|
-
const names = result.entities.map(e => e.name);
|
|
124
|
-
expect(names).toContain('Alice');
|
|
125
|
-
expect(names).toContain('Bob');
|
|
126
|
-
expect(names).toContain('Charlie');
|
|
127
|
-
});
|
|
128
|
-
it('should handle OR with partial matches', async () => {
|
|
129
|
-
const result = await booleanSearch.booleanSearch('Python OR Figma');
|
|
130
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
131
|
-
const names = result.entities.map(e => e.name);
|
|
132
|
-
// Alice and Project_X have Python, Charlie has Figma
|
|
133
|
-
expect(names).toContain('Alice');
|
|
134
|
-
expect(names).toContain('Charlie');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
describe('NOT Operator', () => {
|
|
138
|
-
it('should exclude entities matching NOT term', async () => {
|
|
139
|
-
const result = await booleanSearch.booleanSearch('person AND NOT manager');
|
|
140
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
141
|
-
const names = result.entities.map(e => e.name);
|
|
142
|
-
expect(names).toContain('Alice');
|
|
143
|
-
expect(names).toContain('Charlie');
|
|
144
|
-
expect(names).not.toContain('Bob'); // Bob is manager
|
|
145
|
-
});
|
|
146
|
-
it('should handle NOT with specific field', async () => {
|
|
147
|
-
const result = await booleanSearch.booleanSearch('NOT type:project');
|
|
148
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
149
|
-
const names = result.entities.map(e => e.name);
|
|
150
|
-
expect(names).not.toContain('Project_X');
|
|
151
|
-
});
|
|
152
|
-
it('should support double NOT', async () => {
|
|
153
|
-
const result = await booleanSearch.booleanSearch('NOT NOT Alice');
|
|
154
|
-
expect(result.entities).toHaveLength(1);
|
|
155
|
-
expect(result.entities[0].name).toBe('Alice');
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
describe('Parentheses Grouping', () => {
|
|
159
|
-
it('should support parentheses for precedence', async () => {
|
|
160
|
-
const result = await booleanSearch.booleanSearch('person AND (Python OR Figma)');
|
|
161
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
162
|
-
const names = result.entities.map(e => e.name);
|
|
163
|
-
expect(names).toContain('Alice'); // person with Python
|
|
164
|
-
expect(names).toContain('Charlie'); // person with Figma
|
|
165
|
-
expect(names).not.toContain('Project_X'); // not a person
|
|
166
|
-
});
|
|
167
|
-
it('should handle nested parentheses', async () => {
|
|
168
|
-
const result = await booleanSearch.booleanSearch('(person OR project) AND (Python OR automation)');
|
|
169
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
170
|
-
const names = result.entities.map(e => e.name);
|
|
171
|
-
expect(names).toContain('Alice'); // person with Python
|
|
172
|
-
expect(names).toContain('Project_X'); // project with Python and automation
|
|
173
|
-
});
|
|
174
|
-
it('should handle NOT with parentheses', async () => {
|
|
175
|
-
const result = await booleanSearch.booleanSearch('person AND NOT (manager OR designer)');
|
|
176
|
-
expect(result.entities).toHaveLength(1);
|
|
177
|
-
expect(result.entities[0].name).toBe('Alice'); // Only person who isn't manager or designer
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
describe('Field-Specific Queries', () => {
|
|
181
|
-
it('should support name field query', async () => {
|
|
182
|
-
const result = await booleanSearch.booleanSearch('name:Alice');
|
|
183
|
-
expect(result.entities).toHaveLength(1);
|
|
184
|
-
expect(result.entities[0].name).toBe('Alice');
|
|
185
|
-
});
|
|
186
|
-
it('should support type field query', async () => {
|
|
187
|
-
const result = await booleanSearch.booleanSearch('type:person');
|
|
188
|
-
expect(result.entities).toHaveLength(3);
|
|
189
|
-
result.entities.forEach(e => {
|
|
190
|
-
expect(e.entityType).toBe('person');
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
it('should support entitytype field query', async () => {
|
|
194
|
-
const result = await booleanSearch.booleanSearch('entitytype:organization');
|
|
195
|
-
expect(result.entities).toHaveLength(1);
|
|
196
|
-
expect(result.entities[0].entityType).toBe('organization');
|
|
197
|
-
});
|
|
198
|
-
it('should support observation field query', async () => {
|
|
199
|
-
const result = await booleanSearch.booleanSearch('observation:engineer');
|
|
200
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
201
|
-
const names = result.entities.map(e => e.name);
|
|
202
|
-
expect(names).toContain('Alice');
|
|
203
|
-
});
|
|
204
|
-
it('should support observations field query', async () => {
|
|
205
|
-
const result = await booleanSearch.booleanSearch('observations:manager');
|
|
206
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
207
|
-
const names = result.entities.map(e => e.name);
|
|
208
|
-
expect(names).toContain('Bob');
|
|
209
|
-
});
|
|
210
|
-
it('should support tag field query', async () => {
|
|
211
|
-
const result = await booleanSearch.booleanSearch('tag:python');
|
|
212
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
213
|
-
result.entities.forEach(e => {
|
|
214
|
-
expect(e.tags).toContain('python');
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
it('should support tags field query', async () => {
|
|
218
|
-
const result = await booleanSearch.booleanSearch('tags:ai');
|
|
219
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
220
|
-
result.entities.forEach(e => {
|
|
221
|
-
expect(e.tags).toContain('ai');
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
it('should combine multiple field queries', async () => {
|
|
225
|
-
const result = await booleanSearch.booleanSearch('type:person AND tag:python');
|
|
226
|
-
expect(result.entities).toHaveLength(1);
|
|
227
|
-
expect(result.entities[0].name).toBe('Alice');
|
|
228
|
-
});
|
|
229
|
-
it('should handle unknown field gracefully', async () => {
|
|
230
|
-
const result = await booleanSearch.booleanSearch('unknownfield:test');
|
|
231
|
-
// Should fallback to general search across all fields
|
|
232
|
-
expect(Array.isArray(result.entities)).toBe(true);
|
|
233
|
-
});
|
|
234
|
-
it('should handle field query with colon in value', async () => {
|
|
235
|
-
await entityManager.createEntities([
|
|
236
|
-
{ name: 'URL_Entity', entityType: 'test', observations: ['https://example.com'] },
|
|
237
|
-
]);
|
|
238
|
-
const result = await booleanSearch.booleanSearch('observation:https');
|
|
239
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
240
|
-
const names = result.entities.map(e => e.name);
|
|
241
|
-
expect(names).toContain('URL_Entity');
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
describe('Tag Filtering', () => {
|
|
245
|
-
it('should filter by single tag', async () => {
|
|
246
|
-
const result = await booleanSearch.booleanSearch('engineer', ['python']);
|
|
247
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
248
|
-
result.entities.forEach(e => {
|
|
249
|
-
expect(e.tags).toContain('python');
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
it('should filter by multiple tags (OR logic)', async () => {
|
|
253
|
-
const result = await booleanSearch.booleanSearch('person', ['python', 'design']);
|
|
254
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
255
|
-
result.entities.forEach(e => {
|
|
256
|
-
const hasPython = e.tags?.includes('python');
|
|
257
|
-
const hasDesign = e.tags?.includes('design');
|
|
258
|
-
expect(hasPython || hasDesign).toBe(true);
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
it('should combine boolean query with tag filter', async () => {
|
|
262
|
-
const result = await booleanSearch.booleanSearch('person OR project', ['python']);
|
|
263
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
264
|
-
const names = result.entities.map(e => e.name);
|
|
265
|
-
expect(names).toContain('Alice');
|
|
266
|
-
expect(names).toContain('Project_X');
|
|
267
|
-
});
|
|
268
|
-
it('should exclude entities without matching tags', async () => {
|
|
269
|
-
const result = await booleanSearch.booleanSearch('person', ['nonexistent']);
|
|
270
|
-
expect(result.entities).toHaveLength(0);
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
describe('Importance Filtering', () => {
|
|
274
|
-
it('should filter by minimum importance', async () => {
|
|
275
|
-
const result = await booleanSearch.booleanSearch('person OR project', undefined, 9);
|
|
276
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
277
|
-
result.entities.forEach(e => {
|
|
278
|
-
expect(e.importance).toBeGreaterThanOrEqual(9);
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
it('should filter by maximum importance', async () => {
|
|
282
|
-
const result = await booleanSearch.booleanSearch('person', undefined, undefined, 8);
|
|
283
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
284
|
-
result.entities.forEach(e => {
|
|
285
|
-
expect(e.importance).toBeLessThanOrEqual(8);
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
it('should filter by importance range', async () => {
|
|
289
|
-
const result = await booleanSearch.booleanSearch('person OR project', undefined, 8, 9);
|
|
290
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
291
|
-
result.entities.forEach(e => {
|
|
292
|
-
expect(e.importance).toBeGreaterThanOrEqual(8);
|
|
293
|
-
expect(e.importance).toBeLessThanOrEqual(9);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
it('should combine boolean query with importance filter', async () => {
|
|
297
|
-
const result = await booleanSearch.booleanSearch('Python', undefined, 10);
|
|
298
|
-
expect(result.entities).toHaveLength(1);
|
|
299
|
-
expect(result.entities[0].name).toBe('Project_X');
|
|
300
|
-
// Company doesn't have Python, Alice has importance 9
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
describe('Relations', () => {
|
|
304
|
-
it('should include relations between matched entities', async () => {
|
|
305
|
-
const result = await booleanSearch.booleanSearch('Alice OR Bob');
|
|
306
|
-
expect(result.entities).toHaveLength(2);
|
|
307
|
-
expect(result.relations.length).toBeGreaterThan(0);
|
|
308
|
-
expect(result.relations.some(r => r.from === 'Alice' && r.to === 'Bob')).toBe(true);
|
|
309
|
-
});
|
|
310
|
-
it('should exclude relations to non-matched entities', async () => {
|
|
311
|
-
const result = await booleanSearch.booleanSearch('name:Alice');
|
|
312
|
-
expect(result.entities).toHaveLength(1);
|
|
313
|
-
// Alice has relations to Bob and Project_X, but they're not in the result
|
|
314
|
-
expect(result.relations).toHaveLength(0);
|
|
315
|
-
});
|
|
316
|
-
it('should include all relations in matched subgraph', async () => {
|
|
317
|
-
const result = await booleanSearch.booleanSearch('Alice OR Bob OR Project_X');
|
|
318
|
-
expect(result.entities).toHaveLength(3);
|
|
319
|
-
expect(result.relations.length).toBeGreaterThan(0);
|
|
320
|
-
expect(result.relations.some(r => r.from === 'Alice' && r.to === 'Project_X')).toBe(true);
|
|
321
|
-
expect(result.relations.some(r => r.from === 'Bob' && r.to === 'Project_X')).toBe(true);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
describe('Case Sensitivity', () => {
|
|
325
|
-
it('should be case-insensitive for terms', async () => {
|
|
326
|
-
const result = await booleanSearch.booleanSearch('ALICE');
|
|
327
|
-
expect(result.entities).toHaveLength(1);
|
|
328
|
-
expect(result.entities[0].name).toBe('Alice');
|
|
329
|
-
});
|
|
330
|
-
it('should be case-insensitive for field queries', async () => {
|
|
331
|
-
const result = await booleanSearch.booleanSearch('TYPE:PERSON');
|
|
332
|
-
expect(result.entities).toHaveLength(3);
|
|
333
|
-
});
|
|
334
|
-
it('should be case-insensitive for operators', async () => {
|
|
335
|
-
const result = await booleanSearch.booleanSearch('alice or bob');
|
|
336
|
-
expect(result.entities).toHaveLength(2);
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
describe('Quoted Strings', () => {
|
|
340
|
-
it('should support quoted strings for multi-word searches', async () => {
|
|
341
|
-
const result = await booleanSearch.booleanSearch('"Software engineer"');
|
|
342
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
343
|
-
const names = result.entities.map(e => e.name);
|
|
344
|
-
expect(names).toContain('Alice');
|
|
345
|
-
});
|
|
346
|
-
it('should combine quoted strings with operators', async () => {
|
|
347
|
-
const result = await booleanSearch.booleanSearch('"Product manager" OR "Software engineer"');
|
|
348
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
349
|
-
const names = result.entities.map(e => e.name);
|
|
350
|
-
expect(names).toContain('Alice');
|
|
351
|
-
expect(names).toContain('Bob');
|
|
352
|
-
});
|
|
353
|
-
it('should support quoted strings in field queries', async () => {
|
|
354
|
-
const result = await booleanSearch.booleanSearch('observation:"beautiful UIs"');
|
|
355
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
356
|
-
const names = result.entities.map(e => e.name);
|
|
357
|
-
expect(names).toContain('Charlie');
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
describe('Edge Cases', () => {
|
|
361
|
-
it('should handle single term query', async () => {
|
|
362
|
-
const result = await booleanSearch.booleanSearch('Alice');
|
|
363
|
-
expect(result.entities).toHaveLength(1);
|
|
364
|
-
expect(result.entities[0].name).toBe('Alice');
|
|
365
|
-
});
|
|
366
|
-
it('should handle entities with empty observations', async () => {
|
|
367
|
-
await entityManager.createEntities([
|
|
368
|
-
{ name: 'EmptyObs', entityType: 'test', observations: [] },
|
|
369
|
-
]);
|
|
370
|
-
const result = await booleanSearch.booleanSearch('type:test');
|
|
371
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
372
|
-
const names = result.entities.map(e => e.name);
|
|
373
|
-
expect(names).toContain('EmptyObs');
|
|
374
|
-
});
|
|
375
|
-
it('should handle entities without tags', async () => {
|
|
376
|
-
await entityManager.createEntities([
|
|
377
|
-
{ name: 'NoTags', entityType: 'test', observations: ['Test'] },
|
|
378
|
-
]);
|
|
379
|
-
const result = await booleanSearch.booleanSearch('test');
|
|
380
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
381
|
-
});
|
|
382
|
-
it('should handle entities without importance', async () => {
|
|
383
|
-
await entityManager.createEntities([
|
|
384
|
-
{ name: 'NoImportance', entityType: 'test', observations: ['Test'] },
|
|
385
|
-
]);
|
|
386
|
-
const result = await booleanSearch.booleanSearch('test', undefined, 5);
|
|
387
|
-
const names = result.entities.map(e => e.name);
|
|
388
|
-
expect(names).not.toContain('NoImportance');
|
|
389
|
-
});
|
|
390
|
-
it('should throw error on malformed query (unclosed parenthesis)', async () => {
|
|
391
|
-
await expect(booleanSearch.booleanSearch('(person AND manager')).rejects.toThrow();
|
|
392
|
-
});
|
|
393
|
-
it('should throw error on malformed query (unexpected token)', async () => {
|
|
394
|
-
await expect(booleanSearch.booleanSearch('person) AND manager')).rejects.toThrow();
|
|
395
|
-
});
|
|
396
|
-
it('should throw error on empty query', async () => {
|
|
397
|
-
await expect(booleanSearch.booleanSearch('')).rejects.toThrow();
|
|
398
|
-
});
|
|
399
|
-
it('should handle complex nested query', async () => {
|
|
400
|
-
const result = await booleanSearch.booleanSearch('(type:person AND (tag:python OR tag:design)) OR (type:project AND tag:automation)');
|
|
401
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(2);
|
|
402
|
-
const names = result.entities.map(e => e.name);
|
|
403
|
-
// Alice (person with python tag), Charlie (person with design tag), Project_X (project with automation tag)
|
|
404
|
-
expect(names).toContain('Alice');
|
|
405
|
-
expect(names).toContain('Charlie');
|
|
406
|
-
expect(names).toContain('Project_X');
|
|
407
|
-
});
|
|
408
|
-
it('should handle whitespace in query', async () => {
|
|
409
|
-
const result = await booleanSearch.booleanSearch(' person AND engineer ');
|
|
410
|
-
expect(result.entities.length).toBeGreaterThanOrEqual(1);
|
|
411
|
-
const names = result.entities.map(e => e.name);
|
|
412
|
-
expect(names).toContain('Alice');
|
|
413
|
-
});
|
|
414
|
-
});
|
|
415
|
-
describe('Return Value Structure', () => {
|
|
416
|
-
it('should return KnowledgeGraph with entities and relations', async () => {
|
|
417
|
-
const result = await booleanSearch.booleanSearch('person');
|
|
418
|
-
expect(result).toHaveProperty('entities');
|
|
419
|
-
expect(result).toHaveProperty('relations');
|
|
420
|
-
expect(Array.isArray(result.entities)).toBe(true);
|
|
421
|
-
expect(Array.isArray(result.relations)).toBe(true);
|
|
422
|
-
});
|
|
423
|
-
it('should return complete entity objects', async () => {
|
|
424
|
-
const result = await booleanSearch.booleanSearch('Alice');
|
|
425
|
-
expect(result.entities[0]).toHaveProperty('name');
|
|
426
|
-
expect(result.entities[0]).toHaveProperty('entityType');
|
|
427
|
-
expect(result.entities[0]).toHaveProperty('observations');
|
|
428
|
-
expect(result.entities[0]).toHaveProperty('tags');
|
|
429
|
-
expect(result.entities[0]).toHaveProperty('importance');
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
});
|