# TriviumDB API 完整参考
> **版本**: v0.4.6
> **语言**: Rust 核心 + Python 绑定 (PyO3) + Node.js 绑定 (napi-rs)
> **许可**: Apache-2.0
---
## 目录
- [数据库生命周期](#数据库生命周期)
- [节点 CRUD](#节点-crud)
- [图谱操作](#图谱操作)
- [向量检索](#向量检索)
- [元数据过滤](#元数据过滤)
- [Cypher 图谱查询](#cypher-图谱查询)
- [持久化与压缩](#持久化与压缩)
- [内存管理](#内存管理)
- [工具方法](#工具方法)
- [维度迁移](#维度迁移)
- [事务支持](#事务支持-rust-only)
- [Pythonic 魔术方法](#pythonic-魔术方法)
- [数据类型说明](#数据类型说明)
---
## 数据库生命周期
### Python
```python
import triviumdb
# 基础打开方式(默认 f32 向量、1536 维、normal 同步模式)
db = triviumdb.TriviumDB("my_data.tdb", dim=1536)
# 完整参数
db = triviumdb.TriviumDB(
path="my_data.tdb", # 文件路径(不存在则新建)
dim=1536, # 向量维度(一旦创建不可更改)
dtype="f32", # 向量类型:"f32" | "f16" | "u64"
sync_mode="normal" # WAL 同步模式:"full" | "normal" | "off"
)
# 推荐:使用上下文管理器(退出时自动 flush 落盘)
with triviumdb.TriviumDB("my_data.tdb", dim=1536) as db:
# ... 所有操作 ...
pass # 退出时自动调用 db.flush()
```
**参数说明:**
| `path` | `str` | *必填* | `.tdb` 文件路径,不存在时自动创建 |
| `dim` | `int` | `1536` | 向量维度,必须与后续插入的向量长度一致 |
| `dtype` | `str` | `"f32"` | 向量存储精度:`f32`(标准)、`f16`(省内存)、`u64`(SimHash) |
| `sync_mode` | `str` | `"normal"` | WAL 写入安全级别,详见[持久化与压缩](#持久化与压缩) |
### Rust
```rust
use triviumdb::Database;
use triviumdb::database::{Config, StorageMode};
use triviumdb::storage::wal::SyncMode;
// 基础打开(默认 Mmap 模式 + Normal 同步)
let mut db = Database::<f32>::open("my_data.tdb", 1536)?;
// 指定同步模式(向后兼容)
let mut db = Database::<f32>::open_with_sync("my_data.tdb", 1536, SyncMode::Full)?;
// 高级配置(v0.4+)——同时指定存储模式和同步模式
let mut db = Database::<f32>::open_with_config("my_data.tdb", Config {
dim: 1536,
storage_mode: StorageMode::Rom, // Rom:单文件便携 | Mmap:分离零拷贝(默认)
sync_mode: SyncMode::Normal,
})?;
// 运行时切换同步模式
db.set_sync_mode(SyncMode::Off);
```
**泛型类型参数 `T`:**
| `f32` | 32 位浮点 | 标准 embedding(OpenAI、BGE 等) |
| `half::f16` | 16 位半精度浮点 | 大规模数据集省内存 |
| `u64` | 64 位无符号整数 | SimHash / 二值化向量 |
---
## 节点 CRUD
### insert — 插入节点
向数据库写入一个新节点,同时携带向量和 JSON 元数据。返回自动分配的 `u64` 节点 ID。
**Python:**
```python
node_id = db.insert(
vector=[0.12, -0.45, 0.78, ...], # 向量(长度必须等于 dim)
payload={"text": "小明喜欢吃苹果", "ts": 1711440000} # 任意 JSON
)
```
**Rust:**
```rust
let id = db.insert(&[0.12, -0.45, 0.78], json!({"text": "Hello"}))?;
```
### insert_with_id — 带自定义 ID 插入
适用于从外部系统导入数据时,保持原始 ID 不变。如果 ID 已存在会返回错误。
**Python:**
```python
db.insert_with_id(id=42, vector=[0.1, 0.2, 0.3, ...], payload={"source": "external"})
```
**Rust:**
```rust
db.insert_with_id(42, &[0.1, 0.2, 0.3], json!({"source": "external"}))?;
```
### batch_insert — 批量插入
一次性插入多个节点,返回所有新 ID 的列表。
**Python:**
```python
ids = db.batch_insert(
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
payloads=[{"name": "A"}, {"name": "B"}]
)
```
### batch_insert_with_ids — 带自定义 ID 批量插入
**Python:**
```python
db.batch_insert_with_ids(
ids=[100, 101],
vectors=[[0.1, 0.2, ...], [0.3, 0.4, ...]],
payloads=[{"name": "A"}, {"name": "B"}]
)
```
### get — 获取单个节点
按 ID 获取节点的完整视图,包含向量、元数据和边的数量。不存在时返回 `None`。
**Python:**
```python
node = db.get(42)
if node:
print(node.id) # 42
print(node.vector) # [0.1, 0.2, ...]
print(node.payload) # {"name": "Alice", ...}
print(node.num_edges) # 3
```
**Rust:**
```rust
if let Some(view) = db.get(42) {
println!("ID={}, edges={}", view.id, view.edges.len());
println!("payload={:?}", view.payload);
}
```
### update_payload — 更新元数据
整体替换节点的 JSON 元数据(向量和图谱关系不受影响)。
**Python:**
```python
db.update_payload(id=42, payload={"text": "更新后的文本", "version": 2})
```
### update_vector — 更新向量
就地替换节点的向量(维度必须一致,元数据和图谱关系不受影响)。
**Python:**
```python
db.update_vector(vector=[0.5, 0.6, 0.7, ...], id=42)
```
### delete — 删除节点
**三层原子联删**:同时清除该节点的向量、元数据以及所有关联的图谱边(包括其他节点指向它的入边)。
**Python:**
```python
db.delete(42)
```
**Rust:**
```rust
db.delete(42)?;
```
> ⚠️ 删除操作不可逆。删除后,该节点的向量区间被逻辑置零,待 Compaction 时物理回收。
---
## 图谱操作
### link — 建立有向边
在两个节点之间建立一条有向带权边。两个端点必须已存在,否则返回错误。
**Python:**
```python
db.link(src=1, dst=2, label="knows", weight=0.95)
```
**Rust:**
```rust
db.link(1, 2, "knows", 0.95)?;
```
**参数说明:**
| `src` | `u64` | *必填* | 源节点 ID |
| `dst` | `u64` | *必填* | 目标节点 ID |
| `label` | `str` | `"related"` | 边的类型标签(自定义字符串) |
| `weight` | `f32` | `1.0` | 边的权重(支持负值,可用于表达抑制关系) |
> 💡 边是**有向**的。如需双向关系,需调用两次 `link()`:`link(A, B)` + `link(B, A)`。
### unlink — 断开边
移除从 `src` 到 `dst` 的**所有**边(无论 label 是什么)。
**Python:**
```python
db.unlink(src=1, dst=2)
```
**Rust:**
```rust
db.unlink(1, 2)?;
```
### neighbors — N 跳邻居
从指定节点出发,沿有向边进行广度优先遍历(BFS),返回 N 跳以内所有可达节点的 ID。
**Python:**
```python
neighbor_ids = db.neighbors(id=1, depth=2) # 2 跳以内的所有邻居
```
**Rust:**
```rust
let ids = db.neighbors(1, 2);
```
---
## 搜索与召回
### search_hybrid — 双路混合认知检索 (强推)
TriviumDB 核心杀手锏:引入稀疏文本表示(BM25/AC自动机)与稠密向量(Dense Vector)构成双路融合召回锚定,再在第二阶段进行图谱激活扩散。这极大弥补了纯向量检索容易导致的专有名词幻觉(Hallucination)。
**Python:**
```python
results = db.search_hybrid(
query_vector=[0.10, -0.48, 0.80, ...],
query_text="Rust 内存安全",
top_k=5,
expand_depth=2,
min_score=0.1,
hybrid_alpha=0.7 # 0.7 偏向量,0.3 偏精确文本
)
for hit in results:
print(f"[{hit.id}] score={hit.score:.3f} | {hit.payload}")
```
### search — 纯向量图扩散检索 (基础)
TriviumDB 的基础检索能力(退化态):**先用核心稠密向量相似度找到锚点,再沿图谱关系向外扩散**。
**Python:**
```python
results = db.search(
query_vector=[0.10, -0.48, 0.80, ...], # 查询向量
top_k=5, # 向量阶段返回的锚点数量
expand_depth=2, # 图谱扩散跳数(0 = 纯向量检索)
min_score=0.5 # 最低相似度阈值
)
for hit in results:
print(f"[{hit.id}] score={hit.score:.3f} | {hit.payload}")
```
**Rust:**
```rust
let results = db.search(&[0.10, -0.48, 0.80], 5, 2, 0.5)?;
for hit in &results {
println!("[{}] score={:.3} {:?}", hit.id, hit.score, hit.payload);
}
```
**参数说明:**
| `query_vector` | `list[float]` | *必填* | 查询向量 |
| `top_k` | `int` | `5` | 向量阶段返回的最相似节点数 |
| `expand_depth` | `int` | `0` | 图谱扩散深度。设为 0 则退化为纯向量检索 |
| `min_score` | `float` | `0.5` | 余弦相似度下限,低于此值的结果被过滤 |
**返回值 `SearchHit`:**
| `id` | `u64` | 命中节点的 ID |
| `score` | `f32` | 相似度得分(余弦相似度或扩散热度) |
| `payload` | `dict` | 节点的 JSON 元数据 |
**检索流程:**
```
查询向量 ──→ [向量索引层] ──→ Top-K 锚点
│
▼
[图谱扩散层] ──→ N 跳邻居(Spreading Activation)
│
▼
最终排序结果
```
---
### search_advanced — 认知管线检索
内置九层认知管线的全功能入口。通过 `SearchConfig` 参数化控制 FISTA 残差寻隐、PPR 图扩散、DPP 多样性采样等高级特性。
**Python:**
```python
results = db.search_advanced(
query_vector=[0.10, -0.48, 0.80, ...],
top_k=10,
expand_depth=2,
min_score=0.1,
teleport_alpha=0.15, # PPR 回跳概率
enable_advanced_pipeline=True, # 总开关
enable_sparse_residual=True, # FISTA 影子查询
fista_lambda=0.1,
fista_threshold=0.3,
enable_dpp=True, # DPP 多样性采样
dpp_quality_weight=1.0,
)
for hit in results:
print(f"[{hit.id}] score={hit.score:.3f} | {hit.payload}")
```
**Node.js:**
```javascript
const results = db.searchAdvanced(queryVector, {
topK: 10,
expandDepth: 2,
teleportAlpha: 0.15,
enableAdvancedPipeline: true,
enableSparseResidual: true,
enableDpp: true,
});
```
**Rust:**
```rust
use triviumdb::database::SearchConfig;
let config = SearchConfig {
top_k: 10,
expand_depth: 2,
min_score: 0.1,
teleport_alpha: 0.15,
enable_advanced_pipeline: true,
enable_sparse_residual: true,
fista_lambda: 0.1,
fista_threshold: 0.3,
enable_dpp: true,
dpp_quality_weight: 1.0,
};
let results = db.search_advanced(&query_vec, &config)?;
```
**SearchConfig 参数说明:**
| `top_k` | `usize` | `5` | 最终返回的结果数量 |
| `expand_depth` | `usize` | `2` | 图谱扩散跳数 |
| `min_score` | `f32` | `0.1` | 余弦相似度下限 |
| `teleport_alpha` | `f32` | `0.0` | PPR 回跳概率 (0.0~1.0),越高越抑制深层扩散 |
| `enable_advanced_pipeline` | `bool` | `false` | 认知管线总开关,关闭时退化为普通检索 |
| `enable_sparse_residual` | `bool` | `false` | 启用 FISTA 残差寻隐 + 影子查询 |
| `fista_lambda` | `f32` | `0.1` | FISTA L1 正则化系数 |
| `fista_threshold` | `f32` | `0.3` | 残差范数超过此值时触发影子查询 |
| `enable_dpp` | `bool` | `false` | 启用 DPP 多样性采样 |
| `dpp_quality_weight` | `f32` | `1.0` | DPP 质量权重幂次 |
| `enable_text_hybrid_search`| `bool`| `false`| 是否开启 BM25/AC 双路混合搜索 |
| `hybrid_alpha` | `f32` | `0.7` | 混合检索中向量权重 (0~1),(1-alpha) 为稀疏文本权重 |
| `custom_query_text` | `str`| `None` | (可选) 手动传入用于文本匹配的原始文本 |
> 💡 所有参数均内置安全钳位:`teleport_alpha` 被约束在 [0, 1],`fista_lambda` 在 [1e-5, 100],`dpp_quality_weight` 在 [0, 10]。传入越界值不会崩溃,而是被静默钳平。
> 💡 当 `enable_advanced_pipeline = false` 时,`search_advanced` 的行为与 `search` 完全一致。
---
## 元数据过滤
### filter_where — 高级条件过滤
使用类 MongoDB 语法对所有节点的 Payload 进行条件过滤。返回匹配的 `NodeView` 列表。
**Python:**
```python
# 单条件
adults = db.filter_where({"age": {"$gt": 18}})
# 多条件组合
results = db.filter_where({
"$and": [
{"age": {"$lt": 30}},
{"role": {"$in": ["admin", "mod"]}}
]
})
# OR 组合
results = db.filter_where({
"$or": [
{"age": {"$lt": 18}},
{"role": "admin"}
]
})
```
**支持的操作符:**
| `$eq` | 等于 | 任意 | `{"name": {"$eq": "Alice"}}` 或直接 `{"name": "Alice"}` |
| `$ne` | 不等于 | 任意 | `{"status": {"$ne": "deleted"}}` |
| `$gt` | 大于 | 数字 | `{"age": {"$gt": 18}}` |
| `$gte` | 大于等于 | 数字 | `{"score": {"$gte": 0.8}}` |
| `$lt` | 小于 | 数字 | `{"age": {"$lt": 30}}` |
| `$lte` | 小于等于 | 数字 | `{"price": {"$lte": 99.9}}` |
| `$in` | 包含于列表 | 数组 | `{"role": {"$in": ["admin", "mod"]}}` |
| `$and` | 逻辑与 | 条件数组 | `{"$and": [{...}, {...}]}` |
| `$or` | 逻辑或 | 条件数组 | `{"$or": [{...}, {...}]}` |
**Rust:**
```rust
use triviumdb::filter::Filter;
let filter = Filter::And(vec![
Filter::Gt("age".into(), 18.0),
Filter::In("role".into(), vec![json!("admin"), json!("mod")]),
]);
let results = db.filter_where(&filter);
```
---
## Cypher 图谱查询
### query — 类 Cypher 语法查询
使用类似 Neo4j Cypher 的语法,沿图谱路径模式进行匹配查询。
**Python:**
```python
# 沿 knows 边查找
rows = db.query("MATCH (a)-[:knows]->(b) RETURN b")
for row in rows:
node = row.row["b"] # {"id": ..., "payload": {...}, "num_edges": ...}
print(node["payload"])
# 带内联属性过滤
rows = db.query("MATCH (a {id: 1})-[]->(b) RETURN a, b")
# 带 WHERE 条件
rows = db.query('MATCH (a)-[:knows]->(b) WHERE b.age > 18 RETURN b')
# AND / OR 复合条件
rows = db.query('MATCH (a)-[]->(b) WHERE b.age > 18 AND b.role == "admin" RETURN b')
```
**Rust:**
```rust
let rows = db.query("MATCH (a)-[:knows]->(b) WHERE b.age > 20 RETURN b")?;
for row in &rows {
if let Some(node) = row.get("b") {
println!("{}: {:?}", node.id, node.payload);
}
}
```
**语法规范:**
```
Query := MATCH Pattern (WHERE Condition)? RETURN ReturnList
Pattern := NodePat (EdgePat NodePat)*
NodePat := '(' Ident? ('{' PropList '}')? ')'
EdgePat := '-[' (':' Ident)? ']->'
```
**返回值 `QueryRow`:**
| `row` | `dict[str, dict]` | 变量名 → `{"id": int, "payload": dict, "num_edges": int}` |
> 💡 当起始节点已知 ID 时,强烈建议将 `id` 写入节点属性过滤器。远主键 `id` 走 **O(1) 哈希短路扫描**,而 `type` 等非主键字段会触发 O(N) 全表扫描。
> 💡 当前仅支持**有向**边模式 `-[]->` ,不支持无向匹配或反向匹配。
---
## 持久化与压缩
### flush — 手动落盘
将当前内存中的全部数据写入 `.tdb` 文件。安全写入流程:先写临时文件 → fsync → 原子 rename → 清除 WAL。
**Python:**
```python
db.flush()
```
### WAL 同步模式
通过构造函数参数或运行时方法切换 WAL(Write-Ahead Log)的同步策略:
| `"full"` | ★★★ | 最慢 | 每条写入后 fsync,断电零丢失 |
| `"normal"` | ★★☆ | 均衡 | flush 到 OS 缓冲区,操作系统崩溃可能丢少量数据(**默认**) |
| `"off"` | ★☆☆ | 最快 | 不主动 flush,仅适合测试/批量导入 |
**运行时切换:**
```python
db.set_sync_mode("full") # 切到最安全模式
db.set_sync_mode("off") # 批量导入时临时提速
```
### enable_auto_compaction — 后台自动压缩
启动后台守护线程,定时在后台串行化执行数据压缩与全量落盘(包含 `flush` + WAL 截断清理)。
**Python:**
```python
db.enable_auto_compaction(interval_secs=30) # 每 30 秒后台自动落盘
db.disable_auto_compaction() # 停止后台压缩线程
```
**Rust:**
```rust
db.enable_auto_compaction(Duration::from_secs(30));
db.disable_auto_compaction();
```
### compact — 手动强制压实 (Manual Compaction)
主动触发一次全量数据重写与压实。**此调用会阻塞当前线程**,直到所有的内存数据被安全落盘,并彻底截断清理旧的 WAL 文件。
为了极致的崩溃安全性,执行压实时会短暂阻塞前台读写。强烈建议在关闭了自动压缩后,于业务低峰期(如凌晨调度)执行此方法。
**Python:**
```python
db.compact()
```
**Rust:**
```rust
db.compact()?;
```
---
## 内存管理
### set_memory_limit — 内存预算控制
设置 MemTable 内存使用上限。当估算内存超过限额时,写操作完成后自动触发 flush。
**Python:**
```python
db.set_memory_limit(mb=256) # 限制为 256 MB
db.set_memory_limit(mb=0) # 取消限制(默认)
```
### estimated_memory — 查询当前内存占用
**Python:**
```python
usage_bytes = db.estimated_memory()
print(f"当前内存占用: {usage_bytes / 1024 / 1024:.1f} MB")
```
---
## 文本索引与稀疏检索
### index_text — 建立全文稀疏索引
对指定节点的长文本内容提取 BM25 特征,用于后续的混合检索召回。需在节点 insert 后调用。
**Python:**
```python
db.index_text(id=42, text="Rust 在嵌入式领域取得突破")
```
### index_keyword — 建立精确关键词索引
建立基于 AC 自动机 (Aho-Corasick) 的精确词汇匹配索引,极速锁定特征锚点。
**Python:**
```python
db.index_keyword(id=42, keyword="Rust")
```
### build_text_index — 编译倒排字典树
在数据初始化批量调用完毕后,**必须调用此方法**完成底层 AC 自动机的编译与全局文本 IDF 频率汇算。之后方可进行 `search_hybrid` 混合检索。
**Python:**
```python
db.build_text_index()
```
---
## 工具方法
### all_node_ids — 获取全部节点 ID
返回当前数据库中所有活跃节点的 ID 列表(顺序不定)。可用于遍历全库或批量操作。
**Python:**
```python
ids = db.all_node_ids() # 返回 list[int]
print(f"共 {len(ids)} 个节点")
```
**Rust:**
```rust
let ids = db.all_node_ids(); // Vec<NodeId>
```
### ERPC 自动索引说明
TriviumDB v0.4.6 起采用**全自动双引擎向量索引路由**,不再提供手动 `rebuild_index()` 接口:
| < 2 万节点 或 Mmap 未就绪 | **BruteForce** | 100% 精确召回,零误差 |
| ≥ 2 万节点 + Mmap 模式 + 索引就绪 | **ERPC** | 三阶段近似搜索,Recall@10 > 85% |
ERPC 索引在**后台 Compaction 线程**中自动构建,无需也无法手动触发。索引元数据持久化在 `.tdb` 文件的 ERPC Metadata Block 中,重启后零延迟恢复。
> 💡 如果你的业务对 100% 召回率有强需求(如金融/医疗),可以通过 `StorageMode::Rom` 模式强制使用 BruteForce(ERPC 仅在 `Mmap` 模式下激活)。
---
## 维度迁移
当需要更换 Embedding 模型(维度发生变化)时,使用 `migrate` 将旧库的结构迁移到新维度。
### migrate — 迁移到新维度
将当前数据库的所有节点 Payload、图谱边复制到一个全新的数据库文件中,向量以零向量占位(因为维度变了,旧向量无法直接复用)。
**参数:**
| `new_path` | `str` | 新数据库文件路径 |
| `new_dim` | `int` | 新的向量维度 |
**返回值:** 所有已迁移节点的 ID 列表(`list[int]`)
**Python:**
```python
# 第一步:迁移结构(保留 payload + 边,向量置零)
with triviumdb.TriviumDB("old.tdb", dim=768) as old_db:
node_ids = old_db.migrate("new.tdb", new_dim=1536)
# 第二步:打开新库,用新模型逐节点更新向量
with triviumdb.TriviumDB("new.tdb", dim=1536) as new_db:
for nid in node_ids:
payload = new_db.get(nid).payload
new_vec = new_model.encode(payload["text"]).tolist()
new_db.update_vector(new_vec, nid)
```
**Rust:**
```rust
// 迁移结构
let (mut new_db, node_ids) = old_db.migrate_to("new.tdb", 1536)?;
// 更新向量
for &nid in &node_ids {
let new_vec = new_model.encode(&payload_map[&nid]);
new_db.update_vector(nid, &new_vec)?;
}
new_db.flush()?;
```
> ⚠️ 迁移不修改原数据库,原库仍可正常使用。新库创建完毕后,需要手动更新所有向量后才能进行有效的向量检索。
> 💡 如果希望同时切换 dtype(例如从 f32 换 f16),需在创建新库时指定 `dtype` 参数:`TriviumDB("new.tdb", dim=1536, dtype="f16")`。
## 事务支持 (Rust Only)
TriviumDB 提供轻量级事务,采用**验证前置(Dry-Run)架构**:所有操作先缓冲在内存中,`commit()` 分两阶段执行——首先在纯内存验证全部约束(维度、节点存在性、ID 冲突),全部通过后才一次性写入。
**特性:**
- `commit()` 返回 `Err` 时,**底层数据没有被修改一个字节**,可加入日志后安全重试
- 在同一事务内,`insert_with_id(999)` 后立即 `link(..., 999)` 是完全合法的(虚拟状态叠加给 999 号打过标记)
- `rollback()`(或直接 `drop` 事务对象)将丢弃所有缓冲操作
```rust
let mut tx = db.begin_tx();
tx.insert(&vec1, json!({"type": "event"}));
tx.insert_with_id(9999, &vec2, json!({"type": "person"}));
tx.link(1, 9999, "attended", 1.0);
// 原子提交 → 两阶段: 干跑验证 → 物理写入
let ids = tx.commit()?;
// 或显式回滚(丢弃所有操作)
// tx.rollback();
```
> ⚠️ 事务目前仅在 Rust API 中可用,Python 侧暂未暴露。
---
## Pythonic 魔术方法
| `len(db)` | `db.node_count()` | 当前活跃节点数 |
| `42 in db` | `db.contains(42)` | 节点是否存在 |
| `print(db)` | `db.__repr__()` | 输出如 `TriviumDB(dtype=f32, nodes=100, dim=1536)` |
| `with db:` | `__enter__` / `__exit__` | 退出时自动 `flush()` |
---
## 数据类型说明
### NodeView
节点的完整视图,通过 `get()` 或 `filter_where()` 返回。
| `id` | `id` | `u64` | 全局唯一节点 ID |
| `vector` | `vector` | `list[float]` / `Vec<T>` | 节点的特征向量 |
| `payload` | `payload` | `dict` / `serde_json::Value` | JSON 元数据 |
| `num_edges` | `edges.len()` | `int` / `usize` | 出边数量 |
### SearchHit
向量检索命中结果,通过 `search()` 返回。
| `id` | `u64` | 命中节点 ID |
| `score` | `f32` | 相似度得分 |
| `payload` | `dict` | 节点元数据 |
### QueryRow
Cypher 查询结果行,通过 `query()` 返回。
| `row` | `dict[str, dict]` | 变量名 → 节点摘要字典 |
### Edge (Rust)
图谱边的内部结构。
| `target_id` | `NodeId (u64)` | 目标节点 ID |
| `label` | `String` | 关系类型标签 |
| `weight` | `f32` | 权重(支持负值) |