feat(graph): persist multi-language nodes to KuzuDB and add description field

- Add description STRING column to all code element schemas (Function, Class,
  Interface, Method, CodeElement, and all CODE_ELEMENT_BASE types including Property)
- Add generateMultiLangNodeCSV() to write Struct, Enum, Trait, Property, etc.
  nodes to KuzuDB (previously they existed in-memory only, never reached the DB)
- Update generateAllCSVs() to generate CSVs for all 19 multi-language types
- Fix getCopyQuery() to use correct column list per table type:
  - Multi-language tables: id,name,filePath,startLine,endLine,content,description
  - Core tables: id,name,filePath,startLine,endLine,isExported,content,description
- Update batchInsertNodesToKuzu and insertNodeToKuzu to include description field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Güneş Bizim 2026-02-25 10:31:53 +03:00
parent 890fedaa09
commit 1c02a06d1b
3 changed files with 62 additions and 9 deletions

View file

@ -180,16 +180,16 @@ const generateFolderCSV = (nodes: GraphNode[]): string => {
/**
* Generate CSV for code element nodes (Function, Class, Interface, Method, CodeElement)
* Headers: id,name,filePath,startLine,endLine,isExported,content
* Headers: id,name,filePath,startLine,endLine,isExported,content,description
*/
const generateCodeElementCSV = (
nodes: GraphNode[],
label: NodeLabel,
fileContents: Map<string, string>
): string => {
const headers = ['id', 'name', 'filePath', 'startLine', 'endLine', 'isExported', 'content'];
const headers = ['id', 'name', 'filePath', 'startLine', 'endLine', 'isExported', 'content', 'description'];
const rows: string[] = [headers.join(',')];
for (const node of nodes) {
if (node.label !== label) continue;
const content = extractContent(node, fileContents);
@ -201,9 +201,10 @@ const generateCodeElementCSV = (
escapeCSVNumber(node.properties.endLine, -1),
node.properties.isExported ? 'true' : 'false',
escapeCSVField(content),
escapeCSVField((node.properties as any).description || ''),
].join(','));
}
return rows.join('\n');
};
@ -267,6 +268,35 @@ const generateProcessCSV = (nodes: GraphNode[]): string => {
return rows.join('\n');
};
/**
* Generate CSV for multi-language node tables (Struct, Enum, Trait, Property, etc.)
* These use CODE_ELEMENT_BASE schema: id,name,filePath,startLine,endLine,content,description
*/
const generateMultiLangNodeCSV = (
nodes: GraphNode[],
label: string,
fileContents: Map<string, string>
): string => {
const headers = ['id', 'name', 'filePath', 'startLine', 'endLine', 'content', 'description'];
const rows: string[] = [headers.join(',')];
for (const node of nodes) {
if (node.label !== label) continue;
const content = extractContent(node, fileContents);
rows.push([
escapeCSVField(node.id),
escapeCSVField(node.properties.name || ''),
escapeCSVField(node.properties.filePath || ''),
escapeCSVNumber(node.properties.startLine, -1),
escapeCSVNumber(node.properties.endLine, -1),
escapeCSVField(content),
escapeCSVField((node.properties as any).description || ''),
].join(','));
}
return rows.join('\n');
};
/**
* Generate CSV for the single CodeRelation table
* Headers: from,to,type,confidence,reason
@ -317,7 +347,17 @@ export const generateAllCSVs = (
nodeCSVs.set('CodeElement', generateCodeElementCSV(nodes, 'CodeElement', fileContents));
nodeCSVs.set('Community', generateCommunityCSV(nodes));
nodeCSVs.set('Process', generateProcessCSV(nodes));
// Multi-language node types (CODE_ELEMENT_BASE schema: id,name,filePath,startLine,endLine,content,description)
const multiLangTypes = [
'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl',
'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation',
'Constructor', 'Template', 'Module',
] as const;
for (const mlType of multiLangTypes) {
nodeCSVs.set(mlType, generateMultiLangNodeCSV(nodes, mlType, fileContents));
}
// Generate single relation CSV
const relCSV = generateRelationCSV(graph);

View file

@ -282,8 +282,12 @@ const getCopyQuery = (table: NodeTableName, filePath: string): string => {
if (table === 'Process') {
return `COPY ${t}(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${filePath}" ${COPY_CSV_OPTS}`;
}
// Code element tables (Function, Class, Interface, Method, CodeElement, and multi-language)
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content) FROM "${filePath}" ${COPY_CSV_OPTS}`;
// Multi-language code element tables (CODE_ELEMENT_BASE: no isExported, has description)
if (BACKTICK_TABLES.has(table)) {
return `COPY ${t}(id, name, filePath, startLine, endLine, content, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
}
// Core code element tables (Function, Class, Interface, Method, CodeElement)
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
};
/**
@ -320,7 +324,8 @@ export const insertNodeToKuzu = async (
query = `CREATE (n:Folder {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}})`;
} else {
// Function, Class, Method, Interface, etc. - standard code element schema
query = `CREATE (n:${label} {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, content: ${escapeValue(properties.content || '')}})`;
const descStr = properties.description ? `, description: ${escapeValue(properties.description)}` : '';
query = `CREATE (n:${label} {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, content: ${escapeValue(properties.content || '')}${descStr}})`;
}
// Use per-query connection if dbPath provided (avoids lock conflicts)
@ -385,7 +390,8 @@ export const batchInsertNodesToKuzu = async (
} else if (label === 'Folder') {
query = `MERGE (n:Folder {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}`;
} else {
query = `MERGE (n:${label} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${escapeValue(properties.content || '')}`;
const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : '';
query = `MERGE (n:${label} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${escapeValue(properties.content || '')}${descPart}`;
}
await tempConn.query(query);

View file

@ -64,6 +64,7 @@ CREATE NODE TABLE Function (
endLine INT64,
isExported BOOLEAN,
content STRING,
description STRING,
PRIMARY KEY (id)
)`;
@ -76,6 +77,7 @@ CREATE NODE TABLE Class (
endLine INT64,
isExported BOOLEAN,
content STRING,
description STRING,
PRIMARY KEY (id)
)`;
@ -88,6 +90,7 @@ CREATE NODE TABLE Interface (
endLine INT64,
isExported BOOLEAN,
content STRING,
description STRING,
PRIMARY KEY (id)
)`;
@ -100,6 +103,7 @@ CREATE NODE TABLE Method (
endLine INT64,
isExported BOOLEAN,
content STRING,
description STRING,
PRIMARY KEY (id)
)`;
@ -112,6 +116,7 @@ CREATE NODE TABLE CodeElement (
endLine INT64,
isExported BOOLEAN,
content STRING,
description STRING,
PRIMARY KEY (id)
)`;
@ -154,6 +159,7 @@ CREATE NODE TABLE Process (
// ============================================================================
// Generic code element with startLine/endLine for C, C++, Rust, Go, Java, C#
// description: optional metadata (e.g. Eloquent $fillable fields, relationship targets)
const CODE_ELEMENT_BASE = (name: string) => `
CREATE NODE TABLE \`${name}\` (
id STRING,
@ -162,6 +168,7 @@ CREATE NODE TABLE \`${name}\` (
startLine INT64,
endLine INT64,
content STRING,
description STRING,
PRIMARY KEY (id)
)`;