@henrychong-ai/mcp-neo4j-knowledge-graph 2.7.1 → 2.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/README.md +86 -0
- package/dist/KnowledgeGraphManager.d.ts +40 -0
- package/dist/KnowledgeGraphManager.js +101 -0
- package/dist/KnowledgeGraphManager.js.map +1 -1
- package/dist/cli/report-oversized.d.ts +20 -0
- package/dist/cli/report-oversized.js +158 -0
- package/dist/cli/report-oversized.js.map +1 -0
- package/dist/config/entitySize.d.ts +44 -0
- package/dist/config/entitySize.js +77 -0
- package/dist/config/entitySize.js.map +1 -0
- package/dist/maintenance/EntitySizeService.d.ts +103 -0
- package/dist/maintenance/EntitySizeService.js +143 -0
- package/dist/maintenance/EntitySizeService.js.map +1 -0
- package/dist/server/handlers/callToolHandler.js +22 -1
- package/dist/server/handlers/callToolHandler.js.map +1 -1
- package/dist/server/handlers/listToolsHandler.js +21 -0
- package/dist/server/handlers/listToolsHandler.js.map +1 -1
- package/dist/server/handlers/toolHandlers/addObservationsBatch.js +5 -1
- package/dist/server/handlers/toolHandlers/addObservationsBatch.js.map +1 -1
- package/dist/server/handlers/toolHandlers/createEntitiesBatch.js +5 -1
- package/dist/server/handlers/toolHandlers/createEntitiesBatch.js.map +1 -1
- package/dist/server/handlers/toolHandlers/updateEntitiesBatch.js +5 -1
- package/dist/server/handlers/toolHandlers/updateEntitiesBatch.js.map +1 -1
- package/dist/server/handlers/toolHandlers/writeSizeWarnings.d.ts +45 -0
- package/dist/server/handlers/toolHandlers/writeSizeWarnings.js +106 -0
- package/dist/server/handlers/toolHandlers/writeSizeWarnings.js.map +1 -0
- package/dist/storage/StorageProvider.d.ts +27 -0
- package/dist/storage/StorageProvider.js.map +1 -1
- package/dist/storage/neo4j/Neo4jStorageProvider.d.ts +14 -1
- package/dist/storage/neo4j/Neo4jStorageProvider.js +70 -0
- package/dist/storage/neo4j/Neo4jStorageProvider.js.map +1 -1
- package/example.env +14 -1
- package/package.json +6 -3
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Updates multiple entities in a single optimized batch operation.
|
|
5
5
|
* Provides 10-50x performance improvement over individual updates.
|
|
6
6
|
*/
|
|
7
|
+
import { attachWriteWarnings, collectWriteSizeWarnings, extractWrittenNames, } from './writeSizeWarnings.js';
|
|
7
8
|
/**
|
|
8
9
|
* Handle update_entities_batch tool calls
|
|
9
10
|
*
|
|
@@ -15,11 +16,14 @@ export async function handleUpdateEntitiesBatch(args,
|
|
|
15
16
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
17
|
knowledgeGraphManager) {
|
|
17
18
|
const result = await knowledgeGraphManager.updateEntitiesBatch(args.updates, args.config);
|
|
19
|
+
// Additive, fail-open: flag any entity this write pushed near the open_nodes cap.
|
|
20
|
+
const warnings = await collectWriteSizeWarnings(knowledgeGraphManager, extractWrittenNames(args));
|
|
21
|
+
const payload = attachWriteWarnings(result, warnings);
|
|
18
22
|
return {
|
|
19
23
|
content: [
|
|
20
24
|
{
|
|
21
25
|
type: 'text',
|
|
22
|
-
text: JSON.stringify(
|
|
26
|
+
text: JSON.stringify(payload, null, 2),
|
|
23
27
|
},
|
|
24
28
|
],
|
|
25
29
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateEntitiesBatch.js","sourceRoot":"","sources":["../../../../src/server/handlers/toolHandlers/updateEntitiesBatch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,IAA6B;AAC7B,8DAA8D;AAC9D,qBAA0B;IAE1B,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAE1F,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"updateEntitiesBatch.js","sourceRoot":"","sources":["../../../../src/server/handlers/toolHandlers/updateEntitiesBatch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,IAA6B;AAC7B,8DAA8D;AAC9D,qBAA0B;IAE1B,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAE1F,kFAAkF;IAClF,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAAC,qBAAqB,EAAE,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;IAClG,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEtD,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;aACvC;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helper: non-fatal entity-size warnings for write tools.
|
|
3
|
+
*
|
|
4
|
+
* After a batch write, the touched entities are sized with EntitySizeService and
|
|
5
|
+
* any that cross the WARN/CRITICAL thresholds are returned as an additive
|
|
6
|
+
* `warnings[]` field on the tool result. This is the earliest possible signal —
|
|
7
|
+
* it names the offending entity at the moment it grows.
|
|
8
|
+
*
|
|
9
|
+
* STRICTLY fail-open: a disabled feature, a sizing error, or a hydration failure
|
|
10
|
+
* yields an empty list and never disrupts or fails the write.
|
|
11
|
+
*/
|
|
12
|
+
/** A single non-fatal size warning for a written entity. */
|
|
13
|
+
export interface WriteSizeWarning {
|
|
14
|
+
name: string;
|
|
15
|
+
estTokens: number;
|
|
16
|
+
ratio: number;
|
|
17
|
+
state: 'WARN' | 'CRITICAL';
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extract candidate entity names from a write tool's arguments. Handles the
|
|
22
|
+
* three write shapes: add_observations_batch (observations[].entityName),
|
|
23
|
+
* update_entities_batch (updates[].name), create_entities_batch (entities[].name).
|
|
24
|
+
*
|
|
25
|
+
* @param args Tool arguments
|
|
26
|
+
* @returns Candidate entity names (possibly with duplicates)
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractWrittenNames(args: Record<string, unknown>): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Compute non-fatal size warnings for the entities just written.
|
|
31
|
+
*
|
|
32
|
+
* @param knowledgeGraphManager Knowledge graph manager instance
|
|
33
|
+
* @param names Names of the entities that were written
|
|
34
|
+
* @returns WARN/CRITICAL warnings (empty when disabled or on any error)
|
|
35
|
+
*/
|
|
36
|
+
export declare function collectWriteSizeWarnings(knowledgeGraphManager: any, names: string[]): Promise<WriteSizeWarning[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Attach size warnings to a write result without mutating existing fields.
|
|
39
|
+
* Returns the original result unchanged when there are no warnings.
|
|
40
|
+
*
|
|
41
|
+
* @param result The batch write result
|
|
42
|
+
* @param warnings Size warnings to attach
|
|
43
|
+
* @returns The result, with an additive `warnings` field when applicable
|
|
44
|
+
*/
|
|
45
|
+
export declare function attachWriteWarnings(result: unknown, warnings: WriteSizeWarning[]): unknown;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helper: non-fatal entity-size warnings for write tools.
|
|
3
|
+
*
|
|
4
|
+
* After a batch write, the touched entities are sized with EntitySizeService and
|
|
5
|
+
* any that cross the WARN/CRITICAL thresholds are returned as an additive
|
|
6
|
+
* `warnings[]` field on the tool result. This is the earliest possible signal —
|
|
7
|
+
* it names the offending entity at the moment it grows.
|
|
8
|
+
*
|
|
9
|
+
* STRICTLY fail-open: a disabled feature, a sizing error, or a hydration failure
|
|
10
|
+
* yields an empty list and never disrupts or fails the write.
|
|
11
|
+
*/
|
|
12
|
+
import { getEntitySizeConfig } from '../../../config/entitySize.js';
|
|
13
|
+
import { estimateEntitySize } from '../../../maintenance/EntitySizeService.js';
|
|
14
|
+
import { logger } from '../../../utils/logger.js';
|
|
15
|
+
/**
|
|
16
|
+
* Extract candidate entity names from a write tool's arguments. Handles the
|
|
17
|
+
* three write shapes: add_observations_batch (observations[].entityName),
|
|
18
|
+
* update_entities_batch (updates[].name), create_entities_batch (entities[].name).
|
|
19
|
+
*
|
|
20
|
+
* @param args Tool arguments
|
|
21
|
+
* @returns Candidate entity names (possibly with duplicates)
|
|
22
|
+
*/
|
|
23
|
+
export function extractWrittenNames(args) {
|
|
24
|
+
const names = [];
|
|
25
|
+
const push = (item) => {
|
|
26
|
+
if (!item || typeof item !== 'object') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const record = item;
|
|
30
|
+
if (typeof record.name === 'string') {
|
|
31
|
+
names.push(record.name);
|
|
32
|
+
}
|
|
33
|
+
if (typeof record.entityName === 'string') {
|
|
34
|
+
names.push(record.entityName);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
for (const key of ['observations', 'updates', 'entities']) {
|
|
38
|
+
const arr = args[key];
|
|
39
|
+
if (Array.isArray(arr)) {
|
|
40
|
+
for (const item of arr) {
|
|
41
|
+
push(item);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return names;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Compute non-fatal size warnings for the entities just written.
|
|
49
|
+
*
|
|
50
|
+
* @param knowledgeGraphManager Knowledge graph manager instance
|
|
51
|
+
* @param names Names of the entities that were written
|
|
52
|
+
* @returns WARN/CRITICAL warnings (empty when disabled or on any error)
|
|
53
|
+
*/
|
|
54
|
+
export async function collectWriteSizeWarnings(
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
knowledgeGraphManager, names) {
|
|
57
|
+
try {
|
|
58
|
+
const cfg = getEntitySizeConfig();
|
|
59
|
+
if (!cfg.warnOnWrite) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const unique = [...new Set(names.filter(n => typeof n === 'string' && n.length > 0))];
|
|
63
|
+
if (unique.length === 0) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const graph = await knowledgeGraphManager.openNodes(unique);
|
|
67
|
+
const entities = graph?.entities ?? [];
|
|
68
|
+
const warnings = [];
|
|
69
|
+
for (const entity of entities) {
|
|
70
|
+
const report = estimateEntitySize(entity, cfg);
|
|
71
|
+
if (report.state === 'OK') {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const pct = Math.round(report.ratio * 100);
|
|
75
|
+
warnings.push({
|
|
76
|
+
name: report.name,
|
|
77
|
+
estTokens: report.estTokens,
|
|
78
|
+
ratio: Number(report.ratio.toFixed(3)),
|
|
79
|
+
state: report.state,
|
|
80
|
+
message: report.state === 'CRITICAL'
|
|
81
|
+
? `Entity "${report.name}" is ~${report.estTokens} tokens, at/above the ~${cfg.maxTokens}-token open_nodes cap — split it into sibling entities now; it may already be unretrievable whole.`
|
|
82
|
+
: `Entity "${report.name}" is ~${report.estTokens} tokens (${pct}% of the ~${cfg.maxTokens}-token open_nodes cap) — consider splitting it soon.`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return warnings;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
logger.warn('collectWriteSizeWarnings failed (non-fatal)', error);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Attach size warnings to a write result without mutating existing fields.
|
|
94
|
+
* Returns the original result unchanged when there are no warnings.
|
|
95
|
+
*
|
|
96
|
+
* @param result The batch write result
|
|
97
|
+
* @param warnings Size warnings to attach
|
|
98
|
+
* @returns The result, with an additive `warnings` field when applicable
|
|
99
|
+
*/
|
|
100
|
+
export function attachWriteWarnings(result, warnings) {
|
|
101
|
+
if (warnings.length === 0 || !result || typeof result !== 'object') {
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
return { ...result, warnings };
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=writeSizeWarnings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writeSizeWarnings.js","sourceRoot":"","sources":["../../../../src/server/handlers/toolHandlers/writeSizeWarnings.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,6 +28,23 @@ export interface SearchOptions {
|
|
|
28
28
|
*/
|
|
29
29
|
includeNullDomain?: boolean;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Compact, cap-safe projection of one entity's approximate serialized size.
|
|
33
|
+
* Returned by {@link StorageProvider.scanEntitySizes}; never carries full
|
|
34
|
+
* observation text, so the scan itself can never breach the MCP output cap.
|
|
35
|
+
*/
|
|
36
|
+
export interface EntitySizeScanRow {
|
|
37
|
+
name: string;
|
|
38
|
+
entityType: string;
|
|
39
|
+
/** Approximate serialized characters (observations + structural overhead + relations term). */
|
|
40
|
+
approxChars: number;
|
|
41
|
+
/** Approximate characters contributed by observations alone. */
|
|
42
|
+
obsChars: number;
|
|
43
|
+
/** Number of observations on the entity. */
|
|
44
|
+
obsCount: number;
|
|
45
|
+
/** Number of current relations attached to the entity. */
|
|
46
|
+
relCount: number;
|
|
47
|
+
}
|
|
31
48
|
/**
|
|
32
49
|
* Interface for storage providers that can load and save knowledge graphs
|
|
33
50
|
*/
|
|
@@ -56,6 +73,16 @@ export interface StorageProvider {
|
|
|
56
73
|
* @returns Promise resolving to a KnowledgeGraph containing the specified nodes
|
|
57
74
|
*/
|
|
58
75
|
openNodes(names: string[]): Promise<KnowledgeGraph>;
|
|
76
|
+
/**
|
|
77
|
+
* Scan current entities ranked by approximate serialized size, largest first.
|
|
78
|
+
* Computes size in the storage layer and returns only a compact projection
|
|
79
|
+
* (never full entities), so the scan is immune to the MCP output cap it
|
|
80
|
+
* polices. Optional — providers that cannot scan efficiently may omit it, and
|
|
81
|
+
* callers fall back to an in-memory pass.
|
|
82
|
+
* @param limit Maximum number of (largest) entities to return
|
|
83
|
+
* @returns Promise resolving to ranked size rows, largest first
|
|
84
|
+
*/
|
|
85
|
+
scanEntitySizes?(limit: number): Promise<EntitySizeScanRow[]>;
|
|
59
86
|
/**
|
|
60
87
|
* Create new entities in the knowledge graph
|
|
61
88
|
* @param entities Array of entities to create
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageProvider.js","sourceRoot":"","sources":["../../src/storage/StorageProvider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StorageProvider.js","sourceRoot":"","sources":["../../src/storage/StorageProvider.ts"],"names":[],"mappings":"AAkRA,2EAA2E;AAC3E,6EAA6E;AAC7E,2DAA2D;AAC3D,MAAM,KAAW,eAAe,CAK/B;AALD,WAAiB,eAAe;IAC9B,8DAA8D;IAC9D,SAAgB,iBAAiB,CAAC,GAAQ;QACxC,OAAO,wBAAwB,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IAFe,iCAAiB,oBAEhC,CAAA;AACH,CAAC,EALgB,eAAe,KAAf,eAAe,QAK/B;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,kFAAkF;IAClF,qDAAqD;IACrD,8DAA8D;IAC9D,iBAAiB,CAAC,GAAQ;QACxB,MAAM,kBAAkB,GACtB,GAAG;YACH,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU;YACnC,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU;YACnC,OAAO,GAAG,CAAC,WAAW,KAAK,UAAU;YACrC,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU;YACnC,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU;YACxC,OAAO,GAAG,CAAC,eAAe,KAAK,UAAU;YACzC,OAAO,GAAG,CAAC,eAAe,KAAK,UAAU;YACzC,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU;YACxC,OAAO,GAAG,CAAC,kBAAkB,KAAK,UAAU;YAC5C,OAAO,GAAG,CAAC,eAAe,KAAK,UAAU;YACzC,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,CAAC;QAEtC,6DAA6D;QAC7D,MAAM,oBAAoB,GACxB,CAAC,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,UAAU,CAAC;YAC3D,CAAC,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,CAAC;YACjE,CAAC,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,GAAG,CAAC,gBAAgB,KAAK,UAAU,CAAC;YACrE,CAAC,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,GAAG,CAAC,kBAAkB,KAAK,UAAU,CAAC;YACzE,CAAC,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,CAAC;YACjE,CAAC,CAAC,GAAG,CAAC,eAAe,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,UAAU,CAAC;YACnE,CAAC,CAAC,GAAG,CAAC,qBAAqB,IAAI,OAAO,GAAG,CAAC,qBAAqB,KAAK,UAAU,CAAC;YAC/E,CAAC,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,GAAG,CAAC,mBAAmB,KAAK,UAAU,CAAC;YAC3E,CAAC,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU,CAAC,CAAC;QAEpE,OAAO,kBAAkB,IAAI,oBAAoB,CAAC;IACpD,CAAC;CACF,CAAC"}
|
|
@@ -3,7 +3,7 @@ import { type HybridSearchConfig } from '../../retrieval/index.js';
|
|
|
3
3
|
import type { BatchConfig, BatchResult, ObservationBatch, EntityUpdate } from '../../types/batch-operations.js';
|
|
4
4
|
import type { EntityEmbedding, SemanticSearchOptions } from '../../types/entity-embedding.js';
|
|
5
5
|
import type { Relation } from '../../types/relation.js';
|
|
6
|
-
import type { StorageProvider, SearchOptions } from '../StorageProvider.js';
|
|
6
|
+
import type { StorageProvider, SearchOptions, EntitySizeScanRow } from '../StorageProvider.js';
|
|
7
7
|
import { type Neo4jConfig } from './Neo4jConfig.js';
|
|
8
8
|
import { Neo4jConnectionManager } from './Neo4jConnectionManager.js';
|
|
9
9
|
/**
|
|
@@ -141,6 +141,19 @@ export declare class Neo4jStorageProvider implements StorageProvider {
|
|
|
141
141
|
* @param names Array of node names to open
|
|
142
142
|
*/
|
|
143
143
|
openNodes(names: string[]): Promise<KnowledgeGraph>;
|
|
144
|
+
/**
|
|
145
|
+
* Scan current entities ranked by approximate serialized size, largest first.
|
|
146
|
+
*
|
|
147
|
+
* Size is computed entirely in Cypher and only a compact projection is
|
|
148
|
+
* returned (name, type, char/observation/relation counts) — never full
|
|
149
|
+
* entities — so this scan can never itself breach the MCP output cap it
|
|
150
|
+
* exists to police. Observations may be stored as a JSON string or a list;
|
|
151
|
+
* both forms are handled via valueType() (Neo4j 5.13+).
|
|
152
|
+
*
|
|
153
|
+
* @param limit Maximum number of (largest) entities to return
|
|
154
|
+
* @returns Ranked size rows, largest approxChars first
|
|
155
|
+
*/
|
|
156
|
+
scanEntitySizes(limit: number): Promise<EntitySizeScanRow[]>;
|
|
144
157
|
/**
|
|
145
158
|
* Create new entities in the knowledge graph
|
|
146
159
|
* @param entities Array of entities to create
|
|
@@ -596,6 +596,76 @@ export class Neo4jStorageProvider {
|
|
|
596
596
|
throw error;
|
|
597
597
|
}
|
|
598
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* Scan current entities ranked by approximate serialized size, largest first.
|
|
601
|
+
*
|
|
602
|
+
* Size is computed entirely in Cypher and only a compact projection is
|
|
603
|
+
* returned (name, type, char/observation/relation counts) — never full
|
|
604
|
+
* entities — so this scan can never itself breach the MCP output cap it
|
|
605
|
+
* exists to police. Observations may be stored as a JSON string or a list;
|
|
606
|
+
* both forms are handled via valueType() (Neo4j 5.13+).
|
|
607
|
+
*
|
|
608
|
+
* @param limit Maximum number of (largest) entities to return
|
|
609
|
+
* @returns Ranked size rows, largest approxChars first
|
|
610
|
+
*/
|
|
611
|
+
async scanEntitySizes(limit) {
|
|
612
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 50;
|
|
613
|
+
// obsChars: characters contributed by observations, deliberately biased HIGH
|
|
614
|
+
// so a many-short-observation entity (whose real open_nodes JSON carries
|
|
615
|
+
// large per-line indentation overhead) is not ranked below the top-N and
|
|
616
|
+
// missed. String form => its JSON length + ~10 chars/element for the
|
|
617
|
+
// pretty-print indentation the raw string omits; list form => sum of
|
|
618
|
+
// element lengths + ~12 chars/element (8-space indent + quotes + comma).
|
|
619
|
+
// coalesce guards a null element (which would null the whole rank, and
|
|
620
|
+
// Neo4j sorts nulls FIRST, spuriously promoting it). obsCount is the
|
|
621
|
+
// element count for both forms (string form approximated via split).
|
|
622
|
+
// approxChars: obsChars + a fixed structural/metadata overhead + a small
|
|
623
|
+
// per-relation term. Only used for RANKING; precise sizing is refined
|
|
624
|
+
// against the real entity for the returned top-N.
|
|
625
|
+
const query = `
|
|
626
|
+
MATCH (e:Entity)
|
|
627
|
+
WHERE e.validTo IS NULL
|
|
628
|
+
WITH e,
|
|
629
|
+
CASE
|
|
630
|
+
WHEN e.observations IS NULL THEN 0
|
|
631
|
+
WHEN valueType(e.observations) STARTS WITH 'STRING'
|
|
632
|
+
THEN size(e.observations) + (size(split(e.observations, '","')) * 10)
|
|
633
|
+
WHEN valueType(e.observations) STARTS WITH 'LIST'
|
|
634
|
+
THEN reduce(s = 0, o IN e.observations | s + size(coalesce(toString(o), '')) + 12)
|
|
635
|
+
ELSE 0
|
|
636
|
+
END AS obsChars,
|
|
637
|
+
CASE
|
|
638
|
+
WHEN e.observations IS NULL THEN 0
|
|
639
|
+
WHEN valueType(e.observations) STARTS WITH 'LIST' THEN size(e.observations)
|
|
640
|
+
WHEN valueType(e.observations) STARTS WITH 'STRING' THEN size(split(e.observations, '","'))
|
|
641
|
+
ELSE 0
|
|
642
|
+
END AS obsCount
|
|
643
|
+
OPTIONAL MATCH (e)-[r:RELATES_TO]-(:Entity)
|
|
644
|
+
WHERE r.validTo IS NULL
|
|
645
|
+
WITH e, obsChars, obsCount, count(DISTINCT r) AS relCount
|
|
646
|
+
RETURN
|
|
647
|
+
e.name AS name,
|
|
648
|
+
e.entityType AS entityType,
|
|
649
|
+
obsChars AS obsChars,
|
|
650
|
+
obsCount AS obsCount,
|
|
651
|
+
relCount AS relCount,
|
|
652
|
+
(obsChars + 200 + relCount * 8) AS approxChars
|
|
653
|
+
ORDER BY approxChars DESC
|
|
654
|
+
LIMIT toInteger($limit)
|
|
655
|
+
`;
|
|
656
|
+
const result = await this.connectionManager.executeQuery(query, { limit: safeLimit });
|
|
657
|
+
return result.records.map(record => {
|
|
658
|
+
const toNum = (value) => Number(this.convertNeo4jInt(value) ?? 0);
|
|
659
|
+
return {
|
|
660
|
+
name: record.get('name'),
|
|
661
|
+
entityType: record.get('entityType') ?? '',
|
|
662
|
+
approxChars: toNum(record.get('approxChars')),
|
|
663
|
+
obsChars: toNum(record.get('obsChars')),
|
|
664
|
+
obsCount: toNum(record.get('obsCount')),
|
|
665
|
+
relCount: toNum(record.get('relCount')),
|
|
666
|
+
};
|
|
667
|
+
});
|
|
668
|
+
}
|
|
599
669
|
/**
|
|
600
670
|
* Create new entities in the knowledge graph
|
|
601
671
|
* @param entities Array of entities to create
|