From 397af678d255e42b121802ef8d14e8d1e5884fbd Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Sun, 9 Nov 2025 19:35:24 -0500 Subject: [PATCH 1/8] eng-1012 update query builder to use reified relations --- apps/roam/package.json | 2 +- apps/roam/src/utils/createReifiedBlock.ts | 14 +- apps/roam/src/utils/fireQuery.ts | 10 +- .../registerDiscourseDatalogTranslators.ts | 658 +++++++++++++----- pnpm-lock.yaml | 10 +- 5 files changed, 489 insertions(+), 205 deletions(-) diff --git a/apps/roam/package.json b/apps/roam/package.json index df76bd551..1ca5da66b 100644 --- a/apps/roam/package.json +++ b/apps/roam/package.json @@ -68,7 +68,7 @@ "react-draggable": "4.4.5", "react-in-viewport": "1.0.0-alpha.20", "react-vertical-timeline-component": "3.5.2", - "roamjs-components": "0.85.4", + "roamjs-components": "0.85.6", "tldraw": "2.3.0", "use-sync-external-store": "1.5.0", "xregexp": "^5.0.0", diff --git a/apps/roam/src/utils/createReifiedBlock.ts b/apps/roam/src/utils/createReifiedBlock.ts index 7673edb50..27afc9e09 100644 --- a/apps/roam/src/utils/createReifiedBlock.ts +++ b/apps/roam/src/utils/createReifiedBlock.ts @@ -72,7 +72,7 @@ const createReifiedBlock = async ({ const RELATION_PAGE_TITLE = "roam/js/discourse-graph/relations"; let relationPageUid: string | undefined = undefined; -const getRelationPageUid = async (): Promise => { +export const getOrCreateRelationPageUid = async (): Promise => { if (relationPageUid === undefined) { relationPageUid = getPageUidByPageTitle(RELATION_PAGE_TITLE); if (relationPageUid === "") { @@ -82,8 +82,16 @@ const getRelationPageUid = async (): Promise => { return relationPageUid; }; +export const getExistingRelationPageUid = (): string | undefined => { + if (relationPageUid === undefined) { + const uid = getPageUidByPageTitle(RELATION_PAGE_TITLE); + if (uid !== "") relationPageUid = uid; + } + return relationPageUid; +}; + export const countReifiedRelations = async (): Promise => { - const pageUid = await getRelationPageUid(); + const pageUid = getExistingRelationPageUid(); if (pageUid === undefined) return 0; const r = await window.roamAlphaAPI.data.async.q( `[:find (count ?c) :where [?p :block/children ?c] [?p :block/uid "${pageUid}"]]`, @@ -103,7 +111,7 @@ export const createReifiedRelation = async ({ const authorized = getSetting("use-reified-relations"); if (authorized) { return await createReifiedBlock({ - destinationBlockUid: await getRelationPageUid(), + destinationBlockUid: await getOrCreateRelationPageUid(), schemaUid: relationBlockUid, parameterUids: { sourceUid, diff --git a/apps/roam/src/utils/fireQuery.ts b/apps/roam/src/utils/fireQuery.ts index 3098e0585..e78b4e96e 100644 --- a/apps/roam/src/utils/fireQuery.ts +++ b/apps/roam/src/utils/fireQuery.ts @@ -1,8 +1,8 @@ import conditionToDatalog from "./conditionToDatalog"; import type { PullBlock, - DatalogAndClause, DatalogClause, + DatalogAndClause, } from "roamjs-components/types"; import compileDatalog from "./compileDatalog"; import { getNodeEnv } from "roamjs-components/util/env"; @@ -107,7 +107,8 @@ const optimizeQuery = ( if (Array.from(allVars).every((v) => capturedVariables.has(v))) { score = 10; } else { - score = 100002; + // downgrade disjunction and negation + score = c.type === "and-clause" ? 100002 : 100006; } } else if (c.type === "not-join-clause" || c.type === "or-join-clause") { if (c.variables.every((v) => capturedVariables.has(v.value))) { @@ -156,6 +157,9 @@ const optimizeQuery = ( .filter((v) => v.type === "variable") .forEach((v) => capturedVariables.add(v.value)); } + // Question: Should we not consider all variables in a complex clause captured? + // const newVars = gatherDatalogVariablesFromClause(bestClause); + // newVars.forEach((v) => capturedVariables.add(v)); } return orderedClauses; }; @@ -197,7 +201,7 @@ export const getDatalogQuery = ({ const whereClauses = optimizeQuery( getWhereClauses({ conditions, returnNode }), new Set([]), - ) as DatalogClause[]; + ); const defaultSelections: { mapper: PredefinedSelection["mapper"]; diff --git a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts index 0f54e9f1c..2e52f61c6 100644 --- a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts +++ b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts @@ -1,5 +1,9 @@ import getPageTitlesStartingWithPrefix from "roamjs-components/queries/getPageTitlesStartingWithPrefix"; -import { DatalogAndClause, DatalogClause } from "roamjs-components/types"; +import { + DatalogAndClause, + DatalogOrClause, + DatalogClause, +} from "roamjs-components/types"; import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid"; import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle"; import getSubTree from "roamjs-components/util/getSubTree"; @@ -19,10 +23,24 @@ import replaceDatalogVariables from "./replaceDatalogVariables"; import parseQuery from "./parseQuery"; import { fireQuerySync, getWhereClauses } from "./fireQuery"; import { toVar } from "./compileDatalog"; +import { getSetting } from "./extensionSettings"; +import { getExistingRelationPageUid } from "./createReifiedBlock"; const hasTag = (node: DiscourseNode): node is DiscourseNode & { tag: string } => !!node.tag; +const singleClause = ( + clauses: DatalogClause[], + orClause?: boolean, +): DatalogClause | null => { + if (clauses.length === 0) return null; + if (clauses.length === 1) return clauses[0]; + return { + type: orClause ? "or-clause" : "and-clause", + clauses: clauses, + }; +}; + const collectVariables = ( clauses: (DatalogClause | DatalogAndClause)[], ): Set => @@ -372,6 +390,12 @@ const registerDiscourseDatalogTranslators = () => { registerDatalogTranslator({ key: label, callback: ({ source, target, uid }) => { + const useReifiedRelations = getSetting( + "use-reified-relations", + false, + ); + const relationPageUid = getExistingRelationPageUid(); + const filteredRelations = getFilteredRelations({ discourseRelations, label, @@ -379,210 +403,458 @@ const registerDiscourseDatalogTranslators = () => { target, }); if (!filteredRelations.length) return []; - const andParts = filteredRelations.map( - ({ - triples, - forward, - source: relationSource, - destination: relationTarget, - }) => { - const sourceTriple = triples.find((t) => t[2] === "source"); - const targetTriple = triples.find( - (t) => t[2] === "destination" || t[2] === "target", - ); - if (!sourceTriple || !targetTriple) return []; - const computeEdgeTriple = ({ - nodeType, - value, - triple, - }: { - nodeType: string; - value: string; - triple: readonly [string, string, string]; - }): DatalogClause[] => { - const possibleNodeType = nodeTypeByLabel[value?.toLowerCase()]; - if (possibleNodeType) { - return conditionToDatalog({ - uid, - not: false, - target: possibleNodeType, - relation: "is a", - source: triple[0], - type: "clause", - }); - } else if ( - !!window.roamAlphaAPI.pull("[:db/id]", [":block/uid", value]) - ) { - return [ - { - type: "data-pattern", + + if (useReifiedRelations && relationPageUid !== undefined) { + const relClauseBasis: DatalogClause[] = [ + { + type: "data-pattern", + arguments: [ + { type: "variable", value: "relpage" }, + { type: "constant", value: ":block/uid" }, + { type: "constant", value: `"${relationPageUid}"` }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: "rel" }, + { type: "constant", value: ":block/page" }, + { type: "variable", value: "relpage" }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: "rel" }, + { type: "constant", value: ":block/props" }, + { type: "variable", value: "relprops" }, + ], + }, + { + type: "fn-expr", + fn: "get", + arguments: [ + { type: "variable", value: "relprops" }, + { type: "constant", value: `:discourse-graph` }, + ], + binding: { + type: "bind-scalar", + variable: { type: "variable", value: "reldata" }, + }, + }, + { + type: "fn-expr", + fn: "get", + arguments: [ + { type: "variable", value: "reldata" }, + { type: "constant", value: ":hasSchema" }, + ], + binding: { + type: "bind-scalar", + variable: { type: "variable", value: "relSchema" }, + }, + }, + { + type: "fn-expr", + fn: "get", + arguments: [ + { type: "variable", value: "reldata" }, + { type: "constant", value: ":sourceUid" }, + ], + binding: { + type: "bind-scalar", + variable: { type: "variable", value: "relSource" }, + }, + }, + { + type: "fn-expr", + fn: "get", + arguments: [ + { type: "variable", value: "reldata" }, + { type: "constant", value: ":destinationUid" }, + ], + binding: { + type: "bind-scalar", + variable: { type: "variable", value: "relTarget" }, + }, + }, + ]; + const clauses: DatalogClause[] = [...relClauseBasis]; + const sourceAsUid = window.roamAlphaAPI.pull("[:db/id]", [ + ":block/uid", + source, + ]) + ? source + : null; + const targetAsUid = window.roamAlphaAPI.pull("[:db/id]", [ + ":block/uid", + target, + ]) + ? target + : null; + + const forwardClauses: DatalogClause[] = []; + const reverseClauses: DatalogClause[] = []; + if (sourceAsUid) { + forwardClauses.push({ + type: "pred-expr", + pred: "=", + arguments: [ + { type: "variable", value: "relSource" }, + { type: "constant", value: `"${sourceAsUid}"` }, + ], + }); + reverseClauses.push({ + type: "pred-expr", + pred: "=", + arguments: [ + { type: "variable", value: "relTarget" }, + { type: "constant", value: `"${sourceAsUid}"` }, + ], + }); + if (targetAsUid) { + console.warn("Should both ends be Uids?"); + } else { + forwardClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: target }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relTarget" }, + ], + }); + reverseClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: target }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relSource" }, + ], + }); + } + } + if (targetAsUid) { + forwardClauses.push({ + type: "pred-expr", + pred: "=", + arguments: [ + { type: "variable", value: "relTarget" }, + { type: "constant", value: `"${targetAsUid}"` }, + ], + }); + reverseClauses.push({ + type: "pred-expr", + pred: "=", + arguments: [ + { type: "variable", value: "relSource" }, + { type: "constant", value: `"${targetAsUid}"` }, + ], + }); + if (sourceAsUid) { + console.warn("Should both ends be Uids?"); + } else { + forwardClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: source }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relSource" }, + ], + }); + reverseClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: source }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relTarget" }, + ], + }); + } + } + + const forwardClause = singleClause(forwardClauses)!; + const reverseClause = singleClause(reverseClauses)!; + if (ANY_RELATION_REGEX.test(label)) { + clauses.push({ + type: "or-clause", + clauses: [forwardClause, reverseClause], + }); + } else { + const forwardRelationClauses = filteredRelations + .filter((r) => r.forward) + .map( + (r) => + ({ + type: "pred-expr", + pred: "=", arguments: [ - { type: "variable", value: triple[0] }, - { type: "constant", value: ":block/uid" }, - { type: "constant", value: `"${value}"` }, + { type: "variable", value: "relSchema" }, + { type: "constant", value: `"${r.id}"` }, ], - }, - ]; - } else if ( - value?.toLowerCase() !== "node" && - !!window.roamAlphaAPI.pull("[:db/id]", [":node/title", value]) - ) { - return conditionToDatalog({ - uid, - not: false, - target: value, - relation: "has title", - source: triple[0], - type: "clause", - }); - } else { - return conditionToDatalog({ - uid, - not: false, - target: nodeType, - relation: "is a", - source: triple[0], - type: "clause", - }); - } - }; - const edgeTriples = forward - ? computeEdgeTriple({ - value: source, - triple: sourceTriple, - nodeType: relationSource, - }) - .concat( - computeEdgeTriple({ - value: target, - triple: targetTriple, - nodeType: relationTarget, - }), - ) - .concat([ - { - type: "data-pattern", - arguments: [ - { type: "variable", value: sourceTriple[0] }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${source}-uid` }, - ], - }, - { - type: "data-pattern", - arguments: [ - { type: "variable", value: source }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${source}-uid` }, - ], - }, - { - type: "data-pattern", - arguments: [ - { type: "variable", value: targetTriple[0] }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${target}-uid` }, - ], - }, + }) as DatalogClause, + ); + const reverseRelationClauses = filteredRelations + .filter((r) => !r.forward) + .map( + (r) => + ({ + type: "pred-expr", + pred: "=", + arguments: [ + { type: "variable", value: "relSchema" }, + { type: "constant", value: `"${r.id}"` }, + ], + }) as DatalogClause, + ); + if ( + forwardRelationClauses.length && + reverseRelationClauses.length + ) { + const relClauses = [ + singleClause([ + ...forwardClauses, + singleClause(forwardRelationClauses, true)!, + ])!, + singleClause([ + ...reverseClauses, + singleClause(reverseRelationClauses, true)!, + ])!, + ]; + clauses.push(singleClause(relClauses, true)!); + } else if (forwardRelationClauses.length) { + clauses.push( + ...forwardClauses, + singleClause(forwardRelationClauses, true)!, + ); + } else if (reverseRelationClauses.length) { + clauses.push( + ...reverseClauses, + singleClause(reverseRelationClauses, true)!, + ); + } + } + + return filteredRelations.length > 1 + ? replaceDatalogVariables( + [ + { from: source, to: source }, + { from: target, to: target }, + { from: true, to: (v) => `${uid}-${v}` }, + ], + clauses, + ) + : clauses; + } else { + const andParts = filteredRelations.map( + ({ + triples, + forward, + source: relationSource, + destination: relationTarget, + }) => { + const sourceTriple = triples.find((t) => t[2] === "source"); + const targetTriple = triples.find( + (t) => t[2] === "destination" || t[2] === "target", + ); + if (!sourceTriple || !targetTriple) return []; + const computeEdgeTriple = ({ + nodeType, + value, + triple, + }: { + nodeType: string; + value: string; + triple: readonly [string, string, string]; + }): DatalogClause[] => { + const possibleNodeType = + nodeTypeByLabel[value?.toLowerCase()]; + if (possibleNodeType) { + return conditionToDatalog({ + uid, + not: false, + target: possibleNodeType, + relation: "is a", + source: triple[0], + type: "clause", + }); + } else if ( + window.roamAlphaAPI.pull("[:db/id]", [":block/uid", value]) + ) { + return [ { type: "data-pattern", arguments: [ - { type: "variable", value: target }, + { type: "variable", value: triple[0] }, { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${target}-uid` }, + { type: "constant", value: `"${value}"` }, ], }, + ]; + } else if ( + value?.toLowerCase() !== "node" && + !!window.roamAlphaAPI.pull("[:db/id]", [ + ":node/title", + value, ]) - : computeEdgeTriple({ - value: target, - triple: sourceTriple, - nodeType: relationSource, - }) - .concat( - computeEdgeTriple({ - value: source, - triple: targetTriple, - nodeType: relationTarget, - }), - ) - .concat([ - { - type: "data-pattern", - arguments: [ - { type: "variable", value: targetTriple[0] }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${source}-uid` }, - ], - }, - { - type: "data-pattern", - arguments: [ - { type: "variable", value: source }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${source}-uid` }, - ], - }, - { - type: "data-pattern", - arguments: [ - { type: "variable", value: sourceTriple[0] }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${target}-uid` }, - ], - }, - { - type: "data-pattern", - arguments: [ - { type: "variable", value: target }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: `${target}-uid` }, - ], - }, - ]); - const subQuery = triples - .filter((t) => t !== sourceTriple && t !== targetTriple) - .flatMap(([src, rel, tar]) => - conditionToDatalog({ - source: src, - relation: rel, - target: tar, - not: false, - uid, - type: "clause", - }), + ) { + return conditionToDatalog({ + uid, + not: false, + target: value, + relation: "has title", + source: triple[0], + type: "clause", + }); + } else { + return conditionToDatalog({ + uid, + not: false, + target: nodeType, + relation: "is a", + source: triple[0], + type: "clause", + }); + } + }; + + const edgeTriples = forward + ? computeEdgeTriple({ + value: source, + triple: sourceTriple, + nodeType: relationSource, + }) + .concat( + computeEdgeTriple({ + value: target, + triple: targetTriple, + nodeType: relationTarget, + }), + ) + .concat([ + { + type: "data-pattern", + arguments: [ + { type: "variable", value: sourceTriple[0] }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${source}-uid` }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: source }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${source}-uid` }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: targetTriple[0] }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${target}-uid` }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: target }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${target}-uid` }, + ], + }, + ]) + : computeEdgeTriple({ + value: target, + triple: sourceTriple, + nodeType: relationSource, + }) + .concat( + computeEdgeTriple({ + value: source, + triple: targetTriple, + nodeType: relationTarget, + }), + ) + .concat([ + { + type: "data-pattern", + arguments: [ + { type: "variable", value: targetTriple[0] }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${source}-uid` }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: source }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${source}-uid` }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: sourceTriple[0] }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${target}-uid` }, + ], + }, + { + type: "data-pattern", + arguments: [ + { type: "variable", value: target }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: `${target}-uid` }, + ], + }, + ]); + const subQuery = triples + .filter((t) => t !== sourceTriple && t !== targetTriple) + .flatMap(([src, rel, tar]) => + conditionToDatalog({ + source: src, + relation: rel, + target: tar, + not: false, + uid, + type: "clause", + }), + ); + return replaceDatalogVariables( + [ + { from: source, to: source }, + { from: target, to: target }, + { from: true, to: (v) => `${uid}-${v}` }, + ], + edgeTriples.concat(subQuery), ); - return replaceDatalogVariables( - [ - { from: source, to: source }, - { from: target, to: target }, - { from: true, to: (v) => `${uid}-${v}` }, - ], - edgeTriples.concat(subQuery), - ); - }, - ); - if (andParts.length === 1) return andParts[0]; + }, + ); + if (andParts.length === 1) return andParts[0]; - const orJoinedVars = collectVariables(andParts[0]); - andParts.slice(1).forEach((a) => { - const freeVars = collectVariables(a); - Array.from(orJoinedVars).forEach((v) => { - if (!freeVars.has(v)) orJoinedVars.delete(v); + const orJoinedVars = collectVariables(andParts[0]); + andParts.slice(1).forEach((a) => { + const freeVars = collectVariables(a); + Array.from(orJoinedVars).forEach((v) => { + if (!freeVars.has(v)) orJoinedVars.delete(v); + }); }); - }); - return [ - { - type: "or-join-clause", - variables: Array.from(orJoinedVars).map((v) => ({ - type: "variable", - value: v, - })), - clauses: andParts.map((a) => ({ - type: "and-clause", - clauses: a, - })), - }, - ]; + return [ + { + type: "or-join-clause", + variables: Array.from(orJoinedVars).map((v) => ({ + type: "variable", + value: v, + })), + clauses: andParts.map((a) => ({ + type: "and-clause", + clauses: a, + })), + }, + ]; + } }, targetOptions: () => { const allRelations = discourseRelations.flatMap((dr) => [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31da40a9e..d2e66963a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -279,8 +279,8 @@ importers: specifier: 3.5.2 version: 3.5.2(react@18.2.0) roamjs-components: - specifier: 0.85.4 - version: 0.85.4(8b55b1f14ffbf718c26dbfa3a20c2d6b) + specifier: 0.85.6 + version: 0.85.6(8b55b1f14ffbf718c26dbfa3a20c2d6b) tldraw: specifier: 2.3.0 version: 2.3.0(patch_hash=53157a9866fb748b22a548ab8d9aca2a557f98c0487b201b01f0a4ca89c71708)(@types/react-dom@18.2.17)(@types/react@18.2.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -7963,8 +7963,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - roamjs-components@0.85.4: - resolution: {integrity: sha512-1jKcPg3HXrX3+D6XEjf21IZVQ7IMPfdJHr3zffVX88zQLS4VmqeF0hcv95oAnts4NRRjrWu/kyG3apXwq6BFHw==} + roamjs-components@0.85.6: + resolution: {integrity: sha512-uE4QcwmbBZbLuq5fuYV8iSywDAb+VYw9IyS1rfiweUQxO+BttyAYNhGoCN/O2k3Bow0dhnFKTzPkFLElW6htmg==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} hasBin: true peerDependencies: @@ -18126,7 +18126,7 @@ snapshots: dependencies: glob: 7.2.3 - roamjs-components@0.85.4(8b55b1f14ffbf718c26dbfa3a20c2d6b): + roamjs-components@0.85.6(8b55b1f14ffbf718c26dbfa3a20c2d6b): dependencies: '@blueprintjs/core': 3.50.4(patch_hash=51c5847e0a73a1be0cc263036ff64d8fada46f3b65831ed938dbca5eecf3edc0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@blueprintjs/datetime': 3.23.14(react-dom@18.2.0(react@18.2.0))(react@18.2.0) From debdc4dde77c5539cd58b7b23ab69adcb314dfc7 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 12 Nov 2025 17:02:16 -0500 Subject: [PATCH 2/8] Optimize on multiple forms of a single relation. Update the optimizer some more. --- apps/roam/src/utils/fireQuery.ts | 20 ++++-- .../registerDiscourseDatalogTranslators.ts | 64 ++++++++++--------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/apps/roam/src/utils/fireQuery.ts b/apps/roam/src/utils/fireQuery.ts index e78b4e96e..81ce1991f 100644 --- a/apps/roam/src/utils/fireQuery.ts +++ b/apps/roam/src/utils/fireQuery.ts @@ -65,9 +65,9 @@ const firstVariable = ( }; const optimizeQuery = ( - clauses: (DatalogClause | DatalogAndClause)[], + clauses: DatalogClause[], capturedVariables: Set, -): (DatalogClause | DatalogAndClause)[] => { +): DatalogClause[] => { const marked = clauses.map(() => false); const orderedClauses: (DatalogClause | DatalogAndClause)[] = []; const variablesByIndex: Record> = {}; @@ -126,7 +126,8 @@ const optimizeQuery = ( (a) => a.type !== "variable" || capturedVariables.has(a.value), ) ) { - score = 1000; + // equality is almost as good as a binding + c.type == "pred-expr" && c.pred == "=" ? (score = 5) : (score = 1000); } else { score = 100004; } @@ -156,10 +157,17 @@ const optimizeQuery = ( bestClause.arguments .filter((v) => v.type === "variable") .forEach((v) => capturedVariables.add(v.value)); + } else if (bestClause.type === "fn-expr") { + // A function expression acts as biding a variable to a unique function value + if ( + bestClause.arguments.filter( + (a) => a.type === "variable" && !capturedVariables.has(a.value), + ).length === 0 && + bestClause.binding.type === "bind-scalar" && + bestClause.binding.variable.type === "variable" + ) + capturedVariables.add(bestClause.binding.variable.value); } - // Question: Should we not consider all variables in a complex clause captured? - // const newVars = gatherDatalogVariablesFromClause(bestClause); - // newVars.forEach((v) => capturedVariables.add(v)); } return orderedClauses; }; diff --git a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts index 2e52f61c6..12b3d5083 100644 --- a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts +++ b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts @@ -447,11 +447,11 @@ const registerDiscourseDatalogTranslators = () => { fn: "get", arguments: [ { type: "variable", value: "reldata" }, - { type: "constant", value: ":hasSchema" }, + { type: "constant", value: ":sourceUid" }, ], binding: { type: "bind-scalar", - variable: { type: "variable", value: "relSchema" }, + variable: { type: "variable", value: "relSource" }, }, }, { @@ -459,11 +459,11 @@ const registerDiscourseDatalogTranslators = () => { fn: "get", arguments: [ { type: "variable", value: "reldata" }, - { type: "constant", value: ":sourceUid" }, + { type: "constant", value: ":destinationUid" }, ], binding: { type: "bind-scalar", - variable: { type: "variable", value: "relSource" }, + variable: { type: "variable", value: "relTarget" }, }, }, { @@ -471,11 +471,11 @@ const registerDiscourseDatalogTranslators = () => { fn: "get", arguments: [ { type: "variable", value: "reldata" }, - { type: "constant", value: ":destinationUid" }, + { type: "constant", value: ":hasSchema" }, ], binding: { type: "bind-scalar", - variable: { type: "variable", value: "relTarget" }, + variable: { type: "variable", value: "relSchema" }, }, }, ]; @@ -580,32 +580,36 @@ const registerDiscourseDatalogTranslators = () => { clauses: [forwardClause, reverseClause], }); } else { - const forwardRelationClauses = filteredRelations + let forwardRelationIds = filteredRelations .filter((r) => r.forward) - .map( - (r) => - ({ - type: "pred-expr", - pred: "=", - arguments: [ - { type: "variable", value: "relSchema" }, - { type: "constant", value: `"${r.id}"` }, - ], - }) as DatalogClause, - ); - const reverseRelationClauses = filteredRelations + .map((r) => r.id); + forwardRelationIds = [...new Set(forwardRelationIds)]; + const forwardRelationClauses = forwardRelationIds.map( + (id) => + ({ + type: "pred-expr", + pred: "=", + arguments: [ + { type: "variable", value: "relSchema" }, + { type: "constant", value: `"${id}"` }, + ], + }) as DatalogClause, + ); + let reverseRelationIds = filteredRelations .filter((r) => !r.forward) - .map( - (r) => - ({ - type: "pred-expr", - pred: "=", - arguments: [ - { type: "variable", value: "relSchema" }, - { type: "constant", value: `"${r.id}"` }, - ], - }) as DatalogClause, - ); + .map((r) => r.id); + reverseRelationIds = [...new Set(reverseRelationIds)]; + const reverseRelationClauses = reverseRelationIds.map( + (id) => + ({ + type: "pred-expr", + pred: "=", + arguments: [ + { type: "variable", value: "relSchema" }, + { type: "constant", value: `"${id}"` }, + ], + }) as DatalogClause, + ); if ( forwardRelationClauses.length && reverseRelationClauses.length From 70eb391851a74848840468c34230f2ce3ccf2996 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 12 Nov 2025 18:57:16 -0500 Subject: [PATCH 3/8] sanitize parameter names --- apps/roam/src/utils/createReifiedBlock.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/roam/src/utils/createReifiedBlock.ts b/apps/roam/src/utils/createReifiedBlock.ts index 27afc9e09..df460b5a5 100644 --- a/apps/roam/src/utils/createReifiedBlock.ts +++ b/apps/roam/src/utils/createReifiedBlock.ts @@ -5,10 +5,19 @@ import { getSetting } from "~/utils/extensionSettings"; export const DISCOURSE_GRAPH_PROP_NAME = "discourse-graph"; +const SANE_ROLE_NAME_RE = new RegExp(/^[a-zA-Z][a-zA-Z0-9_-]*$/); + const strictQueryForReifiedBlocks = async ( parameterUids: Record, ): Promise => { const paramsAsSeq = Object.entries(parameterUids); + // sanitize parameter names + if ( + Object.keys(parameterUids).filter((k) => !k.match(SANE_ROLE_NAME_RE)).length + ) + throw new Error( + `invalid parameter names in ${Object.keys(parameterUids).join(", "),}` + ); const query = `[:find ?u ?d :in $ ${paramsAsSeq.map(([k]) => "?" + k).join(" ")} :where [?s :block/uid ?u] [?s :block/props ?p] [(get ?p :${DISCOURSE_GRAPH_PROP_NAME}) ?d] From fe452f46509e7e20c3cc87c883361434e2e0a92c Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Thu, 13 Nov 2025 08:11:36 -0500 Subject: [PATCH 4/8] typo --- apps/roam/src/utils/createReifiedBlock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/roam/src/utils/createReifiedBlock.ts b/apps/roam/src/utils/createReifiedBlock.ts index df460b5a5..4d5895c19 100644 --- a/apps/roam/src/utils/createReifiedBlock.ts +++ b/apps/roam/src/utils/createReifiedBlock.ts @@ -16,7 +16,7 @@ const strictQueryForReifiedBlocks = async ( Object.keys(parameterUids).filter((k) => !k.match(SANE_ROLE_NAME_RE)).length ) throw new Error( - `invalid parameter names in ${Object.keys(parameterUids).join(", "),}` + `invalid parameter names in ${Object.keys(parameterUids).join(", ")}`, ); const query = `[:find ?u ?d :in $ ${paramsAsSeq.map(([k]) => "?" + k).join(" ")} From 11144886775d8b58ec847dc7663472450e351e9b Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Thu, 13 Nov 2025 13:38:19 -0500 Subject: [PATCH 5/8] cleanup --- .../registerDiscourseDatalogTranslators.ts | 76 +++++++++++-------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts index 12b3d5083..e3d772f78 100644 --- a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts +++ b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts @@ -1,9 +1,5 @@ import getPageTitlesStartingWithPrefix from "roamjs-components/queries/getPageTitlesStartingWithPrefix"; -import { - DatalogAndClause, - DatalogOrClause, - DatalogClause, -} from "roamjs-components/types"; +import { DatalogClause } from "roamjs-components/types"; import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid"; import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle"; import getSubTree from "roamjs-components/util/getSubTree"; @@ -29,21 +25,37 @@ import { getExistingRelationPageUid } from "./createReifiedBlock"; const hasTag = (node: DiscourseNode): node is DiscourseNode & { tag: string } => !!node.tag; -const singleClause = ( +const singleOrClause = ( clauses: DatalogClause[], - orClause?: boolean, + variables?: string[], ): DatalogClause | null => { + if (clauses.length === 0) return null; + if (clauses.length === 1) return clauses[0]; + return variables && variables.length + ? { + type: "or-join-clause", + variables: variables.map((v: string) => ({ + type: "variable", + value: v, + })), + clauses: clauses, + } + : { + type: "or-clause", + clauses: clauses, + }; +}; + +const singleAndClause = (clauses: DatalogClause[]): DatalogClause | null => { if (clauses.length === 0) return null; if (clauses.length === 1) return clauses[0]; return { - type: orClause ? "or-clause" : "and-clause", + type: "and-clause", clauses: clauses, }; }; -const collectVariables = ( - clauses: (DatalogClause | DatalogAndClause)[], -): Set => +const collectVariables = (clauses: DatalogClause[]): Set => new Set( clauses.flatMap((c) => { switch (c.type) { @@ -402,7 +414,8 @@ const registerDiscourseDatalogTranslators = () => { source, target, }); - if (!filteredRelations.length) return []; + if (!filteredRelations.length && !ANY_RELATION_REGEX.test(label)) + return []; if (useReifiedRelations && relationPageUid !== undefined) { const relClauseBasis: DatalogClause[] = [ @@ -572,8 +585,8 @@ const registerDiscourseDatalogTranslators = () => { } } - const forwardClause = singleClause(forwardClauses)!; - const reverseClause = singleClause(reverseClauses)!; + const forwardClause = singleAndClause(forwardClauses)!; + const reverseClause = singleAndClause(reverseClauses)!; if (ANY_RELATION_REGEX.test(label)) { clauses.push({ type: "or-clause", @@ -615,39 +628,38 @@ const registerDiscourseDatalogTranslators = () => { reverseRelationClauses.length ) { const relClauses = [ - singleClause([ + singleAndClause([ ...forwardClauses, - singleClause(forwardRelationClauses, true)!, + singleOrClause(forwardRelationClauses)!, ])!, - singleClause([ + singleAndClause([ ...reverseClauses, - singleClause(reverseRelationClauses, true)!, + singleOrClause(reverseRelationClauses)!, ])!, ]; - clauses.push(singleClause(relClauses, true)!); + clauses.push(singleOrClause(relClauses)!); } else if (forwardRelationClauses.length) { clauses.push( ...forwardClauses, - singleClause(forwardRelationClauses, true)!, + singleOrClause(forwardRelationClauses)!, ); } else if (reverseRelationClauses.length) { clauses.push( ...reverseClauses, - singleClause(reverseRelationClauses, true)!, + singleOrClause(reverseRelationClauses)!, ); } } - - return filteredRelations.length > 1 - ? replaceDatalogVariables( - [ - { from: source, to: source }, - { from: target, to: target }, - { from: true, to: (v) => `${uid}-${v}` }, - ], - clauses, - ) - : clauses; + return replaceDatalogVariables( + [ + { from: source, to: source }, + { from: target, to: target }, + { from: "relSchema", to: "relSchema" }, + { from: "relSource", to: "relSource" }, + { from: true, to: (v) => `${uid}-${v}` }, + ], + clauses, + ); } else { const andParts = filteredRelations.map( ({ From e73fb4e61b2dfd0e9ad150d85317badece3e1719 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Sun, 16 Nov 2025 11:05:55 -0500 Subject: [PATCH 6/8] generalize, include title and node type constraints --- .../registerDiscourseDatalogTranslators.ts | 366 ++++++++++++------ 1 file changed, 242 insertions(+), 124 deletions(-) diff --git a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts index e3d772f78..7ce329239 100644 --- a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts +++ b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts @@ -25,6 +25,13 @@ import { getExistingRelationPageUid } from "./createReifiedBlock"; const hasTag = (node: DiscourseNode): node is DiscourseNode & { tag: string } => !!node.tag; +const enum ValueType { + "variable", + "nodeType", + "uid", + "title", +} + const singleOrClause = ( clauses: DatalogClause[], variables?: string[], @@ -416,6 +423,77 @@ const registerDiscourseDatalogTranslators = () => { }); if (!filteredRelations.length && !ANY_RELATION_REGEX.test(label)) return []; + console.log("source", source, "target", target); + const typeOfValue = (value: string): ValueType => { + const possibleNodeType = nodeTypeByLabel[value?.toLowerCase()]; + if (possibleNodeType) { + return ValueType.nodeType; + } else if ( + window.roamAlphaAPI.pull("[:db/id]", [":block/uid", value]) + ) { + return ValueType.uid; + } else if ( + value?.toLowerCase() !== "node" && + !!window.roamAlphaAPI.pull("[:db/id]", [":node/title", value]) + ) { + return ValueType.title; + } else { + return ValueType.variable; + } + }; + const computeEdgeTriple = ({ + variable, + value, + nodeType, + valueType, + }: { + variable: string; + value: string; + nodeType: string; + valueType?: ValueType; + }): DatalogClause[] => { + valueType = valueType || typeOfValue(value); + switch (valueType) { + case ValueType.nodeType: + return conditionToDatalog({ + uid, + not: false, + source: variable, + relation: "is a", + target: nodeTypeByLabel[value?.toLowerCase()], + type: "clause", + }); + case ValueType.uid: + return [ + { + type: "data-pattern", + arguments: [ + { type: "variable", value: variable }, + { type: "constant", value: ":block/uid" }, + { type: "constant", value: `"${value}"` }, + ], + }, + ]; + case ValueType.title: + return conditionToDatalog({ + uid, + not: false, + source: variable, + relation: "has title", + target: value, + type: "clause", + }); + case ValueType.variable: + return conditionToDatalog({ + uid, + not: false, + source: variable, + relation: "is a", + target: nodeType, + type: "clause", + }); + } + }; if (useReifiedRelations && relationPageUid !== undefined) { const relClauseBasis: DatalogClause[] = [ @@ -492,29 +570,62 @@ const registerDiscourseDatalogTranslators = () => { }, }, ]; + const typeOfSource = typeOfValue(source); + const typeOfTarget = typeOfValue(target); const clauses: DatalogClause[] = [...relClauseBasis]; - const sourceAsUid = window.roamAlphaAPI.pull("[:db/id]", [ - ":block/uid", - source, - ]) - ? source - : null; - const targetAsUid = window.roamAlphaAPI.pull("[:db/id]", [ - ":block/uid", - target, - ]) - ? target - : null; + // todo: It could be a title or a node type. + console.log( + "typeOfSource", + typeOfSource, + "typeOfTarget", + typeOfTarget, + ); + if ( + !( + typeOfSource <= ValueType.nodeType || + typeOfTarget <= ValueType.nodeType + ) + ) { + console.error( + `One of source: ${source} or target: ${target} should be a variable (or node type)`, + ); + } const forwardClauses: DatalogClause[] = []; const reverseClauses: DatalogClause[] = []; - if (sourceAsUid) { + const sourceTriples = filteredRelations + .map((r) => r.triples.find((t) => t[2] === "source")) + .filter((x) => x !== undefined); + const targetTriples = filteredRelations + .map((r) => + r.triples.find( + (t) => t[2] === "destination" || t[2] === "target", + ), + ) + .filter((x) => x !== undefined); + console.log( + "sourceTriples", + sourceTriples, + "targetTriples", + targetTriples, + ); + if (typeOfSource === ValueType.uid) { + if (sourceTriples.length) + clauses.push( + ...computeEdgeTriple({ + variable: sourceTriples[0][0], + value: source, + valueType: ValueType.uid, + nodeType: "Any", + }), + ); + else console.error("Cannot find the source triple"); forwardClauses.push({ type: "pred-expr", pred: "=", arguments: [ { type: "variable", value: "relSource" }, - { type: "constant", value: `"${sourceAsUid}"` }, + { type: "constant", value: `"${source}"` }, ], }); reverseClauses.push({ @@ -522,37 +633,75 @@ const registerDiscourseDatalogTranslators = () => { pred: "=", arguments: [ { type: "variable", value: "relTarget" }, - { type: "constant", value: `"${sourceAsUid}"` }, + { type: "constant", value: `"${source}"` }, ], }); - if (targetAsUid) { - console.warn("Should both ends be Uids?"); - } else { - forwardClauses.push({ - type: "data-pattern", - arguments: [ - { type: "variable", value: target }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: "relTarget" }, - ], - }); - reverseClauses.push({ - type: "data-pattern", - arguments: [ - { type: "variable", value: target }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: "relSource" }, - ], - }); + } else { + const sourceVarName = + typeOfSource === ValueType.title ? "sourcePage" : source; + switch (typeOfSource) { + case ValueType.title: + clauses.push( + ...computeEdgeTriple({ + variable: sourceVarName, + value: source, + valueType: ValueType.title, + nodeType: "Any", + }), + ); + break; + case ValueType.nodeType: + // not sure about using the source as both variable and value but works + clauses.push( + ...computeEdgeTriple({ + variable: sourceVarName, // equal to source in this case... + value: source, + valueType: ValueType.nodeType, + nodeType: source, + }), + ); + break; + case ValueType.variable: + // we used to put the nodeType in the or-clause, but I think with reified triples it's unneeded. + break; + default: + console.error("Wrong typeOfSource value"); } + + forwardClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: sourceVarName }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relSource" }, + ], + }); + reverseClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: sourceVarName }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relTarget" }, + ], + }); } - if (targetAsUid) { + if (typeOfTarget === ValueType.uid) { + if (targetTriples.length) + clauses.push( + ...computeEdgeTriple({ + variable: targetTriples[0][0], + valueType: ValueType.uid, + value: target, + nodeType: "Any", + }), + ); + else console.error("Cannot find the target triple"); forwardClauses.push({ type: "pred-expr", pred: "=", arguments: [ { type: "variable", value: "relTarget" }, - { type: "constant", value: `"${targetAsUid}"` }, + { type: "constant", value: `"${target}"` }, ], }); reverseClauses.push({ @@ -560,37 +709,66 @@ const registerDiscourseDatalogTranslators = () => { pred: "=", arguments: [ { type: "variable", value: "relSource" }, - { type: "constant", value: `"${targetAsUid}"` }, + { type: "constant", value: `"${target}"` }, ], }); - if (sourceAsUid) { - console.warn("Should both ends be Uids?"); - } else { - forwardClauses.push({ - type: "data-pattern", - arguments: [ - { type: "variable", value: source }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: "relSource" }, - ], - }); - reverseClauses.push({ - type: "data-pattern", - arguments: [ - { type: "variable", value: source }, - { type: "constant", value: ":block/uid" }, - { type: "variable", value: "relTarget" }, - ], - }); + } else { + const targetVarName = + typeOfTarget === ValueType.title ? "targetPage" : target; + switch (typeOfTarget) { + case ValueType.title: + clauses.push( + ...computeEdgeTriple({ + variable: targetVarName, + value: target, + valueType: ValueType.title, + nodeType: "Any", + }), + ); + break; + case ValueType.nodeType: + // not sure about using the source as both variable and value but works + clauses.push( + ...computeEdgeTriple({ + variable: targetVarName, // equal to target in this case... + value: target, + valueType: ValueType.nodeType, + nodeType: target, + }), + ); + break; + case ValueType.variable: + // we used to put the nodeType in the or-clause, but I think with reified triples it's unneeded. + break; + default: + console.error("Wrong typeOfTarget value"); } + + forwardClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: targetVarName }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relTarget" }, + ], + }); + reverseClauses.push({ + type: "data-pattern", + arguments: [ + { type: "variable", value: targetVarName }, + { type: "constant", value: ":block/uid" }, + { type: "variable", value: "relSource" }, + ], + }); } - const forwardClause = singleAndClause(forwardClauses)!; - const reverseClause = singleAndClause(reverseClauses)!; if (ANY_RELATION_REGEX.test(label)) { clauses.push({ type: "or-clause", - clauses: [forwardClause, reverseClause], + clauses: [ + singleAndClause(forwardClauses)!, + singleAndClause(reverseClauses)!, + ], }); } else { let forwardRelationIds = filteredRelations @@ -654,13 +832,12 @@ const registerDiscourseDatalogTranslators = () => { [ { from: source, to: source }, { from: target, to: target }, - { from: "relSchema", to: "relSchema" }, - { from: "relSource", to: "relSource" }, { from: true, to: (v) => `${uid}-${v}` }, ], clauses, ); } else { + // Pattern-based search const andParts = filteredRelations.map( ({ triples, @@ -673,76 +850,17 @@ const registerDiscourseDatalogTranslators = () => { (t) => t[2] === "destination" || t[2] === "target", ); if (!sourceTriple || !targetTriple) return []; - const computeEdgeTriple = ({ - nodeType, - value, - triple, - }: { - nodeType: string; - value: string; - triple: readonly [string, string, string]; - }): DatalogClause[] => { - const possibleNodeType = - nodeTypeByLabel[value?.toLowerCase()]; - if (possibleNodeType) { - return conditionToDatalog({ - uid, - not: false, - target: possibleNodeType, - relation: "is a", - source: triple[0], - type: "clause", - }); - } else if ( - window.roamAlphaAPI.pull("[:db/id]", [":block/uid", value]) - ) { - return [ - { - type: "data-pattern", - arguments: [ - { type: "variable", value: triple[0] }, - { type: "constant", value: ":block/uid" }, - { type: "constant", value: `"${value}"` }, - ], - }, - ]; - } else if ( - value?.toLowerCase() !== "node" && - !!window.roamAlphaAPI.pull("[:db/id]", [ - ":node/title", - value, - ]) - ) { - return conditionToDatalog({ - uid, - not: false, - target: value, - relation: "has title", - source: triple[0], - type: "clause", - }); - } else { - return conditionToDatalog({ - uid, - not: false, - target: nodeType, - relation: "is a", - source: triple[0], - type: "clause", - }); - } - }; const edgeTriples = forward ? computeEdgeTriple({ value: source, - triple: sourceTriple, + variable: sourceTriple[0], nodeType: relationSource, }) .concat( computeEdgeTriple({ value: target, - triple: targetTriple, + variable: targetTriple[0], nodeType: relationTarget, }), ) @@ -781,14 +899,14 @@ const registerDiscourseDatalogTranslators = () => { }, ]) : computeEdgeTriple({ + variable: sourceTriple[0], value: target, - triple: sourceTriple, nodeType: relationSource, }) .concat( computeEdgeTriple({ + variable: targetTriple[0], value: source, - triple: targetTriple, nodeType: relationTarget, }), ) From 3019445d5ee1d70f4d08a001a9f8b3596217611f Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Sat, 22 Nov 2025 17:01:33 -0500 Subject: [PATCH 7/8] minor adjustments --- apps/roam/src/utils/createReifiedBlock.ts | 6 +++--- .../utils/registerDiscourseDatalogTranslators.ts | 13 ------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/apps/roam/src/utils/createReifiedBlock.ts b/apps/roam/src/utils/createReifiedBlock.ts index 4d5895c19..621a20021 100644 --- a/apps/roam/src/utils/createReifiedBlock.ts +++ b/apps/roam/src/utils/createReifiedBlock.ts @@ -5,13 +5,13 @@ import { getSetting } from "~/utils/extensionSettings"; export const DISCOURSE_GRAPH_PROP_NAME = "discourse-graph"; -const SANE_ROLE_NAME_RE = new RegExp(/^[a-zA-Z][a-zA-Z0-9_-]*$/); +const SANE_ROLE_NAME_RE = new RegExp(/^[\w\-]*$/); const strictQueryForReifiedBlocks = async ( parameterUids: Record, ): Promise => { const paramsAsSeq = Object.entries(parameterUids); - // sanitize parameter names + // validate parameter names if ( Object.keys(parameterUids).filter((k) => !k.match(SANE_ROLE_NAME_RE)).length ) @@ -81,7 +81,7 @@ const createReifiedBlock = async ({ const RELATION_PAGE_TITLE = "roam/js/discourse-graph/relations"; let relationPageUid: string | undefined = undefined; -export const getOrCreateRelationPageUid = async (): Promise => { +const getOrCreateRelationPageUid = async (): Promise => { if (relationPageUid === undefined) { relationPageUid = getPageUidByPageTitle(RELATION_PAGE_TITLE); if (relationPageUid === "") { diff --git a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts index 7ce329239..c0a7351db 100644 --- a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts +++ b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts @@ -423,7 +423,6 @@ const registerDiscourseDatalogTranslators = () => { }); if (!filteredRelations.length && !ANY_RELATION_REGEX.test(label)) return []; - console.log("source", source, "target", target); const typeOfValue = (value: string): ValueType => { const possibleNodeType = nodeTypeByLabel[value?.toLowerCase()]; if (possibleNodeType) { @@ -575,12 +574,6 @@ const registerDiscourseDatalogTranslators = () => { const clauses: DatalogClause[] = [...relClauseBasis]; // todo: It could be a title or a node type. - console.log( - "typeOfSource", - typeOfSource, - "typeOfTarget", - typeOfTarget, - ); if ( !( typeOfSource <= ValueType.nodeType || @@ -603,12 +596,6 @@ const registerDiscourseDatalogTranslators = () => { ), ) .filter((x) => x !== undefined); - console.log( - "sourceTriples", - sourceTriples, - "targetTriples", - targetTriples, - ); if (typeOfSource === ValueType.uid) { if (sourceTriples.length) clauses.push( From e5f66c15445196a25a6697c88a5dabff35f63dd6 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Mon, 24 Nov 2025 09:08:11 -0500 Subject: [PATCH 8/8] extra todo --- apps/roam/src/utils/registerDiscourseDatalogTranslators.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts index c0a7351db..fafb2bdcb 100644 --- a/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts +++ b/apps/roam/src/utils/registerDiscourseDatalogTranslators.ts @@ -573,7 +573,6 @@ const registerDiscourseDatalogTranslators = () => { const typeOfTarget = typeOfValue(target); const clauses: DatalogClause[] = [...relClauseBasis]; - // todo: It could be a title or a node type. if ( !( typeOfSource <= ValueType.nodeType ||