Skip to content

Commit 031b137

Browse files
authored
ENG-1011 Create Function to Record Relation Triples in Roam (#534)
* ENG-1011: create function to reify functions. * remove log * destructure * ensure relation uniqueness * separate cases for clean signatures * respond to comments: replace async by more complex and slower backend query, remove allowDuplicates
1 parent ddf260a commit 031b137

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import createBlock from "roamjs-components/writes/createBlock";
2+
import createPage from "roamjs-components/writes/createPage";
3+
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";
4+
import { getSetting } from "~/utils/extensionSettings";
5+
6+
const strictQueryForReifiedBlocks = async (
7+
parameterUids: Record<string, string>,
8+
): Promise<string | null> => {
9+
const paramsAsSeq = Object.entries(parameterUids);
10+
const query = `[:find ?u ?d
11+
:in $ ${paramsAsSeq.map(([k]) => "?" + k).join(" ")}
12+
:where [?s :block/uid ?u] [?s :block/props ?p] [(get ?p :discourse-graph) ?d]
13+
${paramsAsSeq.map(([k]) => `[(get ?d :${k}) ?_${k}] [(= ?${k} ?_${k})]`).join(" ")} ]`;
14+
// Note: the extra _k binding variable is only needed for the backend query somehow
15+
// In a local query, we can directly map to `[(get ?d :${k}) ?${k}]`
16+
const result = await Promise.resolve(
17+
window.roamAlphaAPI.data.backend.q(
18+
query,
19+
...paramsAsSeq.map(([, v]) => v),
20+
) as [string, Record<string, string>][],
21+
);
22+
// post-filtering because cannot filter by number of keys in datascript
23+
const numParams = Object.keys(parameterUids).length;
24+
const resultF = result
25+
.filter(([, params]) => Object.keys(params).length === numParams)
26+
.map(([uid]) => uid);
27+
if (resultF.length > 1) {
28+
const paramsAsText = Object.entries(parameterUids)
29+
.map(([k, v]) => `${k}: '${v}'`)
30+
.join(", ");
31+
console.warn(
32+
`${resultF.length} results in strict query for {${paramsAsText}}`,
33+
);
34+
}
35+
return resultF.length > 0 ? resultF[0] : null;
36+
};
37+
38+
const createReifiedBlock = async ({
39+
destinationBlockUid,
40+
schemaUid,
41+
parameterUids,
42+
}: {
43+
destinationBlockUid: string;
44+
schemaUid: string;
45+
parameterUids: Record<string, string>;
46+
}): Promise<string> => {
47+
// TODO: Check that the parameterUids keys correspond to the schema
48+
const data = {
49+
...parameterUids,
50+
hasSchema: schemaUid,
51+
};
52+
const existing = await strictQueryForReifiedBlocks(data);
53+
if (existing !== null) return existing;
54+
const newUid = window.roamAlphaAPI.util.generateUID();
55+
await createBlock({
56+
node: {
57+
text: newUid,
58+
uid: newUid,
59+
props: {
60+
// eslint-disable-next-line @typescript-eslint/naming-convention
61+
"discourse-graph": data,
62+
},
63+
},
64+
parentUid: destinationBlockUid,
65+
order: "last",
66+
});
67+
return newUid;
68+
};
69+
70+
const RELATION_PAGE_TITLE = "roam/js/discourse-graph/relations";
71+
let relationPageUid: string | undefined = undefined;
72+
73+
const getRelationPageUid = async (): Promise<string> => {
74+
if (relationPageUid === undefined) {
75+
relationPageUid = getPageUidByPageTitle(RELATION_PAGE_TITLE);
76+
if (relationPageUid === "") {
77+
relationPageUid = await createPage({ title: RELATION_PAGE_TITLE });
78+
}
79+
}
80+
return relationPageUid;
81+
};
82+
83+
export const createReifiedRelation = async ({
84+
sourceUid,
85+
relationBlockUid,
86+
destinationUid,
87+
}: {
88+
sourceUid: string;
89+
relationBlockUid: string;
90+
destinationUid: string;
91+
}): Promise<string | undefined> => {
92+
const authorized = getSetting("use-reified-relations");
93+
if (authorized) {
94+
return await createReifiedBlock({
95+
destinationBlockUid: await getRelationPageUid(),
96+
schemaUid: relationBlockUid,
97+
parameterUids: {
98+
sourceUid,
99+
destinationUid,
100+
},
101+
});
102+
}
103+
};

0 commit comments

Comments
 (0)