diff --git a/apps/roam/src/utils/fireQuery.ts b/apps/roam/src/utils/fireQuery.ts index d07f03cfa..a922f43d9 100644 --- a/apps/roam/src/utils/fireQuery.ts +++ b/apps/roam/src/utils/fireQuery.ts @@ -14,6 +14,7 @@ import predefinedSelections, { import { DEFAULT_RETURN_NODE } from "./parseQuery"; import { DiscourseNode } from "./getDiscourseNodes"; import { DiscourseRelation } from "./getDiscourseRelations"; +import type { json } from "./getBlockProps"; import nanoid from "nanoid"; export type QueryArgs = { @@ -30,6 +31,7 @@ type RelationInQuery = { export type FireQueryArgs = QueryArgs & { isCustomEnabled?: boolean; customNode?: string; + local?: boolean; context?: { relationsInQuery?: RelationInQuery[]; customNodes?: DiscourseNode[]; @@ -314,8 +316,30 @@ export const fireQuerySync = (args: FireQueryArgs): QueryResult[] => { })); }; +const PROP_NAME_RE = /:\w+\/\w+\b/g; + +const renamePropsInResult = ( + result: json | null, + mapping: Record, +): json | null => { + const rename = (x: json | null): json | null => { + if (Array.isArray(x)) return x.map(rename); + if (x === null || x === undefined) return x; + if (typeof x === "object") { + return Object.fromEntries( + Object.entries(x as object).map(([k, v]) => [ + mapping[k] || k, + rename(v), + ]), + ); + } + return x; + }; + return rename(result); +}; + const fireQuery: FireQuery = async (_args) => { - const { isCustomEnabled, customNode, ...args } = _args; + const { isCustomEnabled, customNode, local, ...args } = _args; const { query, formatResult, inputs } = isCustomEnabled ? { @@ -347,11 +371,31 @@ const fireQuery: FireQuery = async (_args) => { console.groupEnd(); } - //@ts-ignore - todo add async q to roamjs-components - const queryResults = await window.roamAlphaAPI.data.backend.q( - query, - ...inputs, - ); + let queryResults: unknown[][] = []; + if (local) { + // look for propNames in query. Could consider looking only in pull when that exists. + const propNames = new Set( + [...query.matchAll(PROP_NAME_RE)].map((m) => m[0]), + ); + const propNamesSub: Record = Object.fromEntries( + [...propNames].map((n) => [n.split("/")[1], n]), + ); + if (Object.keys(propNamesSub).length === propNames.size) { + // no name conflict, safe to use async query + // BUT it returns non-namespaced names, so substitute prop names back + queryResults = await window.roamAlphaAPI.data.async.q(query, ...inputs); + queryResults = renamePropsInResult( + queryResults as json, + propNamesSub, + ) as unknown[][]; + } else { + // more janky but safer + queryResults = window.roamAlphaAPI.data.fast.q(query, ...inputs); + } + } else { + // eslint-disable-next-line @typescript-eslint/await-thenable + queryResults = await window.roamAlphaAPI.data.backend.q(query, ...inputs); // ts-ignore + } if (nodeEnv === "development") { console.timeEnd(`Query - ${queryId}`); diff --git a/apps/roam/src/utils/getExportTypes.ts b/apps/roam/src/utils/getExportTypes.ts index 8f38724c8..eb582f270 100644 --- a/apps/roam/src/utils/getExportTypes.ts +++ b/apps/roam/src/utils/getExportTypes.ts @@ -374,7 +374,7 @@ const getExportTypes = ({ ); }; const getRelationData = () => - getRelationDataUtil(allRelations, nodeLabelByType); + getRelationDataUtil({ allRelations, nodeLabelByType, local: true }); const getJsonData = async () => { const grammar = allRelations.map(({ label, destination, source }) => ({ diff --git a/apps/roam/src/utils/getRelationData.ts b/apps/roam/src/utils/getRelationData.ts index 199110f72..a3ef03ee7 100644 --- a/apps/roam/src/utils/getRelationData.ts +++ b/apps/roam/src/utils/getRelationData.ts @@ -5,10 +5,15 @@ import type { DiscourseRelation } from "./getDiscourseRelations"; // lifted from getExportTypes -export const getRelationDataUtil = async ( - allRelations: DiscourseRelation[], - nodeLabelByType: Record, -) => +export const getRelationDataUtil = async ({ + allRelations, + nodeLabelByType, + local, +}: { + allRelations: DiscourseRelation[]; + nodeLabelByType: Record; + local?: boolean; +}) => Promise.all( allRelations .filter( @@ -23,6 +28,7 @@ export const getRelationDataUtil = async ( ? [] : fireQuery({ returnNode: sourceLabel, + local, conditions: [ { relation: s.label, @@ -50,13 +56,13 @@ export const getRelationDataUtil = async ( }), ).then((r) => r.flat()); -const getRelationData = async () => { +const getRelationData = async (local?: boolean) => { const allRelations = getDiscourseRelations(); const allNodes = getDiscourseNodes(allRelations); const nodeLabelByType = Object.fromEntries( allNodes.map((a) => [a.type, a.text]), ); - return await getRelationDataUtil(allRelations, nodeLabelByType); + return await getRelationDataUtil({ allRelations, nodeLabelByType, local }); }; export default getRelationData; diff --git a/apps/roam/src/utils/migrateRelations.ts b/apps/roam/src/utils/migrateRelations.ts index a2937a274..1d95a4150 100644 --- a/apps/roam/src/utils/migrateRelations.ts +++ b/apps/roam/src/utils/migrateRelations.ts @@ -14,7 +14,7 @@ const migrateRelations = async (dryRun = false): Promise => { const authorized = getSetting("use-reified-relations"); if (!authorized) return 0; const processed = new Set(); - const relationData = await getRelationData(); + const relationData = await getRelationData(true); let numProcessed = 0; for (const rel of relationData) { const key = `${rel.source}:${rel.relUid}:${rel.target}`;