@kiyeonjeon21/datacontext 0.3.2 → 0.4.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/dist/adapters/sqlite.d.ts.map +1 -1
- package/dist/adapters/sqlite.js +13 -0
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +115 -0
- package/dist/api/server.js.map +1 -1
- package/dist/cli/index.js +58 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/core/context-service.d.ts +63 -0
- package/dist/core/context-service.d.ts.map +1 -1
- package/dist/core/context-service.js +66 -0
- package/dist/core/context-service.js.map +1 -1
- package/dist/core/harvester.d.ts +57 -5
- package/dist/core/harvester.d.ts.map +1 -1
- package/dist/core/harvester.js +86 -6
- package/dist/core/harvester.js.map +1 -1
- package/dist/core/types.d.ts +21 -5
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/store.d.ts +186 -3
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +389 -5
- package/dist/knowledge/store.js.map +1 -1
- package/dist/knowledge/types.d.ts +252 -4
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/knowledge/types.js +138 -1
- package/dist/knowledge/types.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +231 -3
- package/dist/mcp/tools.js.map +1 -1
- package/docs/KNOWLEDGE_GRAPH.md +540 -0
- package/docs/KNOWLEDGE_TYPES.md +261 -0
- package/docs/MULTI_DB_ARCHITECTURE.md +319 -0
- package/package.json +1 -1
- package/scripts/create-sqlite-testdb.sh +75 -0
- package/scripts/test-databases.sh +324 -0
- package/sqlite:./test-sqlite.db +0 -0
- package/src/adapters/sqlite.ts +16 -0
- package/src/api/server.ts +134 -0
- package/src/cli/index.ts +57 -16
- package/src/core/context-service.ts +70 -0
- package/src/core/harvester.ts +120 -8
- package/src/core/types.ts +21 -5
- package/src/index.ts +19 -1
- package/src/knowledge/store.ts +480 -6
- package/src/knowledge/types.ts +321 -4
- package/src/mcp/tools.ts +273 -3
- package/test-sqlite.db +0 -0
- package/tests/knowledge-store.test.ts +130 -0
package/src/core/harvester.ts
CHANGED
|
@@ -1,22 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-Harvester
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Automatically collects metadata from database and syncs to Knowledge Store.
|
|
4
5
|
* Platform-agnostic: works with MCP, REST API, SDK, etc.
|
|
6
|
+
*
|
|
7
|
+
* ## What Gets Harvested
|
|
8
|
+
*
|
|
9
|
+
* - **Table/Column Comments**: COMMENT ON TABLE/COLUMN statements
|
|
10
|
+
* - **Indexes**: Index definitions and types
|
|
11
|
+
* - **Foreign Keys**: FK constraints → TableRelationship edges
|
|
12
|
+
* - **Row Counts**: Estimated table sizes
|
|
13
|
+
*
|
|
14
|
+
* ## Relationship Harvesting (NEW in v2.0)
|
|
15
|
+
*
|
|
16
|
+
* Foreign keys are now automatically converted to TableRelationship
|
|
17
|
+
* entries in the Knowledge Graph:
|
|
18
|
+
*
|
|
19
|
+
* ```
|
|
20
|
+
* FK: orders.user_id → users.id
|
|
21
|
+
* ↓
|
|
22
|
+
* TableRelationship {
|
|
23
|
+
* from: { table: 'orders', schema: 'public' },
|
|
24
|
+
* to: { table: 'users', schema: 'public' },
|
|
25
|
+
* joinCondition: 'orders.user_id = users.id',
|
|
26
|
+
* relationshipType: 'foreign_key',
|
|
27
|
+
* cardinality: 'many-to-one'
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @module core/harvester
|
|
5
32
|
*/
|
|
6
33
|
|
|
7
34
|
import type { DatabaseAdapter } from '../adapters/base.js';
|
|
8
35
|
import type { KnowledgeStore } from '../knowledge/store.js';
|
|
9
36
|
import type { HarvestedMetadata } from './types.js';
|
|
37
|
+
import type { TableRelationship } from '../knowledge/types.js';
|
|
38
|
+
import { createKnowledgeMeta, createTableRef } from '../knowledge/types.js';
|
|
10
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Harvester configuration options.
|
|
42
|
+
*/
|
|
11
43
|
export interface HarvesterConfig {
|
|
12
44
|
/** Include table/column comments */
|
|
13
45
|
includeComments: boolean;
|
|
14
46
|
/** Include index information */
|
|
15
47
|
includeIndexes: boolean;
|
|
16
|
-
/** Include foreign key information */
|
|
48
|
+
/** Include foreign key information and convert to relationships */
|
|
17
49
|
includeForeignKeys: boolean;
|
|
18
50
|
/** Include estimated row counts */
|
|
19
51
|
includeRowCounts: boolean;
|
|
52
|
+
/** Automatically create relationships from FK (default: true) */
|
|
53
|
+
createRelationshipsFromFK: boolean;
|
|
20
54
|
}
|
|
21
55
|
|
|
22
56
|
const DEFAULT_CONFIG: HarvesterConfig = {
|
|
@@ -24,6 +58,7 @@ const DEFAULT_CONFIG: HarvesterConfig = {
|
|
|
24
58
|
includeIndexes: true,
|
|
25
59
|
includeForeignKeys: true,
|
|
26
60
|
includeRowCounts: true,
|
|
61
|
+
createRelationshipsFromFK: true,
|
|
27
62
|
};
|
|
28
63
|
|
|
29
64
|
/**
|
|
@@ -117,18 +152,32 @@ export class Harvester {
|
|
|
117
152
|
}
|
|
118
153
|
|
|
119
154
|
/**
|
|
120
|
-
* Sync harvested metadata to knowledge store
|
|
121
|
-
*
|
|
155
|
+
* Sync harvested metadata to knowledge store.
|
|
156
|
+
*
|
|
157
|
+
* Performs the following operations:
|
|
158
|
+
* 1. Adds table descriptions from DB COMMENT (if not already defined)
|
|
159
|
+
* 2. Adds column descriptions from DB COMMENT (if not already defined)
|
|
160
|
+
* 3. Converts FK constraints to TableRelationship entries (NEW in v2.0)
|
|
161
|
+
*
|
|
162
|
+
* Only adds new entries - doesn't overwrite user-defined descriptions.
|
|
163
|
+
*
|
|
164
|
+
* @param schema - Schema to harvest (defaults to 'public')
|
|
165
|
+
* @returns Summary of what was added
|
|
122
166
|
*/
|
|
123
167
|
async syncToKnowledge(schema: string = 'public'): Promise<{
|
|
124
168
|
tablesProcessed: number;
|
|
125
169
|
descriptionsAdded: number;
|
|
126
170
|
columnsAdded: number;
|
|
171
|
+
relationshipsAdded: number;
|
|
127
172
|
}> {
|
|
128
173
|
const harvested = await this.harvestSchema(schema);
|
|
129
174
|
|
|
130
175
|
let descriptionsAdded = 0;
|
|
131
176
|
let columnsAdded = 0;
|
|
177
|
+
let relationshipsAdded = 0;
|
|
178
|
+
|
|
179
|
+
// Collect all relationships first for batch insertion
|
|
180
|
+
const relationshipsToAdd: TableRelationship[] = [];
|
|
132
181
|
|
|
133
182
|
for (const metadata of harvested) {
|
|
134
183
|
// Check if table already has a user-defined description
|
|
@@ -139,9 +188,6 @@ export class Harvester {
|
|
|
139
188
|
await this.knowledge.setTableDescription(metadata.tableName, metadata.tableComment, {
|
|
140
189
|
schema: metadata.schema,
|
|
141
190
|
});
|
|
142
|
-
|
|
143
|
-
// Mark as auto-harvested by updating the source
|
|
144
|
-
// The knowledge store will have source: 'user' by default, we need to track this
|
|
145
191
|
descriptionsAdded++;
|
|
146
192
|
}
|
|
147
193
|
|
|
@@ -160,6 +206,26 @@ export class Harvester {
|
|
|
160
206
|
columnsAdded++;
|
|
161
207
|
}
|
|
162
208
|
}
|
|
209
|
+
|
|
210
|
+
// Convert FK to relationships (NEW in v2.0)
|
|
211
|
+
if (this.config.createRelationshipsFromFK && metadata.foreignKeys) {
|
|
212
|
+
for (const fk of metadata.foreignKeys) {
|
|
213
|
+
const relationship = this.createRelationshipFromFK(
|
|
214
|
+
metadata.tableName,
|
|
215
|
+
metadata.schema,
|
|
216
|
+
fk.column,
|
|
217
|
+
fk.referencedTable,
|
|
218
|
+
fk.referencedColumn,
|
|
219
|
+
fk.constraintName
|
|
220
|
+
);
|
|
221
|
+
relationshipsToAdd.push(relationship);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Batch add relationships
|
|
227
|
+
if (relationshipsToAdd.length > 0) {
|
|
228
|
+
relationshipsAdded = await this.knowledge.addRelationships(relationshipsToAdd);
|
|
163
229
|
}
|
|
164
230
|
|
|
165
231
|
// Save changes
|
|
@@ -169,20 +235,64 @@ export class Harvester {
|
|
|
169
235
|
tablesProcessed: harvested.length,
|
|
170
236
|
descriptionsAdded,
|
|
171
237
|
columnsAdded,
|
|
238
|
+
relationshipsAdded,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Create a TableRelationship from FK information.
|
|
244
|
+
*
|
|
245
|
+
* @internal
|
|
246
|
+
*/
|
|
247
|
+
private createRelationshipFromFK(
|
|
248
|
+
fromTable: string,
|
|
249
|
+
fromSchema: string,
|
|
250
|
+
fromColumn: string,
|
|
251
|
+
toTable: string,
|
|
252
|
+
toColumn: string,
|
|
253
|
+
constraintName?: string
|
|
254
|
+
): TableRelationship {
|
|
255
|
+
const schemaHash = this.knowledge.getSchemaHash();
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
...createKnowledgeMeta('auto', schemaHash),
|
|
259
|
+
type: 'table_relationship',
|
|
260
|
+
from: createTableRef(fromTable, fromSchema),
|
|
261
|
+
to: createTableRef(toTable, fromSchema), // Assume same schema for FK target
|
|
262
|
+
relationshipType: 'foreign_key',
|
|
263
|
+
joinCondition: `${fromTable}.${fromColumn} = ${toTable}.${toColumn}`,
|
|
264
|
+
cardinality: 'many-to-one', // Default for FK
|
|
265
|
+
fromColumns: [fromColumn],
|
|
266
|
+
toColumns: [toColumn],
|
|
267
|
+
isPreferred: true,
|
|
268
|
+
constraintName,
|
|
172
269
|
};
|
|
173
270
|
}
|
|
174
271
|
|
|
175
272
|
/**
|
|
176
|
-
* Get harvesting summary without syncing
|
|
273
|
+
* Get harvesting summary without syncing.
|
|
274
|
+
*
|
|
275
|
+
* Provides a preview of what would be harvested.
|
|
177
276
|
*/
|
|
178
277
|
async getSummary(schema: string = 'public'): Promise<{
|
|
179
278
|
tables: number;
|
|
180
279
|
tablesWithComments: number;
|
|
181
280
|
columnsWithComments: number;
|
|
182
281
|
tablesWithForeignKeys: number;
|
|
282
|
+
totalForeignKeys: number;
|
|
283
|
+
existingRelationships: number;
|
|
183
284
|
}> {
|
|
184
285
|
const harvested = await this.harvestSchema(schema);
|
|
185
286
|
|
|
287
|
+
// Count total FKs
|
|
288
|
+
const totalFKs = harvested.reduce(
|
|
289
|
+
(sum, h) => sum + (h.foreignKeys?.length ?? 0),
|
|
290
|
+
0
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Count existing relationships
|
|
294
|
+
const existingRels = this.knowledge.getRelationships().length;
|
|
295
|
+
|
|
186
296
|
return {
|
|
187
297
|
tables: harvested.length,
|
|
188
298
|
tablesWithComments: harvested.filter(h => h.tableComment).length,
|
|
@@ -191,6 +301,8 @@ export class Harvester {
|
|
|
191
301
|
0
|
|
192
302
|
),
|
|
193
303
|
tablesWithForeignKeys: harvested.filter(h => h.foreignKeys && h.foreignKeys.length > 0).length,
|
|
304
|
+
totalForeignKeys: totalFKs,
|
|
305
|
+
existingRelationships: existingRels,
|
|
194
306
|
};
|
|
195
307
|
}
|
|
196
308
|
}
|
package/src/core/types.ts
CHANGED
|
@@ -78,26 +78,40 @@ export interface QueryFeedback {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Harvested metadata from database
|
|
81
|
+
* Harvested metadata from database.
|
|
82
|
+
*
|
|
83
|
+
* This is the raw data collected from database introspection.
|
|
84
|
+
* Foreign keys are subsequently converted to TableRelationship entries
|
|
85
|
+
* by the Harvester during sync.
|
|
86
|
+
*
|
|
87
|
+
* @see TableRelationship for the graph edge representation
|
|
82
88
|
*/
|
|
83
89
|
export interface HarvestedMetadata {
|
|
84
90
|
/** Table name */
|
|
85
91
|
tableName: string;
|
|
86
92
|
/** Schema */
|
|
87
93
|
schema: string;
|
|
88
|
-
/** Table comment from DB */
|
|
94
|
+
/** Table comment from DB (COMMENT ON TABLE) */
|
|
89
95
|
tableComment?: string;
|
|
90
|
-
/** Column comments from DB */
|
|
96
|
+
/** Column comments from DB (COMMENT ON COLUMN) */
|
|
91
97
|
columnComments: Record<string, string>;
|
|
92
98
|
/** Estimated row count */
|
|
93
99
|
estimatedRowCount?: number;
|
|
94
|
-
/** Indexes */
|
|
100
|
+
/** Indexes (formatted as strings for display) */
|
|
95
101
|
indexes?: string[];
|
|
96
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Foreign keys (raw data).
|
|
104
|
+
* These are converted to TableRelationship entries during harvesting.
|
|
105
|
+
*/
|
|
97
106
|
foreignKeys?: Array<{
|
|
107
|
+
/** Source column name */
|
|
98
108
|
column: string;
|
|
109
|
+
/** Referenced table name */
|
|
99
110
|
referencedTable: string;
|
|
111
|
+
/** Referenced column name */
|
|
100
112
|
referencedColumn: string;
|
|
113
|
+
/** FK constraint name (optional) */
|
|
114
|
+
constraintName?: string;
|
|
101
115
|
}>;
|
|
102
116
|
}
|
|
103
117
|
|
|
@@ -175,6 +189,8 @@ export interface DataContextConfig {
|
|
|
175
189
|
includeIndexes: boolean;
|
|
176
190
|
includeForeignKeys: boolean;
|
|
177
191
|
includeRowCounts: boolean;
|
|
192
|
+
/** Convert FK constraints to TableRelationship entries (default: true) */
|
|
193
|
+
createRelationshipsFromFK?: boolean;
|
|
178
194
|
};
|
|
179
195
|
/** Metrics configuration */
|
|
180
196
|
metrics?: {
|
package/src/index.ts
CHANGED
|
@@ -58,8 +58,26 @@ export type { TableInfo, ColumnInfo, SchemaInfo } from './schema/types.js';
|
|
|
58
58
|
// === Knowledge Types ===
|
|
59
59
|
export type {
|
|
60
60
|
TableDescription,
|
|
61
|
+
TableRelationship,
|
|
62
|
+
TableRef,
|
|
63
|
+
RelationshipType,
|
|
64
|
+
RelationshipCardinality,
|
|
61
65
|
QueryExample,
|
|
62
66
|
BusinessRule,
|
|
63
|
-
|
|
67
|
+
BusinessTerm,
|
|
68
|
+
KnowledgeData,
|
|
69
|
+
KnowledgeEntry,
|
|
70
|
+
KnowledgeMeta,
|
|
71
|
+
KnowledgeSource,
|
|
72
|
+
} from './knowledge/types.js';
|
|
73
|
+
|
|
74
|
+
// === Knowledge Factory Functions ===
|
|
75
|
+
export {
|
|
76
|
+
createTableRef,
|
|
77
|
+
tableRefToString,
|
|
78
|
+
tableRefsEqual,
|
|
79
|
+
createRelationshipFromFK,
|
|
80
|
+
createKnowledgeMeta,
|
|
81
|
+
generateId,
|
|
64
82
|
} from './knowledge/types.js';
|
|
65
83
|
|