ship 1.4.60:Arcrun@98b45409740b
This commit is contained in:
+64
-38
@@ -2127,6 +2127,11 @@ function libraryOf(expr) {
|
||||
var ENTRY_LIBRARY_EXPR = "json_extract(metadata_json, '$.library')";
|
||||
var ENTRY_LIBRARY = libraryOf(ENTRY_LIBRARY_EXPR);
|
||||
var ENTRY_UNLABELLED = `(${ENTRY_LIBRARY_EXPR} IS NULL OR ${ENTRY_LIBRARY_EXPR} = '')`;
|
||||
function parseLibraryList(raw2) {
|
||||
if (!raw2) return void 0;
|
||||
const libs = raw2.split(",").map((s) => s.trim()).filter(Boolean);
|
||||
return libs.length > 0 ? libs : void 0;
|
||||
}
|
||||
|
||||
// kbdb/src/actions/entry-crud.ts
|
||||
function uid(prefix) {
|
||||
@@ -3198,11 +3203,7 @@ function fireAndForget(c, p) {
|
||||
else void p.catch(() => {
|
||||
});
|
||||
}
|
||||
function parseLibraryParam(raw2) {
|
||||
if (!raw2) return void 0;
|
||||
const libs = raw2.split(",").map((s) => s.trim()).filter(Boolean);
|
||||
return libs.length > 0 ? libs : void 0;
|
||||
}
|
||||
var parseLibraryParam = parseLibraryList;
|
||||
entryRoutes.post("/", async (c) => {
|
||||
const body = await c.req.json().catch(() => null);
|
||||
if (!body || !body.entry_type) return c.json({ success: false, error: "entry_type required" }, 400);
|
||||
@@ -4550,7 +4551,15 @@ mapRoutes.get("/:library", async (c) => {
|
||||
});
|
||||
|
||||
// kbdb/src/actions/graph-query.ts
|
||||
async function findTripletEdgesByNode(db, templateIdOrName, fields, nodeValue, owner_id) {
|
||||
var D1_MAX_BOUND_PARAMS = 90;
|
||||
function chunkForD1(items, fixedParams) {
|
||||
const size = Math.max(1, D1_MAX_BOUND_PARAMS - fixedParams);
|
||||
if (items.length <= size) return [items];
|
||||
const out = [];
|
||||
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
||||
return out;
|
||||
}
|
||||
async function findTripletEdgesByNode(db, templateIdOrName, fields, nodeValue, owner_id, libraries) {
|
||||
if (!nodeValue || fields.length === 0) return [];
|
||||
const tpl = await getTemplate(db, templateIdOrName);
|
||||
if (!tpl) return [];
|
||||
@@ -4560,42 +4569,58 @@ async function findTripletEdgesByNode(db, templateIdOrName, fields, nodeValue, o
|
||||
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);
|
||||
const recordIdSet = /* @__PURE__ */ new Set();
|
||||
for (const idsChunk of chunkForD1(valueIds, fieldIds.length + (owner_id ? 1 : 0))) {
|
||||
const dstPh = idsChunk.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 ? [...idsChunk, ...fieldIds, owner_id] : [...idsChunk, ...fieldIds];
|
||||
const relRows = await db.prepare(relSql).bind(...relParams).all();
|
||||
for (const r of relRows.results ?? []) recordIdSet.add(r.record_id);
|
||||
}
|
||||
const recordIds = [...recordIdSet];
|
||||
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 ?? [];
|
||||
const libFilter = libraries && libraries.length > 0 ? libraries : null;
|
||||
const libAgg = "MAX(CASE WHEN r.rel_id = ? THEN v.content END)";
|
||||
const pivotFixed = 5 + (libFilter ? 1 + libFilter.length : 0);
|
||||
const rows = [];
|
||||
for (const recChunk of chunkForD1(recordIds, pivotFixed)) {
|
||||
const recPh = recChunk.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,
|
||||
${libAgg} AS library
|
||||
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${libFilter ? `
|
||||
HAVING ${libraryOf(libAgg)} IN (${libFilter.map(() => "?").join(",")})` : ""}`;
|
||||
const pivotParams = [
|
||||
fieldEntryId(tpl.id, "subject"),
|
||||
fieldEntryId(tpl.id, "predicate"),
|
||||
fieldEntryId(tpl.id, "object"),
|
||||
fieldEntryId(tpl.id, "status"),
|
||||
fieldEntryId(tpl.id, "library"),
|
||||
...recChunk,
|
||||
...libFilter ? [fieldEntryId(tpl.id, "library"), ...libFilter] : []
|
||||
];
|
||||
const pivotRows = await db.prepare(pivotSql).bind(...pivotParams).all();
|
||||
rows.push(...pivotRows.results ?? []);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
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 libraries = opts.library && opts.library.length > 0 ? opts.library : void 0;
|
||||
const visited = /* @__PURE__ */ new Set([start]);
|
||||
let frontier = [start];
|
||||
const neighbors = [];
|
||||
@@ -4603,7 +4628,7 @@ async function graphNeighbors(db, start, opts = {}) {
|
||||
if (frontier.length === 0) break;
|
||||
const next = [];
|
||||
for (const cur of frontier) {
|
||||
const outgoing = await findTripletEdgesByNode(db, template, ["subject"], cur, owner_id);
|
||||
const outgoing = await findTripletEdgesByNode(db, template, ["subject"], cur, owner_id, libraries);
|
||||
for (const e of outgoing) {
|
||||
if (e.status === "deprecated") continue;
|
||||
const nb = e.object;
|
||||
@@ -4613,7 +4638,7 @@ async function graphNeighbors(db, start, opts = {}) {
|
||||
next.push(nb);
|
||||
}
|
||||
if (!directed) {
|
||||
const incoming = await findTripletEdgesByNode(db, template, ["object"], cur, owner_id);
|
||||
const incoming = await findTripletEdgesByNode(db, template, ["object"], cur, owner_id, libraries);
|
||||
for (const e of incoming) {
|
||||
if (e.status === "deprecated") continue;
|
||||
const nb = e.subject;
|
||||
@@ -4626,7 +4651,7 @@ async function graphNeighbors(db, start, opts = {}) {
|
||||
}
|
||||
frontier = next;
|
||||
}
|
||||
return { success: true, start, depth, directed, neighbors, count: neighbors.length };
|
||||
return { success: true, start, depth, directed, libraries: libraries ?? null, neighbors, count: neighbors.length };
|
||||
}
|
||||
|
||||
// kbdb/src/routes/graph.ts
|
||||
@@ -4639,8 +4664,9 @@ graphRoutes.get("/neighbors/:node", async (c) => {
|
||||
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;
|
||||
const library = parseLibraryList(c.req.query("library"));
|
||||
try {
|
||||
const result = await graphNeighbors(c.env.DB, node, { depth, template, directed, owner_id });
|
||||
const result = await graphNeighbors(c.env.DB, node, { depth, template, directed, owner_id, library });
|
||||
const edges = result.neighbors.map((n) => ({
|
||||
subject: n.from,
|
||||
predicate: n.predicate,
|
||||
|
||||
Reference in New Issue
Block a user