Apache GraphAr 数据读写实战详解
使用 Apache GraphAr Python SDK(graphar 0.13.0.dev1)完成图数据读写实战:核心 API 概览、官方 ldbc_sample 数据集读取、自建图写出读回闭环,以及产出目录解析与避坑清单。
Apache GraphAr 数据读写实战详解
目标:用 Apache GraphAr 官方 Python SDK(PyPI 包名
graphar)完成两类最典型的实战——读取官方 ldbc_sample 图数据集、从已有元数据出发自建一张小图并完成「写出、读回」闭环,同时解析产出目录结构并沉淀实测踩坑点。全部代码与回显均在 Ubuntu 22.04(Python 3.10.12,graphar 0.13.0.dev1)真实环境跑通。
一、环境信息
本章实战依赖的软件与路径如下,所有回显均取自该环境的真实运行结果。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- Python:3.10.12,虚拟环境位于
/opt/venv-graphar - Apache GraphAr Python SDK:0.13.0.dev1(含 CLI 工具
graphar与 C++ 核心库绑定) - 官方源码:
/opt/incubator-graphar(commit 06dddb1) - 官方测试数据:
/opt/incubator-graphar/testing(含 ldbc_sample、modern_graph 等) - 实战工作目录:
/root/graphar-demo(自建图产出目录)
进入虚拟环境的验证命令与实测回显:
/opt/venv-graphar/bin/python -c "import graphar; import importlib.metadata as im; print('graphar version:', im.version('graphar'))"
graphar version: 0.13.0.dev1
💡 说明:graphar 包没有提供
__version__属性,官方推荐用标准库importlib.metadata读取版本号,如上实测。
二、前置条件
2.1 安装 graphar Python 包
GraphAr Python 包从 PyPI 安装即可,正确包名为 graphar。以下为从零创建虚拟环境到安装成功的完整命令与实测回显(包名陷阱排障见《Ubuntu 22.04 安装 Apache GraphAr 指南》)。
apt-get update && apt-get install -y python3 python3-venv python3-pip curl
Setting up python3-pip (22.0.2+dfsg-1ubuntu0.7) ...
Setting up python3-dev (3.10.6-1~22.04.1) ...
Processing triggers for libc-bin (2.35-0ubuntu3.15) ...
python3 -m venv /opt/venv-graphar && /opt/venv-graphar/bin/pip install --upgrade pip
Downloading pip-26.2.1-py3-none-any.whl (1.8 MB)
Found existing installation: pip 22.0.2
Uninstalling pip-22.0.2:
Successfully uninstalled pip-22.0.2
Successfully installed pip-26.2.1
/opt/venv-graphar/bin/pip install graphar
Collecting graphar
Downloading graphar-0.13.0.dev1-cp310-cp310-manylinux_2_28_x86_64.whl (15.3 MB)
Installing collected packages: typing-extensions, tomli, shellingham, pyyaml, pygments, pluggy, packaging, mdurl, iniconfig, annotated-types, annotated-doc, typing-inspection, pydantic-core, markdown-it-py, exceptiongroup, rich, pytest, pydantic, typer, graphar
Successfully installed annotated-doc-0.0.5 annotated-types-0.8.0 exceptiongroup-1.3.1 graphar-0.13.0.dev1 iniconfig-2.3.1 markdown-it-py-4.2.0 mdurl-0.1.2 packaging-26.3 pluggy-1.6.0 pydantic-2.11.10 pydantic-core-2.33.2 pygments-2.21.0 pytest-7.4.4 pyyaml-6.0.3 rich-15.0.0 shellingham-1.5.4 tomli-2.5.0 typer-0.27.3 typing-extensions-4.16.0 typing-inspection-0.4.4
安装验证:
/opt/venv-graphar/bin/pip show graphar
Name: graphar
Version: 0.13.0.dev1
Summary: An open source, standard data file format for graph data storage and retrieval.
Author-email: GraphAr community <dev@graphar.apache.org>
License: Apache-2.0
Location: /opt/venv-graphar/lib/python3.10/site-packages
Requires: pydantic, pytest, pyyaml, typer
Required-by:
⚠️ 注意:PyPI 上的
pygraphar是 GraphScope 的占位包,安装它会失败,切勿使用;PyPI wheel 的运行时依赖只有 typer、pydantic、pyyaml、pytest 四个(C++ 层已捆绑 Arrow),不需要单独安装 pyarrow/numpy/pandas。
2.2 获取官方源码与测试数据
官方示例脚本与测试数据在 GitHub 两个仓库中,浅克隆即可满足本文实战:
git clone --depth 1 https://github.com/apache/incubator-graphar.git /opt/incubator-graphar
Cloning into 'incubator-graphar'...
git clone --depth 1 https://github.com/apache/incubator-graphar-testing.git /opt/incubator-graphar/testing
克隆结果验证(源码提交版本与测试数据目录):
git -C /opt/incubator-graphar log -1 --oneline && ls /opt/incubator-graphar/testing/
06dddb1 fix(java): declare maven-install-plugin once in the java module (#985)
LICENSE
README.md
java
ldbc
ldbc_sample
modern_graph
nebula
neo4j
transformer
2.3 设置测试数据环境变量
官方示例脚本通过环境变量 GAR_TEST_DATA 定位测试数据根目录,运行示例前先导出:
export GAR_TEST_DATA=/opt/incubator-graphar/testing
该变量只影响官方示例与测试脚本,不影响 SDK 本身的读写 API;本文实战二的代码不依赖它。
三、核心 API 概览
3.1 API 全景
先看模块暴露的完整 API 面(实测):
/opt/venv-graphar/bin/python -c "import graphar; print([n for n in dir(graphar) if not n.startswith('_')])"
['AdjacentList', 'BuilderEdge', 'BuilderVertex', 'DataType', 'Edge', 'EdgeInfo', 'EdgeIter',
'EdgesBuilder', 'EdgesCollection', 'GraphInfo', 'Property', 'PropertyGroup', 'Vertex',
'VertexInfo', 'VertexIter', 'VerticesBuilder', 'VerticesCollection', 'annotations',
'graph_info', 'high_level']
按用途分为三组,后续小节逐一说明:
| 分组 | API | 作用 |
|---|---|---|
| 元数据 | GraphInfo、VertexInfo、EdgeInfo | 加载与组装图级、顶点级、边级元数据 |
| 读取 | VerticesCollection、EdgesCollection(high_level) | 以集合视图读回顶点与边数据 |
| 写出 | VerticesBuilder、EdgesBuilder(high_level) | 逐条组装顶点/边并写出为 Parquet 数据 |
3.2 元数据三件套:GraphInfo、VertexInfo、EdgeInfo
GraphInfo.load(path) 从图级 YAML 加载元数据,随后可按类型名取到 VertexInfo/EdgeInfo。三者对应磁盘上的三层 YAML 文件(字段详解见《Apache GraphAr 格式规格与元数据详解》),读取侧的典型入口是:
graph_info.get_name()、graph_info.get_prefix():图名与数据根前缀;graph_info.get_vertex_info(type)、graph_info.get_edge_info(src, edge, dst):取子元数据;vertex_info.get_chunk_size()、vertex_info.get_property_groups():分块大小与属性分组。
3.3 读取集合:VerticesCollection 与 EdgesCollection
高层读取 API 位于 graphar.high_level:
VerticesCollection.Make(graph_info, vertex_type):构建某顶点类型的集合,size()取总数,迭代得到Vertex对象,用vertex.id()与vertex.property(name)取值;EdgesCollection.Make(graph_info, src_type, edge_type, dst_type, adj_list_type):构建某边类型在某邻接表布局下的集合,迭代得到Edge对象,用edge.source()、edge.destination()、edge.property(name)取值。
3.4 写出构建器:VerticesBuilder 与 EdgesBuilder
写出侧通过工厂方法创建构建器:
VerticesBuilder.Make(vertex_info, dir, num_vertices):dir为输出目录(务必以斜杠结尾,见第七章排障点),之后BuilderVertex逐个AddProperty并AddVertex,最后Dump()落盘;EdgesBuilder.Make(edge_info, dir, adj_list_type, num_vertices):额外指定邻接表布局,BuilderEdge(src, dst)加属性后AddEdge,Dump()落盘。
两者都支持 SetValidateLevel(types.ValidateLevel.strong_validate) 打开强校验,让非法数据在写出前报错。
💡 技术拓展:
adj_list_type决定边数据在磁盘上的组织方式,可选值与经典图存储结构对应如下——
AdjListType 对应结构 特点 适用场景 unordered_by_source COO 按写入顺序存放边,无排序开销 追加写、全量扫描 ordered_by_source CSR 按源顶点排序 + offset 索引 沿出边遍历(图计算主流) ordered_by_dest CSC 按目的顶点排序 + offset 索引 沿入边遍历 本文实战二实际采用
ordered_by_dest(与官方测试代码一致),以验证 CSC 布局下读回的排序语义。
四、实战一:读取官方 ldbc_sample 数据集
4.1 示例代码
官方示例脚本位于 /opt/incubator-graphar/python/example/graph_info_example.py(元数据访问)与 high_level_example.py(数据读取),以下摘录与 API 直接相关的核心代码(完整脚本含 Apache License 头,见官方仓库):
import os
import graphar
import graphar.high_level as gar_api
import graphar.types as types
def load_graph_info():
test_data_dir = os.environ.get("GAR_TEST_DATA")
graph_yaml_path = os.path.join(
test_data_dir, "ldbc_sample/parquet/ldbc_sample.graph.yml")
graph_info = graphar.GraphInfo.load(graph_yaml_path)
print(f"Graph name: {graph_info.get_name()}")
print(f"Graph prefix: {graph_info.get_prefix()}")
return graph_info
def access_vertex_info(graph_info):
vertex_info = graph_info.get_vertex_info("person")
print(f"Vertex type: {vertex_info.get_type()}")
print(f"Chunk size: {vertex_info.get_chunk_size()}")
print(f"Vertex prefix: {vertex_info.get_prefix()}")
property_groups = vertex_info.get_property_groups()
for i, pg in enumerate(property_groups):
for prop in pg.get_properties():
print(f" - {prop.name}: {prop.type} (primary: {prop.is_primary})")
def access_edge_info(graph_info):
edge_info = graph_info.get_edge_info("person", "knows", "person")
print(f"Edge type: {edge_info.get_edge_type()}")
print(f"Source type: {edge_info.get_src_type()}")
print(f"Destination type: {edge_info.get_dst_type()}")
print(f"Chunk size: {edge_info.get_chunk_size()}")
print(f"Directed: {edge_info.is_directed()}")
adj_list_types = [
types.AdjListType.unordered_by_source,
types.AdjListType.ordered_by_source,
types.AdjListType.unordered_by_dest,
types.AdjListType.ordered_by_dest,
]
for adj_list_type in adj_list_types:
if edge_info.has_adjacent_list_type(adj_list_type):
adj_list = edge_info.get_adjacent_list(adj_list_type)
print(f" - {adj_list.get_type()}: {adj_list.get_file_type()}")
def demonstrate_vertices_collection(graph_info):
vertices = gar_api.VerticesCollection.Make(graph_info, "person")
print(f"Total number of vertices: {vertices.size()}")
count = 0
for vertex in vertices:
if count >= 5:
break
print(f"Vertex ID: {vertex.id()}, Name: {vertex.property('firstName')} {vertex.property('lastName')}")
count += 1
def demonstrate_edges_collection(graph_info):
edges = gar_api.EdgesCollection.Make(
graph_info, "person", "knows", "person", types.AdjListType.ordered_by_source)
print(f"Total number of edges: {edges.size()}")
count = 0
for edge in edges:
if count >= 5:
break
print(f"Edge: {edge.source()} -> {edge.destination()}, Creation Date: {edge.property('creationDate')}")
count += 1
4.2 运行元数据示例与实测回显
cd /opt/incubator-graphar/python/example && GAR_TEST_DATA=/opt/incubator-graphar/testing python graph_info_example.py
GraphAr GraphInfo Example
=========================
Graph name: ldbc_sample
Graph prefix: /opt/incubator-graphar/testing/ldbc_sample/parquet/
--- Vertex Info ---
Vertex type: person
Chunk size: 100
Vertex prefix: vertex/person/
Properties:
Group 0:
- id: <graphar._core.DataType object at 0x708598342170> (primary: True)
Group 1:
- firstName: <graphar._core.DataType object at 0x708598341b70> (primary: False)
- lastName: <graphar._core.DataType object at 0x708598341b70> (primary: False)
- gender: <graphar._core.DataType object at 0x708598341b70> (primary: False)
--- Edge Info ---
Edge type: knows
Source type: person
Destination type: person
Chunk size: 1024
Directed: False
Adjacency list types:
- AdjListType.unordered_by_source: FileType.PARQUET
- AdjListType.ordered_by_source: FileType.PARQUET
- AdjListType.ordered_by_dest: FileType.PARQUET
Properties:
Group 0:
- creationDate: <graphar._core.DataType object at 0x7085983426b0>
Example completed successfully!
💡 说明:
- id: <graphar._core.DataType object at 0x708598342170>这类输出是官方示例直接打印 C++ 绑定对象造成的,属正常现象;类型本体可通过 YAML 元数据确认(如 id 为 int64)。
4.3 运行数据读取示例与实测回显
cd /opt/incubator-graphar/python/example && GAR_TEST_DATA=/opt/incubator-graphar/testing python high_level_example.py
GraphAr High-Level API Example
==============================
--- Vertices Collection ---
Total number of vertices: 903
Vertex ID: 0, Name: Mahinda Perera
Vertex ID: 1, Name: Eli Peretz
Vertex ID: 2, Name: Joseph Anderson
Vertex ID: 3, Name: Yacine Abdelli
Vertex ID: 4, Name: Jose Alonso
--- Edges Collection ---
Total number of edges: 6626
Edge: 0 -> 87, Creation Date: 2010-07-30T15:19:53.298+0000
Edge: 0 -> 623, Creation Date: 2012-04-12T13:56:58.931+0000
Edge: 0 -> 849, Creation Date: 2010-10-27T02:33:06.288+0000
Edge: 1 -> 58, Creation Date: 2011-07-27T15:43:14.869+0000
Edge: 1 -> 318, Creation Date: 2011-06-16T11:28:39.623+0000
Example completed successfully!
4.4 结果解读
- ldbc_sample 图含 903 个 person 顶点与 6626 条 knows 边,均从 Parquet 数据文件按元数据自动读出,无需人工指定任何文件路径;
- 顶点按 chunk_size 100 切成 10 个数据文件、边按 chunk_size 1024 分 part 存储,集合 API 屏蔽了全部分块细节;
EdgesCollection指定ordered_by_source布局后,返回顺序按源顶点 id 升序组织(0 的出边在前、1 的出边在后),与 CSR 语义一致。
五、实战二:自建图写出与读回闭环
5.1 完整代码
从零建图不要求手工编写全部元数据:实测验证的最短路径是「加载一份官方元数据,复用其 VertexInfo/EdgeInfo 组装自己的 GraphInfo,再用 Builder 写出数据」。以下脚本(保存为 /root/graphar_quickstart.py)完整演示该闭环:
import os
import shutil
import graphar as gar
import graphar.types as types
from graphar.high_level import (
VerticesCollection,
EdgesCollection,
BuilderVertex,
VerticesBuilder,
BuilderEdge,
EdgesBuilder,
)
TEST_DATA = "/opt/incubator-graphar/testing"
OUT = "/root/graphar-demo"
shutil.rmtree(OUT, ignore_errors=True)
shutil.rmtree(OUT + "vertex", ignore_errors=True)
shutil.rmtree(OUT + "edge", ignore_errors=True)
os.makedirs(OUT)
# 第 1 步:加载官方示例图元数据,确认加载通道可用
modern = gar.GraphInfo.load(f"{TEST_DATA}/modern_graph/modern_graph.graph.yml")
print("[1] GraphInfo loaded:", modern.get_name())
print(" vertex types:", [vi.get_type() for vi in modern.get_vertex_infos()])
print(" edge types :",
[(ei.get_src_type(), ei.get_edge_type(), ei.get_dst_type())
for ei in modern.get_edge_infos()])
# 第 2 步:复用 ldbc_sample 的 VertexInfo/EdgeInfo 组装新 GraphInfo 并保存三层 YAML
ldbc = gar.GraphInfo.load(
f"{TEST_DATA}/ldbc_sample/parquet/ldbc_sample.graph.yml")
vertex_info = ldbc.get_vertex_info("person")
edge_info = ldbc.get_edge_info("person", "knows", "person")
graph_info = gar.GraphInfo("demo_graph", [vertex_info], [edge_info],
prefix=OUT + "/", version=vertex_info.version())
# save() 生成的 graph.yml 固定按 <类型名>.vertex.yaml / <类型名>.edge.yaml 引用子文件,
# 三个文件名必须与该规则一致,否则重新 load 时会报 IOError 打不开 person.vertex.yaml
vertex_info.save(OUT + "/person.vertex.yaml")
edge_info.save(OUT + "/person_knows_person.edge.yaml")
graph_info.save(OUT + "/demo_graph.graph.yml")
print("[2] metadata yaml saved to", OUT, "->", sorted(os.listdir(OUT)))
# 第 3 步:VerticesBuilder 写出 3 个顶点(目录参数务必带尾斜杠,见第七章排障点)
vertices_data = [
(0, "John", "Smith", "male"),
(1, "Jane", "Doe", "female"),
(2, "Alice", "Wonderland", "female"),
]
vbuilder = VerticesBuilder.Make(vertex_info, OUT + "/", 0)
vbuilder.SetValidateLevel(types.ValidateLevel.strong_validate)
for vid, fn, ln, g in vertices_data:
v = BuilderVertex()
v.AddProperty("id", vid)
v.AddProperty("firstName", fn)
v.AddProperty("lastName", ln)
v.AddProperty("gender", g)
vbuilder.AddVertex(v)
print("[3] vertices to write:", vbuilder.GetNum())
vbuilder.Dump()
print(" Dump OK ->", OUT)
# 第 4 步:EdgesBuilder 写出 4 条边(ordered_by_dest 布局)
edges_data = [
(1, 0, "2010-01-01"),
(0, 1, "2011-01-01"),
(0, 2, "2012-01-01"),
(2, 1, "2013-01-01"),
]
ebuilder = EdgesBuilder.Make(edge_info, OUT + "/",
types.AdjListType.ordered_by_dest,
len(vertices_data))
ebuilder.SetValidateLevel(types.ValidateLevel.strong_validate)
for s, d, date in edges_data:
e = BuilderEdge(s, d)
e.AddProperty("creationDate", date)
ebuilder.AddEdge(e)
print("[4] edges to write:", ebuilder.GetNum())
ebuilder.Dump()
print(" Dump OK ->", OUT)
# 第 5 步:重新加载元数据并读回全部数据验证
gi2 = gar.GraphInfo.load(OUT + "/demo_graph.graph.yml")
vc = VerticesCollection.Make(gi2, "person")
print("[5] vertices read back:", vc.size())
for v in vc:
print(" vertex id=%s name=%s %s gender=%s" % (
v.property("id"), v.property("firstName"),
v.property("lastName"), v.property("gender")))
ec = EdgesCollection.Make(gi2, "person", "knows", "person",
types.AdjListType.ordered_by_dest)
print(" edges read back:", ec.size())
for e in ec:
print(" edge %s -> %s creationDate=%s" % (
e.source(), e.destination(), e.property("creationDate")))
print("ALL DONE")
5.2 运行与实测回显
/opt/venv-graphar/bin/python /root/graphar_quickstart.py
[1] GraphInfo loaded: modern_graph
vertex types: ['person', 'software']
edge types : [('person', 'knows', 'person'), ('person', 'created', 'software')]
[2] metadata yaml saved to /root/graphar-demo -> ['demo_graph.graph.yml', 'person.vertex.yaml', 'person_knows_person.edge.yaml']
[3] vertices to write: 3
Dump OK -> /root/graphar-demo
[4] edges to write: 4
Dump OK -> /root/graphar-demo
[5] vertices read back: 3
vertex id=0 name=John Smith gender=male
vertex id=1 name=Jane Doe gender=female
vertex id=2 name=Alice Wonderland gender=female
edges read back: 4
edge 1 -> 0 creationDate=2010-01-01
edge 0 -> 1 creationDate=2011-01-01
edge 2 -> 1 creationDate=2013-01-01
edge 0 -> 2 creationDate=2012-01-01
ALL DONE
写出与读回全部成功。注意边读回顺序为 1→0、0→1、2→1、0→2:ordered_by_dest 布局先按目的顶点排序(0、1、1、2),同目的内再按源顶点排序,符合 CSC 语义。
5.3 排障实录一:save() 的子文件命名固定为 .yaml 后缀
首次运行本脚本时第 2 步把顶点元数据保存成了 person.vertex.yml,随后重新 load 报错:
ValueError: IOError: Failed to open local file '/root/graphar-demo/person.vertex.yaml'. Detail: [errno 2] No such file or directory
定位过程:检查 graph_info.save() 生成的 demo_graph.graph.yml 内容,发现其中对子元数据的引用固定写作 person.vertex.yaml、person_knows_person.edge.yaml,与保存时使用的 .yml 后缀不一致。结论:使用 GraphInfo.save() 时,子元数据文件必须按 <类型名>.vertex.yaml、<源类型>_<边类型>_<目的类型>.edge.yaml 命名保存,改齐后问题消失。
5.4 排障实录二:Builder 目录参数必须带尾斜杠
第二次运行时把 Builder 的目录参数写成了 OUT(即 /root/graphar-demo,不带尾斜杠),Dump() 照样返回成功,但读回阶段报错:
RuntimeError: IOError: Failed to open local file '/root/graphar-demo/vertex/person/vertex_count'. Detail: [errno 2] No such file or directory
排查:用 find 检查磁盘,数据实际被写到了 /root/graphar-demovertex/person/vertex_count 与 /root/graphar-demoedge/ 目录下——Builder 的目录参数与元数据 prefix 是纯字符串拼接,目录不带尾斜杠会静默丢失路径分隔符(0.13.0.dev1 实测行为)。修正为统一传 OUT + "/" 后,写出与读回路径一致,闭环通过。
⚠️ 注意:这类「写偏了但报成功」的问题读回端才会暴露,建议每次 Dump 后用
find <输出目录> -type f核对实际落盘位置(见第六章)。
5.5 闭环验证
最后用 CLI 校验自建图的元数据规范性:
graphar check -p /root/graphar-demo/demo_graph.graph.yml
INFO Graph is valid
六、产出目录结构解析
自建图闭环跑通后,/root/graphar-demo 的完整产出如下(实测):
find /root/graphar-demo -type f | sort
/root/graphar-demo/demo_graph.graph.yml
/root/graphar-demo/edge/person_knows_person/ordered_by_dest/adj_list/part0/chunk0
/root/graphar-demo/edge/person_knows_person/ordered_by_dest/creationDate/part0/chunk0
/root/graphar-demo/edge/person_knows_person/ordered_by_dest/edge_count0
/root/graphar-demo/edge/person_knows_person/ordered_by_dest/offset/chunk0
/root/graphar-demo/edge/person_knows_person/ordered_by_dest/vertex_count
/root/graphar-demo/person.vertex.yaml
/root/graphar-demo/person_knows_person.edge.yaml
/root/graphar-demo/vertex/person/firstName_lastName_gender/chunk0
/root/graphar-demo/vertex/person/id/chunk0
/root/graphar-demo/vertex/person/vertex_count
逐项说明:
| 文件/目录 | 作用 |
|---|---|
| demo_graph.graph.yml | 图级元数据,引用下面两个子元数据文件 |
| person.vertex.yaml | person 顶点类型元数据(chunk_size、属性分组) |
| person_knows_person.edge.yaml | knows 边类型元数据(三种邻接表声明、属性) |
| vertex/person/<属性组prefix>/chunk0 | 顶点属性数据,每个属性组一个目录,按 chunk 切分 |
| vertex/person/vertex_count | 顶点计数文件(8 字节定长,本图为 3) |
| edge/person_knows_person/ordered_by_dest/adj_list/part0/chunk0 | 邻接表本体(源/目的顶点索引对) |
| edge/person_knows_person/ordered_by_dest/creationDate/part0/chunk0 | 边属性数据,行序与邻接表一致 |
| edge/person_knows_person/ordered_by_dest/offset/chunk0 | CSC 式 offset 索引 |
| edge/person_knows_person/ordered_by_dest/edge_count0 | 边计数文件(本图为 4) |
| edge/person_knows_person/ordered_by_dest/vertex_count | 参与该布局的目的顶点段计数 |
数据文件虽无 .parquet 扩展名,但实测文件头尾均为 Parquet 魔数(PAR1),任何 Parquet 读取器都能直接打开。
七、注意事项
以下均为本次实战实测确认的行为约束,写代码前对照检查可避免大部分返工:
- 包名必须是
graphar:pygraphar是 GraphScope 的占位包,安装直接失败; - save() 子文件命名:
GraphInfo.save()生成的图级 YAML 固定按<类型名>.vertex.yaml、<源>_<边>_<目的>.edge.yaml引用子文件,自存元数据必须用同名文件; - Builder 目录参数带尾斜杠:
VerticesBuilder.Make/EdgesBuilder.Make的目录参数与 prefix 是字符串直拼,不带/会静默写到错位路径且 Dump 不报错; - DataType 无工厂方法:Python 绑定不能凭空构造属性类型,从零建图请加载官方或已有元数据后复用其 VertexInfo/EdgeInfo(实测
Property("id", None)能构造但 save 校验会报 vertex info not validated); - GraphInfo.prefix 决定读写闭环:自建图把
GraphInfo的 prefix 设为绝对输出目录(带尾斜杠),Builder 写出与 Collection 读回才能对齐同一路径; - 边读回顺序由布局决定:ordered_by_dest 下先按目的顶点、再按源顶点排序,业务侧不要假设与写入顺序一致;
- 强校验建议常开:
SetValidateLevel(types.ValidateLevel.strong_validate)能在写出前拦截非法数据,代价可忽略。
八、参考链接
- 官方 Python 示例目录:https://github.com/apache/incubator-graphar/tree/main/python/example
- PyPI 包页面:https://pypi.org/project/graphar/
- 官方测试数据仓库:https://github.com/apache/incubator-graphar-testing
- Apache GraphAr 官网:https://graphar.apache.org/
- 同系列文档:《Apache GraphAr 格式规格与元数据详解》《Apache GraphAr 常用 CLI 命令与工具详解》