126 lines
No EOL
4 KiB
TypeScript
126 lines
No EOL
4 KiB
TypeScript
/**
|
|
* knowledge.ts — Knowledge Graph endpoints
|
|
* Uses raw SQL execution to create tables if not exist
|
|
*/
|
|
|
|
import { Router, Request, Response } from "express";
|
|
import { randomUUID } from "crypto";
|
|
import db from "../db.js";
|
|
|
|
const router = Router();
|
|
|
|
// Ensure tables exist
|
|
const initTables = () => {
|
|
try {
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS knowledge_nodes (
|
|
id TEXT PRIMARY KEY,
|
|
type TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
`);
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS knowledge_edges (
|
|
id TEXT PRIMARY KEY,
|
|
from_node TEXT NOT NULL,
|
|
to_node TEXT NOT NULL,
|
|
relationship TEXT NOT NULL,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
`);
|
|
} catch (e) { /* tables may exist */ }
|
|
};
|
|
initTables();
|
|
|
|
// List all nodes
|
|
router.get("/", async (req: Request, res: Response) => {
|
|
const { q, limit = 50 } = req.query;
|
|
try {
|
|
let sql = "SELECT * FROM knowledge_nodes";
|
|
const params: string[] = [];
|
|
if (q) {
|
|
sql += " WHERE name LIKE ? OR description LIKE ?";
|
|
params.push(`%${q}%`, `%${q}%`);
|
|
}
|
|
sql += " ORDER BY created_at DESC LIMIT ?";
|
|
params.push(String(limit));
|
|
const nodes = db.prepare(sql).all(...params);
|
|
res.json({ nodes });
|
|
} catch (err: any) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// Get node with connections
|
|
router.get("/:id", async (req: Request, res: Response) => {
|
|
const { id } = req.params;
|
|
try {
|
|
const node = db.prepare("SELECT * FROM knowledge_nodes WHERE id = ?").get(id);
|
|
if (!node) return res.status(404).json({ error: "Not found" });
|
|
// Get all edges for graph
|
|
const edges = db.prepare(`
|
|
SELECT ke.from_node, ke.to_node, ke.relationship, kn.name as to_name
|
|
FROM knowledge_edges ke
|
|
JOIN knowledge_nodes kn ON ke.to_node = kn.id
|
|
`).all();
|
|
// Get all nodes for graph
|
|
const allNodes = db.prepare("SELECT * FROM knowledge_nodes").all();
|
|
res.json({ node, connections: edges, allNodes });
|
|
} catch (err: any) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// Create node
|
|
router.post("/", async (req: Request, res: Response) => {
|
|
const { type, name, description } = req.body;
|
|
if (!type || !name) return res.status(400).json({ error: "type, name required" });
|
|
const id = randomUUID();
|
|
try {
|
|
db.prepare("INSERT INTO knowledge_nodes (id, type, name, description) VALUES (?, ?, ?, ?)")
|
|
.run(id, type, name, description || "");
|
|
res.json({ ok: true, id });
|
|
} catch (err: any) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// Create connection
|
|
router.post("/connect", async (req: Request, res: Response) => {
|
|
const { from, to, relationship } = req.body;
|
|
if (!from || !to || !relationship) {
|
|
return res.status(400).json({ error: "from, to, relationship required" });
|
|
}
|
|
try {
|
|
const id = randomUUID();
|
|
db.prepare("INSERT INTO knowledge_edges (id, from_node, to_node, relationship) VALUES (?, ?, ?, ?)")
|
|
.run(id, from, to, relationship);
|
|
res.json({ ok: true, id });
|
|
} catch (err: any) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// Seed initial knowledge
|
|
router.post("/seed", async (req: Request, res: Response) => {
|
|
const nodes = [
|
|
{ t: "person", n: "Manohar", d: "IIT Roorkee, IIM Rohtak, works at Renew Power" },
|
|
{ t: "company", n: "Renew Power", d: "India renewable energy, NYSE: RNW" },
|
|
{ t: "company", n: "Adani Green", d: "Competitor in renewables" },
|
|
{ t: "concept", n: "PE/VC", d: "Career path interest" },
|
|
{ t: "concept", n: "Option Trading", d: "Nifty options selling" },
|
|
];
|
|
try {
|
|
for (const x of nodes) {
|
|
db.prepare("INSERT OR IGNORE INTO knowledge_nodes (id, type, name, description) VALUES (?, ?, ?, ?)")
|
|
.run(randomUUID(), x.t, x.n, x.d);
|
|
}
|
|
res.json({ ok: true, count: nodes.length });
|
|
} catch (err: any) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
export default router; |