RDF 数据模型:三元组、资源、属性、字面量

学 SPARQL 前,先得搞懂 RDF 数据模型。说白了吧——RDF 就是一张超级大网,里头每个节点都绑着一条条“主语-谓语-宾语”的边。你在药物图谱里查药、查靶点、查通路,本质上都是在遍历这张网。我刚开始接触药物图谱时,也傻傻地以为 RDF 就是个带属性的 JSON,结果写出来的 SPARQL 查询要么漏数据,要么跑半天。后来才明白,**抓住三元组,就抓住了 RDF 的灵魂**。

三元组:RDF 最小的叙事单元

一条 RDF 数据就长这样:主语 – 谓语 – 宾语。主语是资源,谓语是属性,宾语是另一个资源或一个字面量。你看,三条信息就能表达一个事实。比如:

阿司匹林 – 治疗 – 头痛
阿司匹林 – 不良反应 – 胃出血

这里“阿司匹林”“头痛”“胃出血”都是资源(后面再细说),“治疗”“不良反应”是属性。三条东西拼在一起,就成了计算机能理解的断言。我曾帮一个制药团队从结构化文档里抽了 20 万条这种三元组,最后用一条 SPARQL 就把所有非甾体抗炎药的交叉副作用找出来了。说实话,当时的感觉就像把一屋子散落的手稿编成了索引卡片。

位置含义在药物图谱中的例子
主语(Subject)描述的对象药物、靶点、疾病、基因
谓语(Predicate)对象之间的关系或属性治疗、抑制、互作
宾语(Object)关系的目标值另一个资源或字面量

一条原则:每个事实只用一个三元组表达。 你想想看,一个药能治多个病,那就要拆成多条。RDF 不怕数据量大,就怕语义模糊。

资源:图谱里的每个“东西”

资源(Resource)就是 RDF 世界里的实体。可以是物理对象(阿司匹林片)、概念(头痛)、数字标识(UniProt 编号),甚至是一段描述。每个资源都有一个 IRI(国际资源标识符),类似 Web 上的 URL。比如:

<http://example.org/drug/Aspirin>
<http://identifiers.org/uniprot/P35354>

IRI 保证了全局唯一——你把两个图的 IRI 放一起对比,就知道是不是在说同一个东西。我遇到过最坑的事,就是两个团队分别爬了 DrugBank 和 ChEMBL,把同一个药「阿司匹林」写成了不同的 IRI。后来用了一个简单的 owl:sameAs 对齐,才把图谱合起来。嗯,这里要记住:IRI 是 RDF 的身份证明,别手写,用标准命名空间。

属性:连接资源的“关系线”

属性(Property)定义在谓语位置上。它本身也是一个资源——有 IRI,可以被定义、被约束。在药物图谱里,常见属性和它们的 IRI 前缀:

  • rdf:type – 实例与类的归属关系(阿司匹林 rdf:type 药物)
  • rdfs:label – 人类可读的名称
  • obo:RO_0002451 – 治疗关系(OBO Foundry 标准)
  • bio:hasTarget – 药物针对的靶点

我个人习惯在建模时先把属性清单列出来,用表格控一下命名空间和定义域/值域。下面是我整理的一张药物图谱常用属性表(节选):

属性IRI 示例预期主语类型预期宾语类型
治疗obo:RO_0002451药物疾病
靶点bio:hasTarget药物基因/蛋白质
副作用obof:hasSideEffect药物不良反应事件
互作bio:interactsWith蛋白质A蛋白质B

曾踩过的坑: 把属性当作字面量来写。比如把“治疗”直接写成 "治疗"^^xsd:string 而不是一个 IRI。结果 SPARQL 查也查不到,因为 RDF 里属性必须用 IRI 表示。记住:属性和资源一样,都是资源,都得有 IRI。

字面量:储存具体值的“叶子”

字面量(Literal)是三元组宾语的另一种形式——它是 具体的值,比如字符串、数字、日期。字面量不能当作主语或谓语,只能当宾语。常见类型:

"布洛芬"^^xsd:string
"200 mg"^^xsd:string
"2024-01-15"^^xsd:date
3.14^^xsd:float

字面量可以加语言标签:"头痛"@zh、"headache"@en。这在多语言药物标签里特别好使。你想想看,一个药在不同的国家注册名称不同,但 IRI 是同一个——通过字面量的语言标签就能把多语言描述挂上去。

我曾经处理过一个跨国临床数据合并项目,不同来源的药物剂量写到不同的单位里:mg、μg、µmol。要是不给每个量配一个 xsd:float 并加上单位属性,查出来的结果就是一团乱码。所以我的建议是:字面量一定要标类型,数字必须带单位,文本尽量打语言标签。

一张图看懂三部曲

把上面三个概念串起来——直接看下面这张 RDF 数据模型结构图。我用一个简单的药物图谱片段来展示:

RDF 三元组结构:资源-属性-字面量 主语(资源) 阿司匹林 IRI 谓语(属性) 治疗 IRI 宾语(资源) 头痛 IRI 谓语(属性) 剂量 IRI 宾语(字面量) "500 mg"^^xsd:string 一条三元组 = 主语 + 谓语 + 宾语 宾语可以是另一个资源(绿色框),也可以是字面量(青色框)

核心摘要

  • RDF 数据模型的最小单元是三元组(Subject - Predicate - Object)
  • 资源必须有 IRI,属性也是资源,必须用 IRI 表示
  • 字面量是纯值,不能当主语/谓语,记得标类型和语言标签
  • 一张药物图谱就是几百万条这样的三元组拼成的——SPARQL 就是在这张网上做“SQL 式的图查询”

为什么偏要搞这套模型?

我猜你会问:用关系数据库存药物数据不香吗?对于固定格式、强模式的表,确实香。但药物领域的数据异构性强:同一个药,可能被不同数据库用不同的字段、不同的单位、不同的关系网络描述。RDF 的优势在于:你可以在不改变已有数据模式的前提下,随时加新的属性、加新的资源。举个例子:你今天突然知道阿司匹林还能降低某酶的表达水平,就在现有三元组集里加一条:

阿司匹林 – 下调表达 – COX-2

不需要改表结构,不需要写 migration,直接插入。这就是 RDF 的“schema-last”哲学——先有数据,再有模型。我记得第一次跟药企的数据架构师聊这个时,他们惊呼“那我不需要提前设计几十个表了?!” 确实,但这也会带来查询效率隐患——所以我们才需要 SPARQL 来高效检索这张网。