前言
本书定义 AsciiDoc 书稿的 source-aware RDF 投影查询工作流。该工作流以 book entry 和 documentRoot 构造 RDF 1.2 Turtle,并使用 SPARQL 查询标题结构、交叉引用网络、引用入度、引用出度、递归子树和审计事实。
大型书稿通常由 book entry、frontmatter、chapters、backmatter 和 shared attributes 组成。源文件适合写作和维护,HTML 适合人工阅读,全文搜索适合查找字符串。它们都不能单独提供稳定的标题图、交叉引用绑定、引用网络和结构化行号查询。RDF 投影把这些事实放入可查询图中,使作者能够用 SPARQL 提问。
RDF 投影中的 fact 携带 origin source coordinate。aat:relativePath 相对于 documentRoot,heading line、line span 和 column span 指向产生该 fact 的 source file。作者修改书稿时,使用查询得到的 path、headingLine、line span、column、headline、selector 和 raw 进入源文件。
TTL 文件属于 projection artifact。它可以被缓存、提交或删除,但它不作为手写源文件存在。book entry 到达的 source set 改变后,TTL 必须重新生成。
本书的操作链条从 投影输入与生成物 开始,经由 SPARQL 查询环境 执行查询,并在 阅读与修改闭环 中回到源书稿维护。交叉引用网络的核心查询集中在 交叉引用边查询、引用入度与出度查询 和 交叉引用邻域查询。
本书使用中文叙事。AsciiDoc、RDF 1.2、Turtle、TTL、SPARQL、heading、xref、selector、payload、raw、line span、source coordinate 和 projection artifact 等术语在正文中保留英文形式或中英并列形式,以保持命令、查询和工程实现之间的术语连续性。
第一部:工作流对象
这一部定义书稿 RDF 投影查询工作流、book entry、documentRoot、TTL 生成物和查询边界。
1. 工作流对象
书稿 RDF 投影查询工作流是从 AsciiDoc book entry 到 SPARQL 查询结果的生成和观察链条。
该工作流的输入是 book entry 和 documentRoot。Book entry 声明书稿入口 source file。DocumentRoot 声明 relativePath 基准和 include path 边界。
投影器读取 book entry 到达的 source set,并构造 source-aware RDF 1.2 标题投影图。投影图中的 heading、xref edge、payload 和 block fact 携带 origin source coordinate。
该工作流的生成物是 TTL。TTL 是 RDF 1.2 标题投影图的 Turtle 序列化表面。TTL 位于 build 目录下,是可丢弃、可重新生成的 projection artifact。
该工作流的查询对象是 RDF12 TTL,查询语言是 SPARQL。查询执行器读取 TTL 和 SPARQL 查询,返回标题、层级、交叉引用、引用入度、引用出度、payload 和审计事实。
1.1. 消费者动作
该工作流服务以下作者动作:
-
查看全书标题骨架。
-
按 address label 或 headline 定位标题。
-
读取标题自身 raw 和 source file line span。
-
读取标题直接子标题和递归子树。
-
查询标题发出的交叉引用。
-
查询指向标题的交叉引用。
-
计算标题在 xref 网络中的入度和出度。
-
沿 xref 网络查看局部脉络。
-
查找未绑定、多义或重复 label 相关事实。
-
使用查询结果回到 source file。
这些动作共同构成书稿结构观察面。不能服务这些动作的对象不进入本工作流的核心说明。
1.2. 对象边界
本工作流不执行 AsciiDoc 写作。源书稿仍由作者维护。
本工作流不替代 HTML 发布。HTML 是面向人工阅读的发布表面,TTL 是面向结构查询的观察表面。
本工作流不实现 include resolution 或 origin recovery。Book-entry parsing、source coordinate recovery 和 RDF12 投影由 asciidoc-abundant-tree 提供。
本工作流不解释 payload 内部 JSON、YAML、TOML、XML 或其它格式。payload raw 作为复杂属性保留。
本工作流不把 TTL 当作源文件。TTL 位于 build 区域,source set 变化后必须重新生成。
1.3. 工作流闭环
工作流闭环由五个动作构成:
book entry -> project ttl -> query ttl -> edit source files
作者修改 source set 后,闭环重新开始。Stale TTL 不代表当前书稿事实。
该闭环的价值不在于一次性生成文件,而在于让作者能够反复提出结构问题,并把查询结果带回写作行动。
投影阶段由 投影输入与生成物 定义。查询执行表面由 SPARQL 查询环境 定义。查询结果进入修改动作时遵守 阅读与修改闭环 的 source coordinate 边界。
2. 投影输入与生成物
投影阶段读取 book entry 和 documentRoot,并生成 RDF12 TTL。Book entry 是书稿入口 source file。DocumentRoot 是 aat:relativePath 的基准和 include path 的边界。
2.1. Book entry
标准书架中,每本书的 book entry 位于:
books/<book-id>/book.adoc
投影命令在 bookshelf root 下运行。该运行目录同时作为 documentRoot:
cd docs/bookshelf
mkdir -p build/rdf12/books
asciidoc-abundant-tree \
books/<book-id>/book.adoc \
--mode book-entry \
--document-root . \
--format rdf12 \
> build/rdf12/books/<book-id>.ttl
2.2. TTL 投影
--mode book-entry 声明输入构造方式。投影器读取 book entry 和受支持 include graph,生成带 origin source coordinate 的 RDF 1.2 Turtle。
--document-root . 声明 relativePath 基准。查询结果中的 aat:relativePath 相对于该目录解释。
--format rdf12 输出面向 SPARQL 查询的 RDF 1.2 Turtle。.ttl 是输出文件后缀。调用者不使用 --format ttl 或 --format turtle。
2.3. 验证 TTL
TTL 生成后必须验证 Turtle 语法:
riot --validate build/rdf12/books/<book-id>.ttl
riot --validate 成功表示 Turtle 可解析。它不表示书稿引用全部正确,也不表示查询结果符合作者意图。
2.4. RDF12 JSON-LD
结构化消费者可以生成 RDF12 JSON-LD:
mkdir -p build/rdf12/books
asciidoc-abundant-tree \
books/<book-id>/book.adoc \
--mode book-entry \
--document-root . \
--format rdf12-json-ld \
> build/rdf12/books/<book-id>.jsonld
JSON-LD 是 RDF graph 的结构化投影。SPARQL 查询使用 TTL。
2.5. 生成物纪律
TTL 和 JSON-LD 放在 build 目录下。它们属于生成物,可以删除。
book entry 到达的 source set 改变后,TTL 必须重新生成。Stale TTL 不能用于当前书稿审计。
作者需要保留 TTL 快照时,应把 source set digest 或 projection state 一同纳入判断。只有 TTL 文件本身不足以证明它仍然匹配当前源书稿。
2.6. 跨项目投影
当投影器位于另一个项目中时,可以在目标 bookshelf root 下运行投影命令:
cd /home/t103o/workbench/projects/ExampleProject/docs/bookshelf
mkdir -p build/rdf12/books
asciidoc-abundant-tree \
books/<book-id>/book.adoc \
--mode book-entry \
--document-root . \
--format rdf12 \
> build/rdf12/books/<book-id>.ttl
这种运行方式让 aat:relativePath 相对于目标 bookshelf root,并让 TTL 留在被查询项目自己的 build 目录中。
3. SPARQL 查询环境
本章定义可复制的 SPARQL 查询执行表面。示例命令使用 Apache Jena ARQ 读取 TTL 数据文件和 SPARQL 查询文本,并输出表格化查询结果。
inkrail 的内置查询执行器由 inkrail CLI 设计书的查询执行器 定义。本章保留 ARQ 命令,是为了让查询语句保持可复制、可验证的原生 SPARQL 表面。
3.1. 基本命令
查询命令使用 --data 指向 TTL,并把 SPARQL 查询文本传给 --query:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?level ?headline ?path ?headingLine WHERE {
?h a aat:Heading ;
aat:headingLevel ?level ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine .
FILTER(?level <= 1)
}
ORDER BY ?path ?headingLine
SPARQL
)
查询文本使用 SPARQL。常用前缀如下:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
PREFIX rel: <https://micheng.dev/ns/asciidoc-relation#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX prov: <http://www.w3.org/ns/prov#>
本书的推荐查询使用 bash here-doc 表面。该表面让查询文本和执行命令保持在同一个可复制单元中。
3.2. 查询动作分组
查询按动作分组:
来源确认
标题定位
标题切片
结构范围
交叉引用边
引用入度
引用出度
交叉引用邻域
质量审计
动作分组提供阅读顺序。查询语句集不是投影器的一部分;它是作者使用投影图的工作台。
3.3. 参数处理
here-doc 查询没有独立参数表面。查询文本可以直接写入目标 label 或 headline:
?root aat:addressLabel "<heading-label>" .
<heading-label> 是教程占位符,不是合法查询值。作者复制查询后,把该 literal 替换为当前书稿中的 address label。
3.4. 排序规则
标题查询和引用查询应显式排序。RDF graph 不承诺 triple 顺序,SPARQL 查询结果也不应依赖数据文件排版顺序。
文档阅读查询通常按 ?path ?start 或 ?path ?start ?col 排序:
ORDER BY ?path ?start ?col
度数查询通常按度数降序排序:
ORDER BY DESC(?inDegree) DESC(?outDegree) ?headline
排序是查询契约的一部分。缺少排序的查询不适合作为稳定教程查询。
第二部:标题阅读查询
这一部定义标题定位、标题切片读取、结构范围和递归子树查询。
4. 标题定位与切片查询
标题定位查询用于从 TTL 中找到 heading node、headline、label 和标题行 source coordinate。标题切片查询用于读取该 heading 自身 raw 和切片 line span。
aat:headingLine 指向标题行。aat:startLine 和 aat:endLine 指向 heading slice 的闭区间。作者打开源文件定位标题时使用 headingLine;作者读取标题切片 raw 时使用 start 和 end。
4.1. 全书骨架
全书骨架查询返回一级和二级阅读结构:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?level ?headline ?path ?headingLine WHERE {
?h a aat:Heading ;
aat:headingLevel ?level ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine .
FILTER(?level <= 1)
}
ORDER BY ?path ?headingLine
该查询是投影 sanity check。作者通过它确认 TTL 覆盖的书稿、标题层级和标题行位置。
4.2. 按 address label 定位
address label 来自作者显式声明的标题 ID。按 address label 查询适合定位稳定引用点。
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline ?path ?headingLine ?start ?end WHERE {
?heading a aat:Heading ;
aat:addressLabel "<heading-label>" ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine ;
aat:startLine ?start ;
aat:endLine ?end .
}
<heading-label> 替换为当前书稿中的 address label。查询结果中的 ?path 指向 origin source file。?headingLine 是标题行。?start 和 ?end 是该 heading slice 在 source file 中的闭区间行号。
4.3. 按 headline 定位
headline 是标题文本。headline 可以重复,查询必须保留多结果。
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline ?path ?headingLine ?start ?end ?label ?generated WHERE {
?heading a aat:Heading ;
aat:headline "边 Payload" ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine ;
aat:startLine ?start ;
aat:endLine ?end .
OPTIONAL { ?heading aat:addressLabel ?label . }
OPTIONAL { ?heading aat:generatedAddressLabel ?generated . }
}
ORDER BY ?path ?headingLine
重复 headline 是源书稿事实。作者根据 heading line、slice span、label 和上下文选择目标标题。
4.4. 读取标题自身 raw
aat:raw 是标题节点自身切片的文本表面。它不包含子标题切片。
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?headline ?path ?start ?end ?raw WHERE {
?heading aat:addressLabel "<heading-label>" ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:endLine ?end ;
aat:raw ?raw .
}
该查询适合快速读取一个标题自身内容。需要读取完整章节时,作者应结合递归子树查询取得后代标题。
4.5. label 空间检查
标题可被 aat:addressLabel、aat:generatedAddressLabel 和 aat:headline 查询。selector 绑定也在该 label 空间中进行。
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline ?path ?label ?generated ?headingLine WHERE {
?heading a aat:Heading ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine .
OPTIONAL { ?heading aat:addressLabel ?label . }
OPTIONAL { ?heading aat:generatedAddressLabel ?generated . }
}
ORDER BY ?path ?headingLine
该查询用于观察书稿的可寻址表面。作者需要稳定引用时,应优先使用显式 address label。
5. 结构范围查询
标题结构边定义阅读范围。aat:containsDirectly 表达直接父子标题关系,aat:previousSibling 表达同父标题下的前一个兄弟标题。
结构范围查询的主要作用是确定章节边界。交叉引用网络查询可以使用这些边界限制 source heading 或 target heading。
5.1. 直接子标题
直接子标题查询读取当前标题的下一层标题:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?child ?childHeadline ?path ?childLine ?start ?end WHERE {
?parent aat:addressLabel "<heading-label>" .
?parent aat:containsDirectly ?child .
?child aat:headline ?childHeadline ;
aat:relativePath ?path ;
aat:headingLine ?childLine ;
aat:startLine ?start ;
aat:endLine ?end .
}
ORDER BY ?path ?childLine
该查询不递归。它回答当前标题直接分成哪些子节。
5.2. 递归子树
递归子树查询读取当前标题的所有后代标题:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?descendant ?level ?headline ?path ?headingLine ?start ?end WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly+ ?descendant .
?descendant aat:headingLevel ?level ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine ;
aat:startLine ?start ;
aat:endLine ?end .
}
ORDER BY ?path ?headingLine
aat:containsDirectly+ 表示一跳或多跳后代。该查询不返回根标题自身。
5.3. 包含根节点的子树
需要把根标题自身纳入范围时,使用 *:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?node ?level ?headline ?path ?headingLine ?start ?end WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?node .
?node aat:headingLevel ?level ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine ;
aat:startLine ?start ;
aat:endLine ?end .
}
ORDER BY ?path ?headingLine
该查询生成章节范围集合。后续 xref 查询可以用该集合判断引用发生在子树内部还是指向子树外部。
5.4. 父标题反查
父标题反查用于定位一个标题所属范围:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?parent ?parentHeadline ?parentPath ?parentLine ?parentSliceStart ?parentSliceEnd WHERE {
?child aat:addressLabel "<heading-label>" .
?parent aat:containsDirectly ?child ;
aat:headline ?parentHeadline ;
aat:relativePath ?parentPath ;
aat:headingLine ?parentLine ;
aat:startLine ?parentSliceStart ;
aat:endLine ?parentSliceEnd .
}
标题最多有一个直接父标题。没有父标题的标题是当前 book entry 投影图中的顶层标题。
5.5. 兄弟顺序
aat:previousSibling 查询当前标题的前一个同父兄弟:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?headingHeadline ?previousHeadline ?path ?headingLine WHERE {
?heading aat:addressLabel "<heading-label>" ;
aat:headline ?headingHeadline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine ;
aat:previousSibling ?previous .
?previous aat:headline ?previousHeadline .
}
兄弟顺序不跨父标题。该查询适合检查章节排列,不适合分析交叉引用脉络。
5.6. 结构范围与 xref 查询
结构范围的高价值用法是限制 xref 查询。下列模式先取得一个章节子树,再把该子树作为 source heading 集合:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?targetHeadline ?path ?start WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?inside .
?edge a aat:XrefEdge ;
aat:sourceHeading ?inside ;
aat:targetHeading ?target ;
aat:relativePath ?path ;
aat:startLine ?start .
?inside aat:headline ?sourceHeadline .
?target aat:headline ?targetHeadline .
}
ORDER BY ?path ?start
该查询回答某个章节范围内的标题引用了哪些目标标题。父子结构在这里承担范围边界,xref 承担脉络关系。
第三部:交叉引用网络查询
这一部定义 xref 边查询、引用入度、引用出度和交叉引用邻域查询。
6. 交叉引用边查询
交叉引用边来自标题切片中的 xref。投影图使用 aat:XrefEdge 保存边证据。边证据记录 source heading、target selector、target heading、raw、行号和列号。
交叉引用查询比全文搜索多出两个结构事实:xref 属于哪个标题切片,selector 是否绑定到目标标题。
6.1. 发出的交叉引用
发出查询读取某个标题切片中的 xref:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?edge ?selector ?target ?targetHeadline ?raw ?path ?start ?col WHERE {
?source aat:addressLabel "<heading-label>" .
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:raw ?raw ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col .
OPTIONAL {
?edge aat:targetHeading ?target .
?target aat:headline ?targetHeadline .
}
}
ORDER BY ?path ?start ?col
该查询保留 ?selector 和 ?raw。目标绑定结果不能替代源文本事实。
6.2. 指向当前标题的交叉引用
反向查询读取哪些标题引用了目标标题:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?source ?sourceHeadline ?raw ?path ?start ?col WHERE {
?target aat:addressLabel "<heading-label>" .
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetHeading ?target ;
aat:raw ?raw ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col .
?source aat:headline ?sourceHeadline .
}
ORDER BY ?path ?start ?col
该查询回答“谁引用了这个标题”。它适合检查核心概念、公共契约或术语入口是否被书稿实际使用。
6.3. 全书交叉引用表
全书引用表按出现顺序列出所有 xref:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?targetHeadline ?path ?start ?col ?raw WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col ;
aat:raw ?raw .
?source aat:headline ?sourceHeadline .
OPTIONAL {
?edge aat:targetHeading ?target .
?target aat:headline ?targetHeadline .
}
}
ORDER BY ?path ?start ?col
该查询是引用网络的基本审阅表。它不评价引用质量,只暴露 source、selector、target 和 source coordinate。
6.4. 未绑定交叉引用
未绑定查询返回没有 target heading、也没有 candidate heading 的 xref:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?raw ?path ?start ?col WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:raw ?raw ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col .
?source aat:headline ?sourceHeadline .
FILTER NOT EXISTS { ?edge aat:targetHeading ?target . }
FILTER NOT EXISTS { ?edge aat:candidateHeading ?candidate . }
}
ORDER BY ?path ?start ?col
未绑定结果不自动等于 dead link。它表示 selector 没有在当前标题 label 空间中绑定。作者需要根据源书稿、外部链接规则和构建语义判断是否修改。
6.5. 多义交叉引用
多义查询返回 selector 的候选标题:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?candidateHeadline ?candidatePath ?candidateLine ?raw ?path ?start WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:candidateHeading ?candidate ;
aat:raw ?raw ;
aat:relativePath ?path ;
aat:startLine ?start .
?source aat:headline ?sourceHeadline .
?candidate aat:headline ?candidateHeadline ;
aat:relativePath ?candidatePath ;
aat:headingLine ?candidateLine .
}
ORDER BY ?path ?start ?selector ?candidatePath ?candidateLine
多义结果是源书稿事实。投影图不替作者任意选择唯一目标。
6.6. 子树内部引用
子树内部引用查询要求 source heading 和 target heading 都在同一个结构范围内:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?targetHeadline ?path ?start WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?source .
?root aat:containsDirectly* ?target .
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetHeading ?target ;
aat:relativePath ?path ;
aat:startLine ?start .
?source aat:headline ?sourceHeadline .
?target aat:headline ?targetHeadline .
}
ORDER BY ?path ?start
该查询观察一个章节范围内部的引用关系。结构范围提供边界,xref 提供脉络。
7. 引用入度与出度查询
xref degree 描述标题在交叉引用网络中的连接数量。它不是章节质量判决,而是阅读和审计信号。
xref out-degree 是标题切片中发出的已投影交叉引用数量。xref in-degree 是指向该标题的已绑定交叉引用数量。
父子包含也可以形成度数,但它主要表达章节结构。交叉引用 degree 更能表达概念依赖、论证桥接和章节中心性。
degree 查询统计连接数量。需要观察连接对象和多跳路径时,使用 交叉引用邻域查询。
7.1. 出度查询
出度查询统计每个标题发出的 xref 数量:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline (COUNT(?edge) AS ?outDegree) WHERE {
?heading a aat:Heading ;
aat:headline ?headline .
OPTIONAL {
?edge a aat:XrefEdge ;
aat:sourceHeading ?heading .
}
}
GROUP BY ?heading ?headline
ORDER BY DESC(?outDegree) ?headline
出度高的标题通常承担综述、桥接、引用坐标或依赖说明作用。出度为零不表示标题孤立;它只表示该标题切片中没有投影出的 xref。
7.2. 入度查询
入度查询统计每个标题被引用的数量:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline (COUNT(?edge) AS ?inDegree) WHERE {
?heading a aat:Heading ;
aat:headline ?headline .
OPTIONAL {
?edge a aat:XrefEdge ;
aat:targetHeading ?heading .
}
}
GROUP BY ?heading ?headline
ORDER BY DESC(?inDegree) ?headline
入度高的标题通常是核心概念、公共定义、稳定入口或被多个章节依赖的对象。入度为零不表示标题没有价值;它只表示当前书稿中没有已绑定 xref 指向该标题。
7.3. 合并入度与出度
合并查询把两个指标放在同一结果表中:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline ?inDegree ?outDegree WHERE {
?heading a aat:Heading ;
aat:headline ?headline .
{
SELECT ?heading (COUNT(?inEdge) AS ?inDegree) WHERE {
?heading a aat:Heading .
OPTIONAL {
?inEdge a aat:XrefEdge ;
aat:targetHeading ?heading .
}
}
GROUP BY ?heading
}
{
SELECT ?heading (COUNT(?outEdge) AS ?outDegree) WHERE {
?heading a aat:Heading .
OPTIONAL {
?outEdge a aat:XrefEdge ;
aat:sourceHeading ?heading .
}
}
GROUP BY ?heading
}
}
ORDER BY DESC(?inDegree) DESC(?outDegree) ?headline
该查询适合观察书稿中哪些标题是引用汇聚点,哪些标题是引用发散点。
7.4. 范围内出度
范围内出度统计一个章节子树中每个标题发出的 xref:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline (COUNT(?edge) AS ?outDegree) WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?heading .
?heading aat:headline ?headline .
OPTIONAL {
?edge a aat:XrefEdge ;
aat:sourceHeading ?heading .
}
}
GROUP BY ?heading ?headline
ORDER BY DESC(?outDegree) ?headline
该查询回答一个章节范围内部哪些标题承担引用发散职责。
7.5. 范围外依赖数量
范围外依赖查询统计一个章节子树向外引用的数量:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?source ?sourceHeadline (COUNT(?edge) AS ?externalOutDegree) WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?source .
?source aat:headline ?sourceHeadline .
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetHeading ?target .
FILTER NOT EXISTS {
?root aat:containsDirectly* ?target .
}
}
GROUP BY ?source ?sourceHeadline
ORDER BY DESC(?externalOutDegree) ?sourceHeadline
该查询用于观察一个章节范围对外部章节的依赖。
8. 交叉引用邻域查询
交叉引用邻域是围绕一个标题的局部 xref 网络。它包含该标题发出的引用、指向该标题的引用,以及这些邻居继续连接到的标题。
交叉引用邻域查询服务章节脉络阅读。它回答一个标题如何连接到其它标题,以及其它标题如何回到它。
8.1. 正向一跳邻域
正向一跳查询读取当前标题直接引用的目标标题:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?target ?targetHeadline ?targetPath ?targetLine ?raw ?edgePath ?edgeStart WHERE {
?root aat:addressLabel "<heading-label>" .
?edge a aat:XrefEdge ;
aat:sourceHeading ?root ;
aat:targetHeading ?target ;
aat:raw ?raw ;
aat:relativePath ?edgePath ;
aat:startLine ?edgeStart .
?target aat:headline ?targetHeadline ;
aat:relativePath ?targetPath ;
aat:headingLine ?targetLine .
}
ORDER BY ?edgePath ?edgeStart ?targetPath ?targetLine
该查询回答“这个标题把读者带向哪里”。
8.2. 反向一跳邻域
反向一跳查询读取直接引用当前标题的 source heading:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?source ?sourceHeadline ?sourcePath ?sourceLine ?raw ?edgePath ?edgeStart WHERE {
?root aat:addressLabel "<heading-label>" .
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetHeading ?root ;
aat:raw ?raw ;
aat:relativePath ?edgePath ;
aat:startLine ?edgeStart .
?source aat:headline ?sourceHeadline ;
aat:relativePath ?sourcePath ;
aat:headingLine ?sourceLine .
}
ORDER BY ?edgePath ?edgeStart ?sourcePath ?sourceLine
该查询回答“哪些标题把读者带到这里”。
8.3. 正向两跳邻域
正向两跳查询读取当前标题引用的标题,以及这些标题继续引用的标题:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?first ?firstHeadline ?firstPath ?firstLine ?second ?secondHeadline ?secondPath ?secondLine WHERE {
?root aat:addressLabel "<heading-label>" .
?edge1 a aat:XrefEdge ;
aat:sourceHeading ?root ;
aat:targetHeading ?first .
?first aat:headline ?firstHeadline ;
aat:relativePath ?firstPath ;
aat:headingLine ?firstLine .
?edge2 a aat:XrefEdge ;
aat:sourceHeading ?first ;
aat:targetHeading ?second .
?second aat:headline ?secondHeadline ;
aat:relativePath ?secondPath ;
aat:headingLine ?secondLine .
}
ORDER BY ?firstPath ?firstLine ?secondPath ?secondLine
该查询显式展开两步。显式展开比复杂 property path 更适合教程,因为 aat:XrefEdge 是边证据资源,不是直接 heading-to-heading 谓词。
8.4. 反向两跳邻域
反向两跳查询读取引用当前标题的标题,以及引用这些标题的更远 source heading:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?near ?nearHeadline ?nearPath ?nearLine ?far ?farHeadline ?farPath ?farLine WHERE {
?root aat:addressLabel "<heading-label>" .
?edge1 a aat:XrefEdge ;
aat:sourceHeading ?near ;
aat:targetHeading ?root .
?near aat:headline ?nearHeadline ;
aat:relativePath ?nearPath ;
aat:headingLine ?nearLine .
?edge2 a aat:XrefEdge ;
aat:sourceHeading ?far ;
aat:targetHeading ?near .
?far aat:headline ?farHeadline ;
aat:relativePath ?farPath ;
aat:headingLine ?farLine .
}
ORDER BY ?nearPath ?nearLine ?farPath ?farLine
该查询回答“哪些标题通过中间标题间接走到这里”。
8.5. 双向一跳邻域
双向一跳查询把入边和出边合并为局部邻居表:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?direction ?neighbor ?neighborHeadline ?edgePath ?edgeStart ?raw WHERE {
?root aat:addressLabel "<heading-label>" .
{
?edge a aat:XrefEdge ;
aat:sourceHeading ?root ;
aat:targetHeading ?neighbor ;
aat:relativePath ?edgePath ;
aat:startLine ?edgeStart ;
aat:raw ?raw .
BIND("out" AS ?direction)
}
UNION
{
?edge a aat:XrefEdge ;
aat:sourceHeading ?neighbor ;
aat:targetHeading ?root ;
aat:relativePath ?edgePath ;
aat:startLine ?edgeStart ;
aat:raw ?raw .
BIND("in" AS ?direction)
}
?neighbor aat:headline ?neighborHeadline .
}
ORDER BY ?direction ?edgePath ?edgeStart
该查询适合在修改一个标题前查看它的引用上下文。
8.6. 子树向外引用
子树向外引用查询读取一个章节范围内的 source heading 指向范围外的 target heading:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?targetHeadline ?path ?start ?raw WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?source .
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetHeading ?target ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:raw ?raw .
FILTER NOT EXISTS {
?root aat:containsDirectly* ?target .
}
?source aat:headline ?sourceHeadline .
?target aat:headline ?targetHeadline .
}
ORDER BY ?path ?start
该查询回答一个章节范围依赖哪些外部章节。
8.7. 外部指向子树
外部入边查询读取范围外标题指向范围内标题的 xref:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?targetHeadline ?path ?start ?raw WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?target .
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetHeading ?target ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:raw ?raw .
FILTER NOT EXISTS {
?root aat:containsDirectly* ?source .
}
?source aat:headline ?sourceHeadline .
?target aat:headline ?targetHeadline .
}
ORDER BY ?path ?start
该查询回答哪些外部章节依赖当前章节范围。
第四部:审计与写作闭环
这一部定义质量审计、source coordinate、阅读编辑动作、跨项目用法和查询语句集。
9. 质量审计查询
质量审计查询暴露书稿结构事实。审计结果不自动构成错误;它们提供作者判断和修改的入口。
9.1. 来源文件确认
来源查询确认 TTL 来自哪个 book entry:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?path ?digest WHERE {
?sourceDoc a aat:AsciiDocSourceDocument ;
aat:relativePath ?path .
OPTIONAL { ?sourceDoc aat:sourceDigest ?digest . }
}
该查询应在跨项目查询前运行。它防止作者拿错 TTL。
9.2. 重复 address label
重复 address label 会影响 selector 绑定稳定性:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?label (COUNT(?h) AS ?count) WHERE {
?h a aat:Heading ;
aat:addressLabel ?label .
}
GROUP BY ?label
HAVING (COUNT(?h) > 1)
ORDER BY DESC(?count) ?label
手动 ID 通常表达作者的稳定引用意图。重复 address label 应进入作者审查。
9.3. 重复 headline
重复 headline 是合法书稿事实:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?headline (COUNT(?h) AS ?count) WHERE {
?h a aat:Heading ;
aat:headline ?headline .
}
GROUP BY ?headline
HAVING (COUNT(?h) > 1)
ORDER BY DESC(?count) ?headline
重复 headline 不自动表示问题。它提示作者在引用这些标题时优先使用显式 address label。
9.4. 未绑定引用审计
未绑定引用查询是交叉引用质量审计的第一入口:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?raw ?path ?start ?col WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:raw ?raw ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col .
?source aat:headline ?sourceHeadline .
FILTER NOT EXISTS { ?edge aat:targetHeading ?target . }
FILTER NOT EXISTS { ?edge aat:candidateHeading ?candidate . }
}
ORDER BY ?path ?start ?col
该结果应由作者审查。外部链接、特殊 anchor、生成 label 差异和真实拼写错误都可能产生未绑定结果。
9.5. 多义引用审计
多义引用查询暴露 selector 命中多个候选标题:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?candidateHeadline ?candidatePath ?candidateLine ?path ?start WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:candidateHeading ?candidate ;
aat:relativePath ?path ;
aat:startLine ?start .
?source aat:headline ?sourceHeadline .
?candidate aat:headline ?candidateHeadline ;
aat:relativePath ?candidatePath ;
aat:headingLine ?candidateLine .
}
ORDER BY ?path ?start ?selector ?candidatePath ?candidateLine
作者可以通过增加显式 ID、修改引用 selector 或重命名标题消除多义。
9.6. payload 绑定审计
节点 payload 查询:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?headingHeadline ?payloadId ?format ?payloadPath ?payloadStart ?payloadEnd WHERE {
?heading a aat:Heading ;
aat:headline ?headingHeadline ;
aat:payload ?payload .
?payload aat:payloadKind "node" ;
aat:payloadId ?payloadId ;
aat:relativePath ?payloadPath ;
aat:startLine ?payloadStart ;
aat:endLine ?payloadEnd .
OPTIONAL { ?payload aat:format ?format . }
}
ORDER BY ?payloadPath ?payloadStart
边 payload 查询:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?targetHeadline ?payloadId ?format ?payloadPath ?payloadStart ?payloadEnd WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetHeading ?target ;
aat:payload ?payload .
?source aat:headline ?sourceHeadline .
?target aat:headline ?targetHeadline .
?payload aat:payloadKind "edge" ;
aat:payloadId ?payloadId ;
aat:relativePath ?payloadPath ;
aat:startLine ?payloadStart ;
aat:endLine ?payloadEnd .
OPTIONAL { ?payload aat:format ?format . }
}
ORDER BY ?payloadPath ?payloadStart
payload raw 不展开为业务图。审计查询只检查 payload 是否绑定到预期 heading 或 xref edge。
9.7. raw 抽查
raw 抽查用于确认关键标题切片包含预期文本:
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?headline ?path ?headingLine ?containsExpected WHERE {
?h aat:headline "<heading-headline>" ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine ;
aat:raw ?raw .
BIND(CONTAINS(?raw, "<expected-fragment>") AS ?containsExpected)
}
<heading-headline> 和 <expected-fragment> 是教程占位符。该查询适合做针对性回归。它不替代完整测试,但能快速暴露关键切片 raw 是否符合作者预期。
10. 阅读与修改闭环
SPARQL 查询结果进入作者行动时,必须进入 source file 阅读、编辑和审计动作。
10.1. Source coordinate
TTL 中的 RDF fact 携带 source coordinate。aat:relativePath 相对于 documentRoot。Heading fact 使用 aat:headingLine 指向标题行。Slice、xref occurrence、payload 和 block fact 使用 aat:startLine、aat:endLine、aat:startColumn 和 aat:endColumn 指向该 source file 中的 authored surface。
path: books/<book-id>/chapters/03-query-contract.adoc
headingLine: 41
start: 42
end: 47
col: 12
修改执行者使用 path 打开 source file。标题定位使用 headingLine。xref occurrence 使用 start 和 col。payload、block raw 和 heading slice 使用 line span。
10.2. 查询后阅读
作者拿到查询结果后,先读取 source file 对应行范围:
path: books/<book-id>/chapters/03-query-contract.adoc
start: 191
end: 193
raw 字段保存对应 authored surface。作者可以用 raw 确认标题切片、段落、引用、payload 或附近文本是否为目标编辑对象。
10.3. 搜索确认
搜索确认使用 rg。搜索范围应限制在当前书籍目录或当前 bookshelf source tree 内。
按 headline 搜索:
rg -n "引用坐标" books/<book-id>
按 raw 片段搜索:
rg -n "参考坐标列于" books/<book-id>
按 selector 或 xref raw 搜索:
rg -n "<<target-selector>>|target-selector" books/<book-id>
搜索用于确认上下文、区分相似标题或检查引用表面。编辑动作仍以查询结果中的 source coordinate 为入口。
10.4. 修改后刷新
修改源书稿后,工作流必须刷新:
cd docs/bookshelf
mkdir -p build/rdf12/books
asciidoc-abundant-tree \
books/<book-id>/book.adoc \
--mode book-entry \
--document-root . \
--format rdf12 \
> build/rdf12/books/<book-id>.ttl
riot --validate build/rdf12/books/<book-id>.ttl
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?raw ?path ?start ?col WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:raw ?raw ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col .
?source aat:headline ?sourceHeadline .
FILTER NOT EXISTS { ?edge aat:targetHeading ?target . }
FILTER NOT EXISTS { ?edge aat:candidateHeading ?candidate . }
}
ORDER BY ?path ?start ?col
SPARQL
)
刷新后的查询结果才代表当前书稿事实。
10.5. 修改前邻域检查
修改一个被大量引用的标题前,应先查询它的反向邻域和入度。
反向邻域说明哪些标题引用它。入度说明它在交叉引用网络中的汇聚程度。入度高的标题修改 headline、address label 或章节位置时,应检查引用 selector 是否仍然成立。
10.6. 修改后审计
修改后至少运行以下查询:
-
来源文件确认。
-
全书骨架。
-
未绑定引用。
-
多义引用。
-
目标标题的反向引用。
这些查询构成轻量审计闭环。它们不证明书稿内容正确,但能发现结构和引用层面的常见破坏。
11. 跨项目用法
每个书架项目负责自己的 book entry、documentRoot 和 projection artifacts。asciidoc-abundant-tree 命令读取目标 book entry,并输出 source-aware RDF12 标题投影图。
11.1. 项目内书稿入口
标准书架的 book entry 位于目标 bookshelf root 下:
books/<book-id>/book.adoc
DocumentRoot 是目标 bookshelf root。TTL 位于目标项目自己的 build 目录:
/home/t103o/workbench/projects/ExampleProject/docs/bookshelf/build/rdf12/books/<book-id>.ttl
11.2. 投影命令
在目标 bookshelf root 中运行投影命令:
cd /home/t103o/workbench/projects/ExampleProject/docs/bookshelf
mkdir -p build/rdf12/books
asciidoc-abundant-tree \
books/<book-id>/book.adoc \
--mode book-entry \
--document-root . \
--format rdf12 \
> build/rdf12/books/<book-id>.ttl
该命令让 TTL 留在目标项目自己的 build 目录下。
11.3. TTL 放置规则
TTL 放在目标项目的 build 目录下:
docs/bookshelf/build/rdf12/books/<book-id>.ttl
该位置表达 TTL 是目标书的 projection artifact。TTL 不放入源书稿目录,不放入仓库根目录,不作为手写文档维护。
11.4. 查询语句复用
查询语句可以从本书复制到目标项目的终端会话中执行。
通用查询语句使用 <heading-label> 等占位符。项目执行查询时,把占位符替换为当前书稿的 label、headline 或文本片段。
查询语句复用不改变 TTL 所属项目。使用 ARQ 命令表面时,--data 指向目标项目 TTL,--query 接收当前命令中的 SPARQL 文本。
11.5. 跨项目路径边界
投影器的 documentRoot 由 --document-root 指定。跨项目投影时,在目标 bookshelf root 下运行可以让 source coordinate 保持相对于目标 bookshelf root。
该规则让 TTL 中的 aat:relativePath 与目标项目的 source tree 对齐。
12. 查询语句集
查询语句集是本工作流的公共操作面。每个查询对应一个作者动作。查询使用 bash here-doc 表面,作者复制命令后直接执行。
12.1. 来源确认
确认 TTL 来源:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?path ?digest WHERE {
?sourceDoc a aat:AsciiDocSourceDocument ;
aat:relativePath ?path .
OPTIONAL { ?sourceDoc aat:sourceDigest ?digest . }
}
SPARQL
)
12.2. 目录骨架
读取全书骨架:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?level ?headline ?path ?headingLine WHERE {
?h a aat:Heading ;
aat:headingLevel ?level ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine .
FILTER(?level <= 1)
}
ORDER BY ?path ?headingLine
SPARQL
)
12.3. 标题 raw
按 address label 读取标题 raw:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?headline ?path ?start ?end ?raw WHERE {
?heading aat:addressLabel "<heading-label>" ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:endLine ?end ;
aat:raw ?raw .
}
SPARQL
)
<heading-label> 替换为当前书稿中的 address label。
12.4. 递归子树命令
按 address label 读取递归子树:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?node ?level ?headline ?path ?headingLine ?start ?end WHERE {
?root aat:addressLabel "<heading-label>" .
?root aat:containsDirectly* ?node .
?node aat:headingLevel ?level ;
aat:headline ?headline ;
aat:relativePath ?path ;
aat:headingLine ?headingLine ;
aat:startLine ?start ;
aat:endLine ?end .
}
ORDER BY ?path ?headingLine
SPARQL
)
该查询返回包含根标题的结构范围。
12.5. 全书 xref
读取全书 xref:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?targetHeadline ?path ?start ?col ?raw WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col ;
aat:raw ?raw .
?source aat:headline ?sourceHeadline .
OPTIONAL {
?edge aat:targetHeading ?target .
?target aat:headline ?targetHeadline .
}
}
ORDER BY ?path ?start ?col
SPARQL
)
12.6. 未绑定 xref
读取未绑定 xref:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?sourceHeadline ?selector ?raw ?path ?start ?col WHERE {
?edge a aat:XrefEdge ;
aat:sourceHeading ?source ;
aat:targetSelector ?selector ;
aat:raw ?raw ;
aat:relativePath ?path ;
aat:startLine ?start ;
aat:startColumn ?col .
?source aat:headline ?sourceHeadline .
FILTER NOT EXISTS { ?edge aat:targetHeading ?target . }
FILTER NOT EXISTS { ?edge aat:candidateHeading ?candidate . }
}
ORDER BY ?path ?start ?col
SPARQL
)
12.7. xref 入度
统计 xref 入度:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline (COUNT(?edge) AS ?inDegree) WHERE {
?heading a aat:Heading ;
aat:headline ?headline .
OPTIONAL {
?edge a aat:XrefEdge ;
aat:targetHeading ?heading .
}
}
GROUP BY ?heading ?headline
ORDER BY DESC(?inDegree) ?headline
SPARQL
)
12.8. xref 出度
统计 xref 出度:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?heading ?headline (COUNT(?edge) AS ?outDegree) WHERE {
?heading a aat:Heading ;
aat:headline ?headline .
OPTIONAL {
?edge a aat:XrefEdge ;
aat:sourceHeading ?heading .
}
}
GROUP BY ?heading ?headline
ORDER BY DESC(?outDegree) ?headline
SPARQL
)
12.9. 双向邻域
按 address label 读取双向邻域:
arq --data build/rdf12/books/<book-id>.ttl --query <(cat <<'SPARQL'
PREFIX aat: <https://micheng.dev/ns/asciidoc-abundant-tree#>
SELECT ?direction ?neighbor ?neighborHeadline ?edgePath ?edgeStart ?raw WHERE {
?root aat:addressLabel "<heading-label>" .
{
?edge a aat:XrefEdge ;
aat:sourceHeading ?root ;
aat:targetHeading ?neighbor ;
aat:relativePath ?edgePath ;
aat:startLine ?edgeStart ;
aat:raw ?raw .
BIND("out" AS ?direction)
}
UNION
{
?edge a aat:XrefEdge ;
aat:sourceHeading ?neighbor ;
aat:targetHeading ?root ;
aat:relativePath ?edgePath ;
aat:startLine ?edgeStart ;
aat:raw ?raw .
BIND("in" AS ?direction)
}
?neighbor aat:headline ?neighborHeadline .
}
ORDER BY ?direction ?edgePath ?edgeStart
SPARQL
)
12.10. 查询语句维护
查询语句中的 <heading-label> 是教程占位符。项目复制查询后替换为自己的 address label。
查询语句应保持 ARQ 原生可执行。包装脚本可以存在,但包装脚本不替代可复制的查询命令。
Appendix A: 术语表
- bookshelf
-
保存 AsciiDoc 书稿、共享属性、构建脚本和生成物的书架工作区。
- book entry
-
调用者显式传入的书稿入口 source file,标准书架中为
books/<book-id>/book.adoc。 - documentRoot
-
aat:relativePath的基准和 include path 的边界。 - source set
-
book entry 通过受支持 include directive 到达的 source files 集合。
- RDF 1.2 heading projection graph
-
从 book entry 和 documentRoot 生成的 RDF 1.2 标题投影图。它描述 heading node、标题结构边、xref edge evidence、payload、source file reconstruction surface 和 origin source coordinate。
- TTL projection artifact
-
RDF 1.2 标题投影图的 Turtle 文件。它位于 build 目录下,是可重新生成的 projection artifact。
- ARQ command surface
-
使用 Apache Jena ARQ 执行 SPARQL 查询的可复制命令表面。
- heading node
-
由 AsciiDoc 等号标题创建的 RDF resource。
- heading slice
-
heading node 对应的 source file 连续文本区域。
- xref edge evidence
-
保存 xref raw、source heading、target selector、target heading、候选 heading 和 source coordinate 的 RDF resource。
- target selector
-
xref 中用于绑定目标标题的 selector 字符串。
- address label
-
作者在标题表面显式声明的 ID。
- generated address label
-
解析器为没有显式 ID 的标题恢复出的可寻址 label。
- recursive query
-
使用 SPARQL property path 或显式多段模式读取多跳关系的查询。
- xref in-degree
-
指向某个 heading node 的已绑定 xref edge 数量。
- xref out-degree
-
从某个 heading node 发出的 xref edge 数量。
- xref neighborhood
-
围绕一个 heading node 的局部交叉引用网络,包括入边、出边和多跳邻居。
- source file reconstruction surface
-
RDF 投影中用于重建 source set 文件的
aat:SourceFile资源集合,每个资源保存 source file 的aat:relativePath和完整aat:raw。 - origin source coordinate
-
RDF fact 在 source file 中的
relativePath、heading line、line span、column span 和 raw 表面。 - projection artifact
-
由投影命令生成、可以删除并重新生成的文件。
- stale projection
-
source set 已改变,但 TTL 尚未重新生成的过期投影。
参考资料
-
[rdf12-concepts] W3C, RDF 1.2 Concepts and Abstract Data Model. https://www.w3.org/TR/rdf12-concepts/
-
[rdf12-turtle] W3C, RDF 1.2 Turtle. https://www.w3.org/TR/rdf12-turtle/
-
[sparql12-query] W3C, SPARQL 1.2 Query Language. https://www.w3.org/TR/sparql12-query/
-
[apache-jena-arq] Apache Jena, ARQ - A SPARQL Processor for Jena. https://jena.apache.org/documentation/query/
-
[apache-jena-riot] Apache Jena, RIOT RDF Parser. https://jena.apache.org/documentation/io/
-
[asciidoctor] Asciidoctor Documentation. https://docs.asciidoctor.org/
-
[ripgrep] BurntSushi, ripgrep. https://github.com/BurntSushi/ripgrep