From 772e7a1093ffc26ba3ed4c2f0c45bb7c4434cc24 Mon Sep 17 00:00:00 2001 From: Michael Gartner Date: Thu, 25 Jun 2026 22:04:28 -0600 Subject: [PATCH] ENG-1062 use internal find variables for relation metadata --- .../src/utils/__tests__/fireQuery.test.ts | 28 ++++++++ apps/roam/src/utils/fireQuery.ts | 69 ++++++++++++------- .../src/utils/getDiscourseContextResults.ts | 48 ++++++------- apps/roam/src/utils/predefinedSelections.ts | 22 ------ .../registerDiscourseDatalogTranslators.ts | 2 - 5 files changed, 93 insertions(+), 76 deletions(-) diff --git a/apps/roam/src/utils/__tests__/fireQuery.test.ts b/apps/roam/src/utils/__tests__/fireQuery.test.ts index dcc1efb54..6b5686605 100644 --- a/apps/roam/src/utils/__tests__/fireQuery.test.ts +++ b/apps/roam/src/utils/__tests__/fireQuery.test.ts @@ -66,6 +66,34 @@ describe("getDatalogQuery", () => { ]); expect(formatted).toMatchObject({ text: "A", uid: "u1", Created: "123" }); }); + + it("adds internal find variables to the query output", async () => { + const built = getDatalogQuery({ + conditions: [], + selections: [], + findVariables: [ + { label: "relationUid", variable: "condition-relSchema" }, + { label: "effectiveSource", variable: "?condition-relSource" }, + ], + }); + + expect(built.query).toContain("?condition-relSchema"); + expect(built.query).toContain("?condition-relSource"); + + const formatted = await built.formatResult([ + { ":node/title": "A", ":block/uid": "u1" }, + { ":block/uid": "u1" }, + "rel-1", + "source-1", + ]); + + expect(formatted).toMatchObject({ + text: "A", + uid: "u1", + relationUid: "rel-1", + effectiveSource: "source-1", + }); + }); }); describe("fireQuery", () => { diff --git a/apps/roam/src/utils/fireQuery.ts b/apps/roam/src/utils/fireQuery.ts index 1d68589d6..05b5bb27f 100644 --- a/apps/roam/src/utils/fireQuery.ts +++ b/apps/roam/src/utils/fireQuery.ts @@ -21,6 +21,10 @@ export type QueryArgs = { conditions: Condition[]; selections: Selection[]; inputs?: Record; + findVariables?: { + label: string; + variable: string; + }[]; }; type RelationInQuery = { id: string; @@ -39,6 +43,12 @@ export type FireQueryArgs = QueryArgs & { }; type FireQuery = (query: FireQueryArgs) => Promise; +type DefinedSelection = { + mapper?: PredefinedSelection["mapper"]; + pull: string; + label: string; + key: string; +}; const firstVariable = ( clause: DatalogClause | DatalogAndClause, @@ -200,6 +210,7 @@ export const getDatalogQuery = ({ selections, returnNode = DEFAULT_RETURN_NODE, inputs = {}, + findVariables = [], }: FireQueryArgs) => { const expectedInputs = getConditionTargets(conditions) .filter((c) => /^:in /.test(c)) @@ -211,12 +222,7 @@ export const getDatalogQuery = ({ new Set([]), ); - const defaultSelections: { - mapper: PredefinedSelection["mapper"]; - pull: string; - label: string; - key: string; - }[] = [ + const defaultSelections: DefinedSelection[] = [ { mapper: (r) => { return { @@ -237,26 +243,35 @@ export const getDatalogQuery = ({ key: "", }, ]; + const userSelections: DefinedSelection[] = selections + .map((s) => ({ + defined: predefinedSelections.find((p) => p.test.test(s.text)), + s, + })) + .filter( + (p): p is { defined: PredefinedSelection; s: Selection } => !!p.defined, + ) + .map((p) => ({ + mapper: p.defined.mapper, + pull: p.defined.pull({ + where: whereClauses, + returnNode, + match: p.defined.test.exec(p.s.text), + }), + label: p.s.label || p.s.text, + key: p.s.text, + })) + .filter((p) => !!p.pull); + const internalFindVariables: DefinedSelection[] = findVariables.map( + ({ label, variable }) => ({ + pull: variable.startsWith("?") ? variable : `?${variable}`, + label, + key: variable, + }), + ); const definedSelections = defaultSelections.concat( - selections - .map((s) => ({ - defined: predefinedSelections.find((p) => p.test.test(s.text)), - s, - })) - .filter( - (p): p is { defined: PredefinedSelection; s: Selection } => !!p.defined, - ) - .map((p) => ({ - mapper: p.defined.mapper, - pull: p.defined.pull({ - where: whereClauses, - returnNode, - match: p.defined.test.exec(p.s.text), - }), - label: p.s.label || p.s.text, - key: p.s.text, - })) - .filter((p) => !!p.pull), + userSelections, + internalFindVariables, ); const find = definedSelections.map((p) => p.pull).join("\n "); const where = whereClauses.map((c) => compileDatalog(c, 1)).join("\n"); @@ -274,7 +289,9 @@ export const getDatalogQuery = ({ definedSelections .map((c, i) => (prev: QueryResult) => { const pullResult = result[i]; - return typeof pullResult === "object" && pullResult !== null + return c.mapper && + typeof pullResult === "object" && + pullResult !== null ? Promise.resolve( c.mapper(pullResult as PullBlock, c.key, prev), ).then((output) => ({ diff --git a/apps/roam/src/utils/getDiscourseContextResults.ts b/apps/roam/src/utils/getDiscourseContextResults.ts index 53161a326..032781857 100644 --- a/apps/roam/src/utils/getDiscourseContextResults.ts +++ b/apps/roam/src/utils/getDiscourseContextResults.ts @@ -57,19 +57,6 @@ const buildSelections = ({ text: `node:${conditionUid}-Context`, }); } - if (ANY_RELATION_REGEX.test(r.label)) { - selections.push({ - uid: window.roamAlphaAPI.util.generateUID(), - label: "relationUid", - text: "hasSchema", - }); - selections.push({ - uid: window.roamAlphaAPI.util.generateUID(), - label: "effectiveSource", - text: "effectiveSource", - }); - } - return selections; }; @@ -148,7 +135,20 @@ const buildQueryConfig = ({ const returnNode = nodeTextByType[target]; const conditionUid = window.roamAlphaAPI.util.generateUID(); + const isAllRelationsQuery = ANY_RELATION_REGEX.test(r.label); const selections = buildSelections({ r, conditionUid }); + const findVariables = isAllRelationsQuery + ? [ + { + label: "relationUid", + variable: `${conditionUid}-relSchema`, + }, + { + label: "effectiveSource", + variable: `${conditionUid}-relSource`, + }, + ] + : []; const { nodes, relations } = fireQueryContext; return { relation, @@ -166,6 +166,7 @@ const buildQueryConfig = ({ }, ], selections, + findVariables, context: { relationsInQuery: [relation], customNodes: nodes, @@ -290,26 +291,21 @@ const getDiscourseContextResults = async ({ const relation = uniqueRelations.get(ruid); if (!relation) { console.error("Relation with obsolete relation type:" + ruid); - return { relation, results }; + return undefined; } + const isComplement = relation.complement; return { relation: { - id: ruid, - label: ruid.endsWith("-true") - ? relation.r.label - : relation.r.complement, - isComplement: ruid.endsWith("-true"), - text: ruid.endsWith("-true") - ? relation.r.label - : relation.r.complement, - target: ruid.endsWith("-true") - ? relation.r.source - : relation.r.destination, + id: relation.id, + label: isComplement ? relation.r.complement : relation.r.label, + isComplement, + text: isComplement ? relation.r.complement : relation.r.label, + target: isComplement ? relation.r.source : relation.r.destination, }, results, }; }) - .filter((o) => !!o.relation); + .filter((o): o is NonNullable => !!o); } const groupedResults = Object.fromEntries( resultsWithRelation.map((r) => [ diff --git a/apps/roam/src/utils/predefinedSelections.ts b/apps/roam/src/utils/predefinedSelections.ts index 5bbb4951c..060a33750 100644 --- a/apps/roam/src/utils/predefinedSelections.ts +++ b/apps/roam/src/utils/predefinedSelections.ts @@ -30,8 +30,6 @@ const NODE_TEST = /^node:(\s*[^:]+\s*)(:.*)?$/i; const ACTION_TEST = /^action:\s*([^:]+)\s*(?::(.*))?$/i; const DATE_FORMAT_TEST = /^date-format\(([^,)]+),([^,)]+)\)$/i; const MILLISECONDS_IN_DAY = 1000 * 60 * 60 * 24; -const HAS_SCHEMA_TEST = /^hasSchema$/; -const EFFECTIVE_SOURCE_TEST = /^effectiveSource$/; const getArgValue = (key: string, result: QueryResult) => { if (/^today$/i.test(key)) return new Date(); @@ -287,26 +285,6 @@ const predefinedSelections: PredefinedSelection[] = [ }, suggestions: EDIT_BY_SUGGESTIONS, }, - { - test: HAS_SCHEMA_TEST, - pull: () => { - return "?relSchema"; - }, - mapper: () => { - // not sure here? - return "?relSchema"; - }, - }, - { - test: EFFECTIVE_SOURCE_TEST, - pull: () => { - return "?relSource"; - }, - mapper: () => { - // not sure here? - return "?relSource"; - }, - }, { test: NODE_TEST, pull: ({ match, returnNode, where }) => { diff --git a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts index a5fb7f6af..f89108983 100644 --- a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts +++ b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts @@ -819,8 +819,6 @@ const registerDiscourseDatalogTranslators = (snapshot?: SettingsSnapshot) => { [ { from: source, to: source }, { from: target, to: target }, - { from: "relSchema", to: "relSchema" }, - { from: "relSource", to: "relSource" }, { from: true, to: (v) => `${uid}-${v}` }, ], clauses,