三元组字段应缩写为s、p、o以降低存储开销,p字段必须单独建索引;属性型与关系型三元组宜分集合存储;多值宾语须拆分为多条独立三元组;实体类型信息不应冗余存于三元组文档内,而应通过$lookup关联实体集合。

三元组字段命名要不要严格对齐SPO结构
直接用 subject、predicate、object 作为字段名看似规范,但实际会带来查询和索引负担。MongoDB 的 BSON 键名会随每条文档重复存储,字段名越长,磁盘和内存开销越大;更关键的是,predicate 值通常为字符串(如 "成立时间"、"总部地点"),高频用于聚合和 $lookup,若不加索引,$group 或 $match 性能骤降。
建议统一缩写为 s、p、o,配合注释或 schema 文档说明含义。若业务方强依赖可读性,至少把 p 单独建索引:db.triples.createIndex({ p: 1 })。注意避免在 o 上建通配索引——它可能存日期、ID、文本甚至嵌套对象,类型混杂会导致索引膨胀且失效。
实体与属性三元组是否该拆成不同集合
知识图谱中两类三元组常混在一起:(公司, 成立时间, 2010) 是属性型,(公司, 投资了, 初创企业) 是关系型。硬塞进同一集合会引发三个问题:查询时无法按语义过滤、o 字段类型不可控、后续想导出 RDF 时需额外标注 type 字段区分。
推荐按谓词语义分集合,例如:entity_attrs 存属性三元组,entity_relations 存关系三元组。这样既能为各自 p 字段定制索引(比如 entity_attrs.p 索引只覆盖几十个预设属性),也能在应用层做权限隔离(如属性数据可公开,关系数据需鉴权)。不要为了“省一个集合”牺牲后期维护成本。
如何处理多值宾语(如一个人有多个电话)
不能把多个电话拼成字符串存进 o,否则无法原子性增删、无法按单个号码查、无法建精确索引。常见错误是存成数组:{ s: "张三", p: "联系电话", o: ["138...", "159..."] } —— 这看似合理,但 MongoDB 数组字段的索引是多键索引(multikey),一旦 o 数组长度超过一定规模(比如 >10),索引条目数爆炸,写放大严重。
正确做法是**一条三元组只表达一个事实**:{ s: "张三", p: "联系电话", o: "138..." }{ s: "张三", p: "联系电话", o: "159..." }
再配合复合索引 db.triples.createIndex({ s: 1, p: 1, o: 1 }),既支持按主体+关系查所有值,也支持按主体+关系+具体值精准定位。
要不要在文档里冗余存储实体类型或本体信息
比如给每个 s 都加上 s_type: "Company" 字段。这看起来方便过滤,但容易导致数据不一致:当某实体在别处被重标类型时,这些三元组不会自动同步。更糟的是,如果类型信息来自外部本体(如 Schema.org),硬编码进文档会让升级本体版本变得极难。
真正可控的做法是:只在源头实体集合(如 entities)里存 type 和 iri,三元组集合通过 s 字段引用该 ID;查询时用 $lookup 关联。虽然多一次 pipeline 阶段,但换来的是类型定义集中管控。如果性能敏感,可将常用类型字段投影到聚合结果中,而非固化在三元组文档内。
{s:"A", p:"成立时间", o:"2010"},这类逻辑得在写入前由服务层拦截。


















