本节摘要:向量之外,polyglot 存储还有两翼。
graph_store/(11 个文件)统一了 4 种属性图数据库——Neo4j、FalkorDB、Apache AGE、Amazon Neptune(amazon_neptune.py 约 1800 行,IAM 签名认证是四库中最重的实现);triplet_store/(15 个文件)统一了 RDF 三元组库——内嵌零依赖的 Oxigraph,加上 Blazegraph、Jena、RDF4J(另有 Anzo 共 5 个后端)。本节拆两条分发链与各自的管理器体系,讨论属性图 LPG 与 RDF 语义网的选型,并说明:正是 RDF 库让 Semantica 真正"语义网兼容"。
内容来源:原项目源码
semantica/graph_store/graph_store.py、neo4j_store.py、amazon_neptune.py、query_sanitize.py、semantica/triplet_store/triplet_store.py、oxigraph_store.py、query_engine.py、bulk_loader.py、sparql_escaping.py。
⚠️ 注意:图库与三元组库是两个独立模块、两套接口,不能互相顶替——属性图查 Cypher 走
graph_store,SPARQL 走triplet_store;同一份知识图谱若要两边都落,得靠第 9 章 export 模块做格式转换,而不是指望某个"万能后端"。
阅读完本节,你应当能够:
GraphStore 的 4 个后端与分发逻辑,以及四个管理器(Node/Relationship/Query/Analytics)的分工。TripletStore.SUPPORTED_BACKENDS 的 5 个名字,理解 Oxigraph"内嵌零依赖"意味着什么。add_skos_concept 翻译成标准三元组。GraphStore 门面(graph_store.py:525)的 docstring 只承诺 Neo4j 与 FalkorDB,实际分发链覆盖四家(graph_store.py:564-596):
566 if self.backend == "neo4j": 568 from .neo4j_store import Neo4jStore 569 neo4j_config = graph_store_config.get_neo4j_config() 570 neo4j_config.update(self.config) 571 self._store_backend = Neo4jStore(**neo4j_config) 573 elif self.backend == "falkordb": 575 from .falkordb_store import FalkorDBStore ... 580 elif self.backend == "neptune" or self.backend == "amazon_neptune": 582 from .amazon_neptune import AmazonNeptuneStore ... 587 elif self.backend == "age" or self.backend == "apache_age": 589 from .age_store import ApacheAgeStore ... 594 else: 594 raise ValidationError(f"Unknown backend: {self.backend}")
三个细节:后端名支持别名(neptune/amazon_neptune、age/apache_age);默认值取自 graph_store_config.get("default_backend", "neo4j")(542-546 行);每家先从 config 模块取默认配置再被调用方覆盖(569-570 行的 merge 顺序)。四个后端文件体量都不小——neo4j_store.py 1066 行、falkordb_store.py 1025 行、age_store.py 1343 行、amazon_neptune.py 1793 行:图库适配的复杂度远高于向量库,因为除增删查外还要对齐索引、批量导入与分析接口。Apache AGE 值得一提:它是 PostgreSQL 的开源扩展,图数据直接住在你已有的 PG 里——与 01 节 pgvector 是"复用 PG"思路在图世界的镜像。
GraphStore 把操作拆给四个内聚的管理器(graph_store.py):
create/create_batch/get/update/delete,节点 CRUD 与批量;create/get/delete,边操作;execute(255 行)跑 Cypher,自带查询缓存——_generate_cache_key(289 行)把查询串参数化成 key,enable_cache/disable_cache/clear_cache(300-311 行)三个开关随手控制;shortest_path(326 行)、get_neighbors(351 行)、degree_centrality(376 行)、connected_components(432 行)——第 3 章图分析调用正是这层。GraphManager(482 行)再包一层 get_stats/create_index 给统计与索引入口。安全闸在门外:query_sanitize.py(仅 32 行)专给查询入参消毒。这个"门面只做分发、管理器按对象正交拆分、后端各自实现"的结构,是读任何 *_store.py 前都该装进脑子的地图。
amazon_neptune.py 模块头(1-15 行)点明两个特殊性:
IAM authentication using AWS SigV4 signing via AuthManager / OpenCypher query language support via Bolt protocol
Neptune 复用 Neo4j 的 Bolt 驱动,但连接前要 AWS SigV4 签名——模块内置 NeptuneAuthTokenManager(IAM 认证处理器),Key Features 列表写明"automatic retry with backoff / connection recovery / token refresh"(自动重试退避、连接恢复、token 刷新)。用起来是纯配置问题(模块 docstring 示例):
>>> store = AmazonNeptuneStore( ... endpoint="your-neptune-cluster.region.neptune.amazonaws.com", ... port=8182, region="us-east-1", iam_auth=True) >>> store.connect() >>> node_id = store.create_node(labels=["Person"], properties={"name": "Alice"})
存在意义与 pgvector 同构:把第 3 章 GraphBuilder 产出的图谱原样搬进云托管图数据库,业务代码零改动。代价是适配层最重——1793 行里大半在处理认证、重试与连接生命周期。
TripletStore(triplet_store.py:40)的官方清单比图库还多一个:
48 SUPPORTED_BACKENDS = {"blazegraph", "jena", "rdf4j", "anzo", "oxigraph"} 51 NAMED_GRAPH_CAPABLE_BACKENDS = { 52 "blazegraph", "rdf4j", "anzo", "oxigraph", 53 }
主推四家之外还有 Anzo;NAMED_GRAPH_CAPABLE_BACKENDS 记录谁支持命名图(RDF 的"图中之图",同一库内做数据集隔离——多租户/多项目的 RDF 版答案)。_initialize_store_backend(96-163 行)里,服务型后端都有默认端点:Blazegraph http://localhost:9999/blazegraph、Jena http://localhost:3030/ds、RDF4J http://localhost:8080/rdf4j-server,endpoint 参数可覆盖。而 Oxigraph 不需要端点——它是内嵌的:
69 self.store = self._oxigraph.Store(store_path)
一行就是全部魔法。Oxigraph 是 Rust 实现的嵌入式 RDF 库(Rust 编译的 Python 绑定),像 SQLite 之于 MySQL——pip install 完直接在进程内起一个支持 SPARQL 的三元组库,store_path 给了就落盘、不给就内存,失败时 71-74 行还会报出初始化位置。这是"无 key 跑通"承诺在 RDF 侧的答案:不装任何服务器,也能拥有一个真·语义网数据库。
门面层的能力(triplet_store.py):store(165 行,写入时 _resolve_iri 把本地名解析成 IRI)、add_triplet/add_triplets/get_triplets/delete_triplet/update_triplet(345-450 行一组标准 CRUD)、execute_query(450 行跑 SPARQL)、compute_delta(656 行,对比两组 SPARQL 结果算三元组级增量——审计与同步都吃这个)。构造时还挂上两个组件:QueryEngine(query_engine.py,571 行,查询构造与执行)与 BulkLoader(bulk_loader.py,370 行,批量装载)。防御与效率各有一位专职:sparql_escaping.py(195 行,SPARQL 注入防护——与图库的 query_sanitize 同一思路)与 construct_templates.py(828 行,预置常用 SPARQL 构造模板)。
RDF 库最区别于图库的一点:它对 W3C 标准词汇表是"母语"。add_skos_concept(triplet_store.py:504-560)把一个 SKOS 概念翻译成一组标准三元组:
548 triplets: List[Triplet] = [ 549 # Scheme declaration 550 Triplet(scheme_uri, RDF_TYPE, f"{SKOS}ConceptScheme"), 551 # Concept core 552 Triplet(concept_uri, RDF_TYPE, f"{SKOS}Concept"), 553 Triplet(concept_uri, f"{SKOS}inScheme", scheme_uri), 554 Triplet(concept_uri, f"{SKOS}prefLabel", pref_label), 555 ] 556 for lbl in (alt_labels or []): 557 triplets.append(Triplet(concept_uri, f"{SKOS}altLabel", lbl)) 558 for uri in (broader or []): 558 triplets.append(Triplet(concept_uri, f"{SKOS}broader", uri))
签名里的 alt_labels/broader/narrower/related/definition/notation 逐个落成 skos: 谓词,ConceptScheme 不存在还会自动建(544-547 行 docstring 声明)。"同义词"(altLabel)、"上下位"(broader/narrower)、"关联"(related)全部变成可 SPARQL 查询的数据——get_skos_concepts(570 行)读回,第 9 章本体工程的 SKOS 词表就落在这里。
属性图(LPG)与 RDF 是知识图谱两大建模阵营,Semantica 两个都接:
| 维度 | graph_store(LPG) | triplet_store(RDF) |
|---|---|---|
| 模型 | 节点/边都带属性 | 三元组 s-p-o,边属性需物化或 RDF-star |
| 查询语言 | Cypher / OpenCypher | SPARQL |
| 标准词汇 | 无强制,标签随意 | OWL 本体、SKOS 词表、SHACL 约束原生兼容 |
| 工具生态 | Neo4j Browser、图可视化 | Protégé、语义网推理机、链接数据平台 |
| 典型场景 | 实时图应用、GraphRAG 多跳、图算法 | 数据集成、跨组织互通、受控词表、合规发布 |
| 起步成本 | 起 Neo4j/FalkorDB 服务 | Oxigraph 零依赖内嵌,一行起库 |
选型心法:图给你自己用、要性能与灵活标签——LPG;图要发给别的系统/组织用、要对齐 W3C 标准——RDF。Semantica 的答案是不必二选一:第 3 章的 KnowledgeGraph 是中立数据结构,第 9 章 export 既能出 LPG(Cypher/GraphML)也能出 RDF(Turtle/JSON-LD/N-Triples),落库时按场景各取所需。
💡 装配要点:两条对称的分发链——
GraphStore._initialize_store_backend(graph_store.py:564)与TripletStore._initialize_store_backend(triplet_store.py:96)都是"config 读默认→按名 import→合并配置→实例化"。差异在生态位:图库四管理器对齐 Cypher 世界;三元组库的 QueryEngine/BulkLoader/construct_templates/sparql_escaping 对齐 SPARQL 世界。Oxigraph 用第 69 行那一行Store(store_path)兜住"语义网兼容"的最低门槛。
ValidationError。Store(store_path) 起库;命名图能力按后端声明。add_skos_concept 把概念/同义词/层级/关联翻成 skos: 三元组,语义网兼容落到数据层。下一节:02 节看到了"一行参数切换后端"的现象,03 节拆它的机关——
MethodRegistry与PluginRegistry:注册表模式如何让多后端即插即用,又如何成为你自己写扩展的正规入口。