ship 1.4.59:Arcrun@f87260b1234f
This commit is contained in:
+116
-2
@@ -4241,11 +4241,15 @@ async function selectLibrariesForQuestion(db, question, opts = {}) {
|
||||
SELECT object AS name, ${libraryOf("library")} AS library FROM act WHERE object IS NOT NULL)
|
||||
SELECT name, library, COUNT(*) AS degree
|
||||
FROM ent
|
||||
WHERE LENGTH(name) >= 2 AND instr(?, lower(name)) > 0
|
||||
WHERE LENGTH(name) >= 2
|
||||
AND (
|
||||
instr(?, lower(name)) > 0
|
||||
OR (LENGTH(?) >= 2 AND instr(lower(name), ?) > 0)
|
||||
)
|
||||
GROUP BY name, library
|
||||
ORDER BY degree DESC, name ASC
|
||||
LIMIT 200`
|
||||
).bind(...params, qNorm).all();
|
||||
).bind(...params, qNorm, qNorm, qNorm).all();
|
||||
for (const r of res.results ?? []) {
|
||||
const cur = hits.get(r.library) ?? { entities: /* @__PURE__ */ new Set(), score: 0 };
|
||||
cur.entities.add(r.name);
|
||||
@@ -4545,6 +4549,109 @@ mapRoutes.get("/:library", async (c) => {
|
||||
return c.json({ success: true, map });
|
||||
});
|
||||
|
||||
// kbdb/src/actions/graph-query.ts
|
||||
async function findTripletEdgesByNode(db, templateIdOrName, fields, nodeValue, owner_id) {
|
||||
if (!nodeValue || fields.length === 0) return [];
|
||||
const tpl = await getTemplate(db, templateIdOrName);
|
||||
if (!tpl) return [];
|
||||
const valueSql = owner_id ? `SELECT id FROM entries WHERE content = ? AND entry_type = 'value' AND (owner_id = ? OR owner_id IS NULL)` : `SELECT id FROM entries WHERE content = ? AND entry_type = 'value'`;
|
||||
const valueParams = owner_id ? [nodeValue, owner_id] : [nodeValue];
|
||||
const valueRows = await db.prepare(valueSql).bind(...valueParams).all();
|
||||
const valueIds = (valueRows.results ?? []).map((r) => r.id);
|
||||
if (valueIds.length === 0) return [];
|
||||
const fieldIds = fields.map((f) => fieldEntryId(tpl.id, f));
|
||||
const dstPh = valueIds.map(() => "?").join(",");
|
||||
const relPh = fieldIds.map(() => "?").join(",");
|
||||
const relSql = owner_id ? `SELECT DISTINCT src_id AS record_id FROM entries
|
||||
WHERE dst_id IN (${dstPh}) AND rel_id IN (${relPh}) AND owner_id = ?` : `SELECT DISTINCT src_id AS record_id FROM entries
|
||||
WHERE dst_id IN (${dstPh}) AND rel_id IN (${relPh})`;
|
||||
const relParams = owner_id ? [...valueIds, ...fieldIds, owner_id] : [...valueIds, ...fieldIds];
|
||||
const relRows = await db.prepare(relSql).bind(...relParams).all();
|
||||
const recordIds = (relRows.results ?? []).map((r) => r.record_id);
|
||||
if (recordIds.length === 0) return [];
|
||||
const recPh = recordIds.map(() => "?").join(",");
|
||||
const pivotSql = `
|
||||
SELECT b.src_id AS record_id,
|
||||
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS subject,
|
||||
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS predicate,
|
||||
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS object,
|
||||
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS status
|
||||
FROM entries b
|
||||
LEFT JOIN entries r ON r.src_id = b.src_id AND r.rel_id != 'sys_belongs'
|
||||
LEFT JOIN entries v ON v.id = r.dst_id
|
||||
WHERE b.rel_id = 'sys_belongs' AND b.src_id IN (${recPh})
|
||||
GROUP BY b.src_id`;
|
||||
const pivotParams = [
|
||||
fieldEntryId(tpl.id, "subject"),
|
||||
fieldEntryId(tpl.id, "predicate"),
|
||||
fieldEntryId(tpl.id, "object"),
|
||||
fieldEntryId(tpl.id, "status"),
|
||||
...recordIds
|
||||
];
|
||||
const pivotRows = await db.prepare(pivotSql).bind(...pivotParams).all();
|
||||
return pivotRows.results ?? [];
|
||||
}
|
||||
async function graphNeighbors(db, start, opts = {}) {
|
||||
const depth = Math.max(1, Math.min(Math.floor(opts.depth ?? 1) || 1, 10));
|
||||
const template = opts.template ?? "triplet";
|
||||
const directed = !!opts.directed;
|
||||
const owner_id = opts.owner_id;
|
||||
const visited = /* @__PURE__ */ new Set([start]);
|
||||
let frontier = [start];
|
||||
const neighbors = [];
|
||||
for (let d = 1; d <= depth; d++) {
|
||||
if (frontier.length === 0) break;
|
||||
const next = [];
|
||||
for (const cur of frontier) {
|
||||
const outgoing = await findTripletEdgesByNode(db, template, ["subject"], cur, owner_id);
|
||||
for (const e of outgoing) {
|
||||
if (e.status === "deprecated") continue;
|
||||
const nb = e.object;
|
||||
if (!nb || visited.has(nb)) continue;
|
||||
visited.add(nb);
|
||||
neighbors.push({ node: nb, predicate: e.predicate ?? "", from: cur, depth: d });
|
||||
next.push(nb);
|
||||
}
|
||||
if (!directed) {
|
||||
const incoming = await findTripletEdgesByNode(db, template, ["object"], cur, owner_id);
|
||||
for (const e of incoming) {
|
||||
if (e.status === "deprecated") continue;
|
||||
const nb = e.subject;
|
||||
if (!nb || visited.has(nb)) continue;
|
||||
visited.add(nb);
|
||||
neighbors.push({ node: nb, predicate: e.predicate ?? "", from: cur, depth: d });
|
||||
next.push(nb);
|
||||
}
|
||||
}
|
||||
}
|
||||
frontier = next;
|
||||
}
|
||||
return { success: true, start, depth, directed, neighbors, count: neighbors.length };
|
||||
}
|
||||
|
||||
// kbdb/src/routes/graph.ts
|
||||
var graphRoutes = new Hono2();
|
||||
graphRoutes.get("/neighbors/:node", async (c) => {
|
||||
const node = c.req.param("node");
|
||||
if (!node) return c.json({ success: false, error: "node required" }, 400);
|
||||
const depthRaw = Number(c.req.query("depth"));
|
||||
const depth = Number.isFinite(depthRaw) && depthRaw > 0 ? Math.floor(depthRaw) : void 0;
|
||||
const directed = c.req.query("directed") === "true";
|
||||
const template = c.req.query("template") || void 0;
|
||||
const owner_id = c.req.query("owner_id") || void 0;
|
||||
try {
|
||||
const result = await graphNeighbors(c.env.DB, node, { depth, template, directed, owner_id });
|
||||
const edges = result.neighbors.map((n) => ({
|
||||
subject: n.from,
|
||||
predicate: n.predicate,
|
||||
object: n.node
|
||||
}));
|
||||
return c.json({ ...result, edges });
|
||||
} catch (e) {
|
||||
return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// kbdb/src/actions/execution-log.ts
|
||||
var SUCCESS_MESSAGE_MAX = 200;
|
||||
var FAILED_MESSAGE_MAX = 2e3;
|
||||
@@ -4832,6 +4939,12 @@ var GENERATIONS = [
|
||||
// 🪦 entry_values 2026-08-15 廢除;「看到它=那台沒跟上,不是它還合法」。
|
||||
{ kind: "no_table", name: "entry_values" }
|
||||
]
|
||||
},
|
||||
{
|
||||
n: 8,
|
||||
file: "0008_entries_content_index.sql",
|
||||
what: "entries.content \u7D22\u5F15\u2014\u2014graph \u9130\u5C45\u67E5\u8A62\u5F9E\u7BC0\u9EDE\u540D\u76F4\u63A5\u67E5\uFF08\u4E0D\u6488\u5168\u8868\uFF09\uFF0CArcrun#168 \u6839\u56E0\u4FEE\u5FA9\u7684\u5730\u57FA",
|
||||
checks: [{ kind: "index", name: "idx_entries_content" }]
|
||||
}
|
||||
];
|
||||
var EXPECTED_GENERATION = GENERATIONS[GENERATIONS.length - 1].n;
|
||||
@@ -4990,6 +5103,7 @@ app.route("/recipe-stats", recipeStatRoutes);
|
||||
app.route("/execution-log", executionLogRoutes);
|
||||
app.route("/embed", embedRoutes);
|
||||
app.route("/map", mapRoutes);
|
||||
app.route("/graph", graphRoutes);
|
||||
var index_default = app;
|
||||
export {
|
||||
index_default as default
|
||||
|
||||
Reference in New Issue
Block a user