From c2b00babedcd684e09ca8cd8b5740748d72450a5 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 31 Dec 2025 13:34:31 -0500 Subject: [PATCH 1/4] eng-1230 calculate backlinks --- apps/roam/src/utils/jsonld.ts | 66 ++++++++++++++++---------- apps/website/public/schema/dg_base.ttl | 5 -- apps/website/public/schema/dg_core.ttl | 23 ++++++++- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/apps/roam/src/utils/jsonld.ts b/apps/roam/src/utils/jsonld.ts index 9ce326880..4852ce9a8 100644 --- a/apps/roam/src/utils/jsonld.ts +++ b/apps/roam/src/utils/jsonld.ts @@ -17,6 +17,7 @@ export const jsonLdContext = (baseUrl: string): Record => ({ prov: "http://www.w3.org/ns/prov#", sioc: "http://rdfs.org/sioc/ns#", dgb: "https://discoursegraphs.com/schema/dg_base", + dg: "https://discoursegraphs.com/schema/dg_core", subClassOf: "rdfs:subClassOf", title: "dc:title", label: "rdfs:label", @@ -32,6 +33,7 @@ export const jsonLdContext = (baseUrl: string): Record => ({ relationDef: "dgb:RelationDef", relationInstance: "dgb:RelationInstance", inverseOf: "owl:inverseOf", + backlink: "dg:backlink", pages: `${baseUrl}/page/`, }); @@ -131,40 +133,56 @@ export const getJsonLdData = async ({ .filter((s) => s.content !== undefined) .map((node) => [node.label, node["@id"]]), ); + const nodeSet = new Set(pageData.map((n) => n.uid)); - await Promise.all( + const nodes = await Promise.all( pageData.map(async (page: Result) => { - const r = await pageToMarkdown(page, { + const md = await pageToMarkdown(page, { ...settings, allNodes, linkType: "roam url", }); - page.content = r.content; + const { content } = md; + page.content = content; + const { text, uid, type } = page; + const { date, displayName, modified } = getPageMetadata(text); + const nodeType = nodeSchemaUriByName[type as string]; + if (!nodeType) { + internalError({ + error: `Unknown node type "${type as string}" for page "${text}"`, + }); + } + const backlinks = ( + await (window.roamAlphaAPI.data.backend.q( + `[:find ?uid + :where + [?page :block/uid "${uid}"] + [?block :block/page ?page] + [or [?refBlock :block/refs ?block] [?refBlock :block/refs ?page]] + [?refBlock :block/page ?refPage] + [?refPage :block/uid ?uid] + ]`, + ) as Promise>) + ) + .map((x) => x[0]) + .filter((x) => nodeSet.has(x)); + const r: Record = { + "@id": `pages:${uid}`, // eslint-disable-line @typescript-eslint/naming-convention + "@type": nodeType ?? "nodeSchema", // eslint-disable-line @typescript-eslint/naming-convention + title: text, + content, + modified: modified?.toJSON(), + created: date.toJSON(), + creator: displayName, + }; + if (backlinks.length > 0) { + r["backlinks"] = backlinks.map((x) => `pages:${x}`); + } numTreatedPages += 1; await updateExportProgress(0.1 + (numTreatedPages / numPages) * 0.75); + return r; }), ); - - const nodes = pageData.map(({ text, uid, content, type }) => { - const { date, displayName, modified } = getPageMetadata(text); - const nodeType = nodeSchemaUriByName[type]; - if (!nodeType) { - internalError({ - error: `Unknown node type "${type}" for page "${text}"`, - }); - } - const r = { - "@id": `pages:${uid}`, // eslint-disable-line @typescript-eslint/naming-convention - "@type": nodeType ?? "nodeSchema", // eslint-disable-line @typescript-eslint/naming-convention - title: text, - content: content as string, - modified: modified?.toJSON(), - created: date.toJSON(), - creator: displayName, - }; - return r; - }); - const nodeSet = new Set(pageData.map((n) => n.uid)); const rels = await getRelationData(); await updateExportProgress(1); const relations = uniqJsonArray( diff --git a/apps/website/public/schema/dg_base.ttl b/apps/website/public/schema/dg_base.ttl index cdbf659fa..b06b6d22a 100644 --- a/apps/website/public/schema/dg_base.ttl +++ b/apps/website/public/schema/dg_base.ttl @@ -43,11 +43,6 @@ dgb:destination a dgb:Role ; rdfs:range dgb:NodeSchema ; rdfs:comment "The destination of a binary relation"@en . -dgb:textRefersToNode a owl:ObjectProperty; - rdfs:domain dgb:NodeSchema; - rdfs:range dgb:NodeSchema; - rdfs:comment "The text of a node refers to another node"@en . - # examples diff --git a/apps/website/public/schema/dg_core.ttl b/apps/website/public/schema/dg_core.ttl index 0a12121a7..9648bd7ab 100644 --- a/apps/website/public/schema/dg_core.ttl +++ b/apps/website/public/schema/dg_core.ttl @@ -3,11 +3,18 @@ @prefix : . @prefix dc: . @prefix owl: . -@prefix vs: . -@prefix sioc: . @prefix prov: . @prefix dgb: . @prefix dg: . +# http://purl.org/spar/po +@prefix po: . + + + dc:date "2025-12-31" ; + rdfs:comment "DiscourseGraph core vocabulary"@en ; + rdfs:label "DiscourseGraph core vocabulary"@en ; + owl:versionInfo "0 (tentative)" ; + a owl:Ontology. dg:Question a dgb:NodeSchema; rdfs:label "Question"@en; @@ -90,3 +97,15 @@ dg:curatedFrom a dgb:RelationDef; rdfs:label "Curated from"@en; rdfs:range dg:Evidence; rdfs:domain dg:Source. + +dg:containsRec a owl:TransitiveProperty. +po:contains rdfs:subClassOf dg:containsRec. +dct:hasPart rdfs:subClassOf dg:containsRec. + +dg:containsRef a owl:ObjectProperty; + owl:propertyChainAxiom (dg:containsRec dct:references); + rdfs:label "Contains a reference to"@en. + +dg:backlink a owl:ObjectProperty; + owl:inverseOf dg:containsRef; + rdfs:label "is referred by"@en. From 18d262f85705a876f41b03e80bc23760991fc6cd Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 31 Dec 2025 13:42:49 -0500 Subject: [PATCH 2/4] broaden return type --- apps/roam/src/utils/jsonld.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/roam/src/utils/jsonld.ts b/apps/roam/src/utils/jsonld.ts index 4852ce9a8..a022c1e60 100644 --- a/apps/roam/src/utils/jsonld.ts +++ b/apps/roam/src/utils/jsonld.ts @@ -107,7 +107,10 @@ export const getJsonLdData = async ({ nodeLabelByType: Record; updateExportProgress: (progress: number) => Promise; }): Promise< - Record | Record[]> + Record< + string, + string | Record | Record[] + > > => { const roamUrl = canonicalRoamUrl(); const getRelationData = () => From e696a89b8aa1f2788ea5c754d08a8c42b225d0ce Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 31 Dec 2025 13:47:25 -0500 Subject: [PATCH 3/4] correct dc namespace --- apps/website/public/schema/dg_core.ttl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/website/public/schema/dg_core.ttl b/apps/website/public/schema/dg_core.ttl index 9648bd7ab..7ccbc6867 100644 --- a/apps/website/public/schema/dg_core.ttl +++ b/apps/website/public/schema/dg_core.ttl @@ -100,10 +100,10 @@ dg:curatedFrom a dgb:RelationDef; dg:containsRec a owl:TransitiveProperty. po:contains rdfs:subClassOf dg:containsRec. -dct:hasPart rdfs:subClassOf dg:containsRec. +dc:hasPart rdfs:subClassOf dg:containsRec. dg:containsRef a owl:ObjectProperty; - owl:propertyChainAxiom (dg:containsRec dct:references); + owl:propertyChainAxiom (dg:containsRec dc:references); rdfs:label "Contains a reference to"@en. dg:backlink a owl:ObjectProperty; From 2877c899204ba1e729a060dde727fd3fe61298e9 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 31 Dec 2025 13:56:57 -0500 Subject: [PATCH 4/4] coderabbit corrections --- apps/roam/src/utils/jsonld.ts | 2 +- apps/website/public/schema/dg_core.ttl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/roam/src/utils/jsonld.ts b/apps/roam/src/utils/jsonld.ts index a022c1e60..ce0699a17 100644 --- a/apps/roam/src/utils/jsonld.ts +++ b/apps/roam/src/utils/jsonld.ts @@ -171,7 +171,7 @@ export const getJsonLdData = async ({ .filter((x) => nodeSet.has(x)); const r: Record = { "@id": `pages:${uid}`, // eslint-disable-line @typescript-eslint/naming-convention - "@type": nodeType ?? "nodeSchema", // eslint-disable-line @typescript-eslint/naming-convention + "@type": nodeType, // eslint-disable-line @typescript-eslint/naming-convention title: text, content, modified: modified?.toJSON(), diff --git a/apps/website/public/schema/dg_core.ttl b/apps/website/public/schema/dg_core.ttl index 7ccbc6867..cbf70630f 100644 --- a/apps/website/public/schema/dg_core.ttl +++ b/apps/website/public/schema/dg_core.ttl @@ -99,8 +99,8 @@ dg:curatedFrom a dgb:RelationDef; rdfs:domain dg:Source. dg:containsRec a owl:TransitiveProperty. -po:contains rdfs:subClassOf dg:containsRec. -dc:hasPart rdfs:subClassOf dg:containsRec. +po:contains rdfs:subPropertyOf dg:containsRec. +dc:hasPart rdfs:subPropertyOf dg:containsRec. dg:containsRef a owl:ObjectProperty; owl:propertyChainAxiom (dg:containsRec dc:references);