TriviumDB API 完整参考

September 1, 2026 · View on GitHub

版本: v0.8.4 语言: Rust 核心 + Python 绑定 (PyO3) + Node.js 绑定 (napi-rs)
许可: Apache-2.0


目录


数据库生命周期

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"
    load_text_index=False,    # 打开时是否加载持久化全文索引
    auto_build_quiver=True,   # 是否允许查询自动构建 QuIVer;flush 不会触发 ANN 构建
    expected_nodes=3_600_000, # 预计总节点数,仅本次进程预留,不是硬上限
    memory_limit_mb=28_672,   # 内核内存预算,0 表示不限制
)

# 推荐:使用上下文管理器(退出时自动 flush 落盘)
with triviumdb.TriviumDB("my_data.tdb", dim=1536) as db:
    # ... 所有操作 ...
    pass  # 退出时自动调用 db.flush()

参数说明:

参数类型默认值说明
pathstr必填.tdb 文件路径,不存在时自动创建
dimint1536向量维度,必须与后续插入的向量长度一致;强烈建议不超过 3072
dtypestr"f32"向量存储精度:f32(标准)、f16(省内存)、u64(SimHash)
sync_modestr"normal"WAL 写入安全级别,详见持久化与压缩
load_text_indexboolFalse是否在打开时加载持久化全文索引
auto_build_quiverboolTrue是否允许查询准备阶段自动构建 QuIVer;纯 flush() 不构建 ANN
expected_nodesint | NoneNone预计总节点数;仅预留核心容器、不持久化、不是硬上限
memory_limit_mbint0TriviumDB 内核内存预算(MiB);0 表示不限制
access_modestr"read_write"read_write 使用排他锁;read_only 使用共享锁;immutable 验证 manifest 后无锁打开

search_with_context / searchWithContext 返回的上下文除阶段耗时和候选数外,还包含 observationsestimated_heap_bytesmmap_vector_bytesnode_count、查询路由与 QuIVer ef_search;Linux 额外提供进程 RSS、累计主缺页和次缺页。缺页计数是进程累计值,调用方应计算相邻采样差值。

⚠️ 强烈建议 dim <= 3072 数据库存储和精确 BruteForce 检索支持 3073–65536 维,但 QuIVer 仅支持 1–3072 维。高于 3072 维时,自动 QuIVer 构建会被安全跳过,搜索回退到 BruteForce;显式构建 QuIVer 会返回错误,不会 panic。若数据规模可能达到 1 万节点以上并依赖 ANN 性能,请在建库前选择不超过 3072 维的 embedding 模型或先做降维。

只读访问模式

只读模式面向已完成 flush() 的不可变数据库代际。多个进程可以同时以共享锁打开同一路径,但同路径 Reader 与 Writer 互斥。

TriviumDB 采用单写多读模型。同一语言绑定实例可被多个线程调用且不会再抛出底层借用冲突,但写 API 仍应由调用方通过单一写队列、Lock / Mutex 或 actor 串行化;这不是并行多 Writer 提交能力。高并发读取应使用 ReadOnly/Immutable Reader。

db = TriviumDB("generation-42/data.tdb", dim=768, access_mode="read_only")
const db = new TriviumDB("generation-42/data.tdb", {
  dim: 768,
  accessMode: "readOnly",
})

Rust 可使用 Database::<f32>::open_read_only(path, dim),或在 Config 中设置 AccessMode::ReadOnly

只读句柄具有以下安全语义:

  • 不创建数据库、WAL、sidecar 或一致性标记;
  • 不截断、回放或清理 WAL;检测到待恢复 WAL 时返回 RecoveryRequired
  • 不删除损坏或错配的 TextIndex/QuIVer sidecar;
  • .tdb/.vec/.flush_ok 代际不完整时拒绝打开,不执行 metadata-only 降级;
  • 禁止 CRUD、事务提交、TQL mutation、索引修改、QuIVer 构建、flush 和 compact;
  • close() 和 Drop 不执行持久化;
  • QuIVer 不可用时保持内存只读并走安全检索路径,不写回磁盘。

发布只读代际前必须由 Writer 完成 flush()close(),并保留相邻 .lock 文件供 Reader 获取共享锁。不要在仍有进程持锁时删除 .lock 文件。

不可变 Generation

Writer 可在完成持久化后原子生成 generation manifest:

db.publish_generation_manifest("generation-42")?;

Python 使用 db.publish_generation_manifest("generation-42"),Node.js 使用 db.publishGenerationManifest("generation-42")。该操作会先执行安全 flush(),随后为当前 .tdb.vec.flush_ok 及已存在的 TextIndex/QuIVer sidecar 记录文件大小和 CRC32,并最后原子发布 <path>.manifest.json

发布完成后可在不需要 .lock.wal 的部署目录中使用:

let db = Database::<f32>::open_immutable(path, 768)?;
db = TriviumDB(path, dim=768, access_mode="immutable")
const db = new TriviumDB(path, { dim: 768, accessMode: "immutable" })

Immutable 会在加载前验证 manifest 版本、完成状态、dtype、维度、文件存在性、大小和 CRC32,不创建锁文件或 WAL,也不修复任何文件。句柄存活期间禁止原地修改、覆盖或删除 generation 中的文件;正确发布方式是构建新目录或新路径,然后由应用原子切换 current 指针。

Config.missing_index_policy 控制 Reader 遇到 QuIVer/TextIndex 缺失、错配或损坏时的行为:

策略行为
Fallback默认;忽略不可用 sidecar,QuIVer 回退安全检索路径
BuildInMemory允许当前进程在内存中惰性构建 QuIVer,但绝不写回 sidecar
Error严格模式;立即返回 ImmutableArtifactInvalid,不修改任何文件

Python 使用 missing_index_policy="fallback|build_in_memory|error";Node.js 使用 missingIndexPolicy: "fallback" | "buildInMemory" | "error"。ReadOnly/Immutable Reader 禁止启用 enable_refractory_fatigue,避免不同 Worker 因进程本地疲劳状态产生不一致结果。

Rust 类型化门面

Rust 应用可以使用类型化门面,让只读代码在编译期无法调用写 API:

use triviumdb::{DatabaseReader, DatabaseWriter};

let mut writer = DatabaseWriter::<f32>::open("data.tdb", 768)?;
writer.insert(&vector, payload)?;
writer.publish_generation_manifest("generation-42")?;

let reader = DatabaseReader::<f32>::open_read_only("data.tdb", 768)?;
let hits = reader.search(&query, 10, 0, 0.0)?;

let immutable = DatabaseReader::<f32>::open_immutable("data.tdb", 768)?;
let hits = immutable.search_exact(&query, 10)?;

DatabaseReader<T> 只暴露查询、节点读取、图遍历、TQL 只读查询、统计和关闭能力,不实现 Deref<Target = Database<T>>,因此无法绕过门面访问 insert、事务、索引修改、flush 或 compact。DatabaseWriter<T> 保留完整 Database<T> 能力,并通过 Deref/DerefMut 保持使用体验。

原有 Database<T> API 继续保留,用于动态语言绑定和需要运行时切换访问模式的兼容场景。

Generation Store

服务端或多 Worker 应使用 GenerationStore 管理不可变代际,不要原地覆盖 Reader 正在 mmap 的文件:

use triviumdb::{DatabaseWriter, GenerationStore};

let store = GenerationStore::new("./generations");
let path = store.prepare_generation("generation-42", "data.tdb")?;

let mut writer = DatabaseWriter::<f32>::open(path.to_str().unwrap(), 768)?;
writer.insert(&vector, payload)?;
writer.publish_generation_manifest("generation-42")?;
drop(writer);

store.publish_current("generation-42", "data.tdb")?;

let reader = store.open_current::<f32>(768)?;
assert_eq!(reader.generation_id(), "generation-42");

目录结构:

generations/
├── current.json
├── current.json
├── generation-41/
│   └── data.tdb...
└── generation-42/
    └── data.tdb...

publish_current() 使用临时文件、文件 fsync、原子 rename 和父目录同步,并在切换前完整验证 manifest、文件 checksum、generation ID 与实际 node count。open_current() 在解析 current 与获取 generation 共享租约期间持有管理共享锁,避免与切代或回收竞态。GenerationReader 在整个生命周期持有外部 runtime 共享租约;reclaim_generation() 只有取得排他租约后才删除旧代际,并且在 current 损坏时 fail closed。

租约不会写入不可变 generation。默认 runtime 目录位于系统临时目录的 triviumdb-runtime/store-<hash>,也可以使用 GenerationStore::with_runtime_dir(root, runtime_dir) 指定可写位置。generation root 因此可以保持只读。

generation ID 和数据库文件名必须是单一安全路径组件,..、绝对路径和路径分隔符都会被拒绝。旧 Reader 在切代后继续读取原 generation,新 Reader 读取新的 current。调用方应在 Writer 关闭并移除非制品 .wal/.lock 后再分发 generation。

CRC32 用于检测随机损坏,不提供对抗性防篡改或来源认证。不可信分发应由外层对 manifest 使用 SHA-256/BLAKE3 并进行数字签名。

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)?;

// 指定同步与存储模式统一使用 Config;open_with_sync 已移除
// 高级配置——同时指定存储模式和同步模式
let mut db = Database::<f32>::open_with_config("my_data.tdb", Config {
    dim: 1536,
    storage_mode: StorageMode::Rom,  // Rom:单文件便携 | Mmap:分离零拷贝(默认)
    sync_mode: SyncMode::Normal,
    expected_nodes: Some(3_600_000),
    memory_limit: 28 * 1024 * 1024 * 1024,
    ..Default::default()
})?;

// 运行时切换同步模式
db.set_sync_mode(SyncMode::Off);

close() 会先进入 Closing,拒绝新操作并等待已经进入的操作结束,再执行最终 flush。只有 flush 成功后才释放文件锁并进入 Closed;flush 失败会恢复为 Open 且继续持有文件锁。关闭后的旧对象不能再次用于查询、写入或 flush。

泛型类型参数 T

类型说明适用场景
f3232 位浮点标准 embedding(OpenAI、BGE 等)
half::f1616 位半精度浮点大规模数据集省内存
u6464 位无符号整数SimHash / 二值化向量

节点 CRUD

insert — 插入节点

向数据库写入一个新节点,同时携带向量和 JSON 元数据。返回自动分配的 u64 节点 ID。

Python:

node_id = db.insert(
    vector=[0.12, -0.45, 0.78, ...],       # 向量(长度必须等于 dim)
    payload={"text": "小明喜欢吃苹果", "ts": 1711440000}  # 任意 JSON
)

Rust:

let id = db.insert(&[0.12, -0.45, 0.78], json!({"text": "Hello"}))?;

insert_with_id — 带自定义 ID 插入

适用于从外部系统导入数据时,保持原始 ID 不变。如果 ID 已存在会返回错误。

Python:

db.insert_with_id(id=42, vector=[0.1, 0.2, 0.3, ...], payload={"source": "external"})

Rust:

db.insert_with_id(42, &[0.1, 0.2, 0.3], json!({"source": "external"}))?;

reserve_nodes — 主动预留增量容量

expected_nodes 表示目标总节点数;reserve_nodes(additional) 表示从当前容量再增加可插入空间。两者只覆盖向量增量层、Payload/ID 映射、槽位与快速标签,不会提前构建 BQ、QuIVer、文本索引或图边。

db.reserve_nodes(200_000)
db.reserve_nodes(200_000)?;

预留受 memory_limit 约束。预算不足、整数溢出或 allocator 拒绝时会在写 WAL 前失败;节点、ID、generation 和 WAL 不变。已成功取得但尚未使用的空 capacity 允许保留。

Node.js 配置对象

Node.js 构造器只接受配置对象。旧数字位置参数已移除,并返回 TDB_API_MIGRATION_REQUIRED

const db = new TriviumDB('my_data.tdb', {
  dim: 1024,
  dtype: 'f16',
  storageMode: 'mmap',
  expectedNodes: 3_600_000,
  memoryLimitMb: 28 * 1024,
  autoBuildQuiver: false,
})
db.reserveNodes(200_000)

所有 Node 数量参数必须是 JavaScript 安全整数,负数、小数、NaN、Infinity 和超过 Number.MAX_SAFE_INTEGER 的值都会拒绝。

batch_insert — 批量插入

一次性插入多个节点,返回所有新 ID 的列表。Python/Node 绑定会先转换并验证整个批次,再为整批预留核心容器,最后通过单个事务写入;任一向量、ID、容量预算或 WAL 步骤失败都不会产生半批数据。

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:

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:

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:

if let Some(view) = db.get(42) {
    println!("ID={}, edges={}", view.id, view.edges.len());
    println!("payload={:?}", view.payload);
}

update_payload — 更新元数据

整体替换节点的 JSON 元数据(向量和图谱关系不受影响)。

Python:

db.update_payload(id=42, payload={"text": "更新后的文本", "version": 2})

update_vector — 更新向量

就地替换节点的向量(维度必须一致,元数据和图谱关系不受影响)。

Python:

db.update_vector(vector=[0.5, 0.6, 0.7, ...], id=42)

delete — 删除节点

三层原子联删:同时清除该节点的向量、元数据以及所有关联的图谱边(包括其他节点指向它的入边)。

Python:

db.delete(42)

Rust:

db.delete(42)?;

⚠️ 删除操作不可逆。删除后,该节点的向量区间被逻辑置零,待 Compaction 时物理回收。

get_payload — 轻量级获取元数据

只获取节点的 JSON Payload,不含向量,比 get() 更轻量。

Python:

payload = db.get_payload(42)
if payload:
    print(payload["name"])  # "Alice"

Node.js:

const payload = db.getPayload(42)
if (payload) console.log(payload.name)

get_edges — 获取出边列表

获取节点的所有出向边(不含向量和 Payload)。

Python:

edges = db.get_edges(42)
for e in edges:
    print(f"{e.target_id} ({e.label}, w={e.weight})")

Node.js:

const edges = db.getEdges(42)
edges.forEach(e => console.log(`${e.targetId} (${e.label})`))

contains — 节点存在检查

Python:

if db.contains(42):     # 或用 42 in db
    print("节点存在")

Node.js:

if (db.contains(42)) console.log('节点存在')

图谱操作

在两个节点之间建立一条有向带权边。边以 (src, dst, label) 三元组唯一;重复调用会更新 weight,不会增加重复边。两个端点必须已存在,weight 必须是有限 f32,否则在写 WAL 前返回错误。

Python:

db.link(src=1, dst=2, label="knows", weight=0.95)

Rust:

db.link(1, 2, "knows", 0.95)?;

参数说明:

参数类型默认值说明
srcu64必填源节点 ID
dstu64必填目标节点 ID
labelstr"related"边的类型标签(自定义字符串)
weightf321.0边的权重(支持负值,可用于表达抑制关系)

💡 边是有向的。如需双向关系,需调用两次 link()link(A, B) + link(B, A)

精确边读取与 Upsert

边的唯一键是 (src, dst, label)get_edge/getEdge 精确读取单边;upsert_edge/upsertEdge 创建或覆盖 weight 与任意 JSON metadataupdate_edge/updateEdge 只更新已存在边的指定字段。边元数据会贯穿 WAL、v7 .tdb 文件、迁移和各语言绑定。

let metadata = serde_json::json!({"confidence": 0.98});
db.upsert_edge(1, 2, "knows", 0.95, metadata)?;
let edge = db.get_edge(1, 2, "knows");
db.update_edge(1, 2, "knows", Some(0.9), None)?;
db.upsert_edge(1, 2, "knows", 0.95, {"confidence": 0.98})
edge = db.get_edge(1, 2, "knows")
db.update_edge(1, 2, "knows", weight=0.9)
db.upsertEdge(1, 2, 'knows', 0.95, { confidence: 0.98 })
const edge = db.getEdge(1, 2, 'knows')
db.updateEdge(1, 2, 'knows', 0.9)

移除从 srcdst所有边(无论 label 是什么)。

Python:

db.unlink(src=1, dst=2)
db.unlink(src=1, dst=2, label="knows")  # 仅删除指定标签

Rust:

db.unlink(1, 2)?;
db.unlink_label(1, 2, "knows")?;

neighbors — N 跳邻居

从指定节点出发,沿有向边进行广度优先遍历(BFS),返回 N 跳以内所有可达节点的 ID。

Python:

neighbor_ids = db.neighbors(id=1, depth=2)  # 2 跳以内的所有邻居
knows_only = db.neighbors(id=1, depth=2, labels=["knows"])

Rust:

let ids = db.neighbors(1, 2);
let knows = db.neighbors_with_labels(1, 2, Some(&["knows".to_string()]));

返回顺序固定为 BFS 最短距离升序、同距离 NodeId 升序。labels=None 表示遍历全部边,空列表表示禁止扩散。

get_incoming_edges — 获取完整入边

返回 source_idtarget_idlabelweight,可按单个标签过滤。

incoming = db.get_incoming_edges(id=2, label="knows")
let incoming = db.get_incoming_edges(2, Some("knows"));

reachable — 确定性结构可达性

reachable 执行纯结构 BFS,不计算相关性 score。默认沿出边遍历,也可选择入边或双向遍历;每个目标只返回一条确定性的最短路径,结果包含完整 NodeId 路径和逐跳 label。

Python:

paths = db.reachable(
    id=1,
    min_depth=1,
    max_depth=3,
    labels=["knows", "works_with"],
    direction="both",
    max_visited_nodes=10_000,
)
for item in paths:
    print(item.target_id, item.depth, item.path)
    print([(step.from_id, step.to_id, step.label) for step in item.steps])

Rust:

use triviumdb::graph::reachability::{ReachabilityConfig, ReachabilityDirection};

let paths = db.reachable(1, &ReachabilityConfig {
    min_depth: 1,
    max_depth: 3,
    labels: Some(vec!["knows".into(), "works_with".into()]),
    direction: ReachabilityDirection::Both,
    max_visited_nodes: 10_000,
})?;

Node.js / TypeScript:

const paths = db.reachable(1, {
  minDepth: 1,
  maxDepth: 3,
  labels: ['knows', 'works_with'],
  direction: 'both',
  maxVisitedNodes: 10_000,
})

labels=None 或省略表示全部 label,空列表表示禁止遍历。min_depth=0 可让源节点作为深度 0 结果返回。三个预算分别限制访问节点、结果和扫描边;详细接口在超限时保留已完成结果并设置 truncated=true

  • Rust:reachable_detailed()query_subgraph()
  • Python:reachable_detailed()query_subgraph()
  • Node.js:reachableDetailed()querySubgraph()

详细可达性同时返回 visited_nodes/visitedNodestraversed_edges/traversedEdgestruncated。逐跳结果包含遍历方向的 from/to、原始边权重和元数据。子图按 NodeId 和原始 (source, target, label) 确定性排序,并保持入边的原始方向。

图统计、校验与修复

graph_stats/graphStats 返回节点数、边数、孤立节点数和标签数。validate_graph/validateGraph 检查悬空边、重复三元组以及入边、入度、标签派生索引。Writer 可调用 repair_graph_indexes/repairGraphIndexes 清理无效边、从权威出边表重建派生索引并立即持久化。

自定义 ID Upsert 与 Node.js 原子事务

upsert_with_id/upsertWithId 在 ID 不存在时插入,存在时原子覆盖向量和 Payload。Node.js 的 commitTransaction(operations) 可在同一个 WAL-first 事务中混合 insertinsertWithIddeleteupdatePayloadupdateVectorlink/upsertEdgeunlinkunlinkLabel;任何预检失败都不会应用部分操作。


搜索与召回

search_graph_first — 图候选集内精确向量排名

GraphFirst 先由业务图查询产生 anchor ID 集合,再只在该集合内进行精确向量评分。输入 anchor 会按 NodeId 去重;超过 max_anchor_nodes 时直接报错,不会退化为全库搜索。

hits = db.search_graph_first(
    query_vector=[0.1, 0.2, 0.3],
    anchor_ids=[12, 18, 25],
    top_k=5,
    max_anchor_nodes=100_000,
)
let hits = db.search_graph_first(
    &[0.1, 0.2, 0.3],
    &[12, 18, 25],
    5,
    100_000,
)?;
const hits = db.searchGraphFirst(
  [0.1, 0.2, 0.3],
  [12, 18, 25],
  5,
  100_000,
)

该 API 保证 anchor 集合内 Top-K 完备,但不会使用 QuIVer ANN。查询维度不匹配、top_k=0、预算为 0 或去重后的 anchor 数量超预算时返回错误;不存在的 anchor ID 会被忽略。

search_hybrid — 双路混合认知检索 (强推)

TriviumDB 核心杀手锏:引入稀疏文本表示(BM25/AC自动机)与稠密向量(Dense Vector)构成双路融合召回锚定,再在第二阶段进行图谱激活扩散。这极大弥补了纯向量检索容易导致的专有名词幻觉(Hallucination)。

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}")

TriviumDB 的基础检索能力(退化态):先用核心稠密向量相似度找到锚点,再沿图谱关系向外扩散

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:

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_vectorlist[float]必填查询向量
top_kint5向量阶段返回的最相似节点数
expand_depthint0图谱扩散深度。设为 0 则退化为纯向量检索
min_scorefloat0.5余弦相似度下限,低于此值的结果被过滤

返回值 SearchHit

属性类型说明
idu64命中节点的 ID
scoref32相似度得分(余弦相似度或扩散热度)
payloaddict节点的 JSON 元数据

search_batch — 批量并行 ANN 检索

同一个数据库实例一次接收多条向量查询,由 Rust 共享线程池并行执行。所有查询在执行前完成整批校验;任一向量维度错误或包含 NaN/Infinity 时整批失败,不返回部分结果。外层结果顺序严格对应输入顺序。

Python:

results = db.search_batch(
    query_vectors=[[0.10, -0.48, 0.80], [0.72, 0.11, -0.35]],
    top_k=10,
    recall_k=0,
    rerank_k=0,
    expand_depth=0,
    min_score=0.0,
    parallelism=0,
)

Python 在整个 Rust 查询阶段释放 GIL,避免逐条 FFI 调用造成的吞吐损失。

Node.js:

const results = await db.searchBatch(queryVectors, 10, 0, 0.0)

Node.js 接口返回 Promise,ANN 工作在后台线程执行,不阻塞事件循环。参数依次为 queryVectorstopKparallelismminScore

Rust:

let results = db.search_batch(
    &queries,
    &SearchConfig {
        top_k: 10,
        expand_depth: 0,
        min_score: 0.0,
        ..Default::default()
    },
    &BatchSearchConfig { parallelism: 0 },
)?;
约束语义
parallelism = 0自动选择并发度
parallelism = 1..64指定批内最大并发度
parallelism > 64明确拒绝,防止过度并行
空查询批次返回空列表
top_k = 0明确拒绝
fatigue明确拒绝,批量 API 仅支持无状态查询
生命周期close 阻止新批次,并等待已进入批次完成

检索流程:

查询向量 ──→ [向量索引层] ──→ Top-K 锚点


              [图谱扩散层] ──→ N 跳邻居(Spreading Activation)


                           最终排序结果

search_advanced — 认知管线检索

内置认知管线的全功能入口。通过 SearchConfig 参数化控制 FISTA 残差寻隐、SA-PPR 有限深度扩散、DPP 多样性采样等高级特性。SA-PPR 是带个性化重启的有限深度 Spreading Activation,不是迭代至收敛的标准 PageRank。

Python:

results = db.search_advanced(
    query_vector=[0.10, -0.48, 0.80, ...],
    top_k=10,
    recall_k=200,                   # 初始稠密/稀疏召回池
    rerank_k=50,                    # SA-PPR/FISTA/DPP 前候选池
    expand_depth=2,
    min_score=0.1,
    teleport_alpha=0.15,            # SA-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,
    expand_labels=["knows", "related"], # None=全部,[]=禁止图扩散
    max_edges_per_node=20,         # 每节点按绝对权重仅保留最强 20 条边
    min_edge_weight=0.3,           # 过滤绝对权重低于 0.3 的弱边
    edge_direction="both",         # out / in / both
)
for hit in results:
    print(f"[{hit.id}] score={hit.score:.3f} | {hit.payload}")

图扩散先按方向、标签和 abs(weight) 阈值过滤,再按 abs(weight) 降序、目标 ID 升序、标签字典序稳定排序并应用 max_edges_per_node。传播预算只在最终保留的边之间重新归一化。inhibition 和负权边同样按绝对值参与阈值与强边选择,但继续传播负能量。

Node.js:

const results = db.searchAdvanced(queryVector, {
    topK: 10,
    expandDepth: 2,
    teleportAlpha: 0.15,
    enableAdvancedPipeline: true,
    enableSparseResidual: true,
    enableDpp: true,
    expandLabels: ['knows', 'related'],
});

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_kusize5最终返回的结果数量
recall_kusize0(自动)初始召回池;自动值为 max(top_k × 8, 64)
rerank_kusize0(自动)高级处理候选池;自动值为 max(top_k × 4, 32),且不超过 recall_k
expand_depthusize2SA-PPR 有限深度扩散跳数
min_scoref320.1余弦相似度下限
teleport_alphaf320.0PPR 回跳概率 (0.0~1.0),越高越抑制深层扩散
enable_advanced_pipelineboolfalse认知管线总开关,关闭时退化为普通检索
enable_sparse_residualboolfalse启用 FISTA 残差寻隐 + 影子查询
fista_lambdaf320.1FISTA L1 正则化系数
fista_thresholdf320.3残差范数超过此值时触发影子查询
enable_dppboolfalse启用 DPP 多样性采样
dpp_quality_weightf321.0DPP 质量权重幂次
enable_text_hybrid_searchboolfalse是否开启 BM25/AC 双路混合搜索
text_boostf321.5加权 RRF 中的稀疏排名权重;不再与余弦分数直接相加
hybrid_alphaf320.7混合检索中向量权重 (0~1),(1-alpha) 为稀疏文本权重
custom_query_textstrNone(可选) 手动传入用于文本匹配的原始文本
force_brute_forceboolfalse强制使用暴力搜索,禁用 QuIVer 图索引(用于基准测试和需要精确结果的场景)

💡 所有参数均内置安全钳位:teleport_alpha 被约束在 [0, 1],fista_lambda 在 [1e-5, 100],dpp_quality_weight 在 [0, 10]。传入越界值不会崩溃,而是被静默钳平。

💡 当 enable_advanced_pipeline = false 时,search_advanced 的行为与 search 完全一致。


🔌 Hook 扩展系统

TriviumDB v0.6.0 新增的检索管线 Hook 系统,允许开发者在 6 个关键阶段注入自定义逻辑,高度自定义检索管线。

管线 Hook 点整体架构

  查询输入

  🔌 #1 on_pre_search        — 查询预处理(改写向量 / 修改配置 / 提前终止)

  🔌 #2 on_custom_recall     — 自定义召回(可替代内置召回)

  ┌── 内置召回管线 ──┐
  │  L1 文本稀疏召回  │
  │  L2 向量稠密召回  │
  │  L3 布隆预过滤    │
  └──────────────────┘

  🔌 #3 on_post_recall       — 召回后处理(业务过滤 / 分数调权)

  🔌 #4 on_pre_graph_expand  — 图扩散前拦截

  ┌── 图谱扩散 ──────┐
  │  L6 SA-PPR 扩散   │
  │  L7 不应期/抑制    │
  └──────────────────┘

  🔌 #5 on_rerank            — 自定义重排序

  🔌 #6 on_post_search       — 最终后处理

  返回结果

load_ffi_hook — 加载 C/C++ 动态库插件

加载一个导出了 C ABI 符号的动态库(.so / .dll / .dylib)作为检索管线 Hook。动态库中的所有符号均为可选,未找到的符号将自动被无操作替代。

Python:

db.load_ffi_hook("./libmy_plugin.so")
results = db.search(query_vec)  # 自动经过 C++ Hook

Node.js:

db.loadFfiHook('./libmy_plugin.so')
const results = db.search(queryVec)  // 自动经过 C++ Hook

Rust:

use triviumdb::hook::FfiHook;

let ffi_hook = FfiHook::load("./libmy_plugin.so")?;
db.set_hook(ffi_hook);

clear_hook — 清除已注册 Hook

清除当前的 Hook,恢复为默认的零开销 NoopHook

Python:

db.clear_hook()

Node.js:

db.clearHook()

Rust:

db.clear_hook();

search_with_context — 带管线上下文的检索

search 相同的检索能力,但额外返回 HookContext 对象,包含管线各阶段的计时统计和 Hook 注入的自定义数据。

Python:

hits, ctx = db.search_with_context(
    query_vector=[0.10, -0.48, 0.80, ...],
    top_k=10,
    expand_depth=2,
    min_score=0.1,
)

print(ctx.timings)
# {'hook_pre_search': 0.012, 'hook_custom_recall': 0.001, 'graph_expand': 2.34, ...}

print(ctx.custom_data)   # Hook 注入的自定义数据
print(ctx.aborted)       # 管线是否被 Hook 提前终止

Node.js:

const { hits, context } = db.searchWithContext(queryVec, {
    topK: 10,
    expandDepth: 2,
    minScore: 0.1,
})

console.log(context.timings)     // { hook_pre_search: 0.012, graph_expand: 2.34, ... }
console.log(context.customData)  // Hook 注入的自定义数据
console.log(context.aborted)     // 管线是否被提前终止

Rust:

use triviumdb::database::SearchConfig;

let config = SearchConfig {
    top_k: 10,
    expand_depth: 2,
    ..Default::default()
};
let (results, ctx) = db.search_hybrid_with_context(None, Some(&query_vec), &config)?;

for (stage, dur) in &ctx.stage_timings {
    println!("{}: {:.2}ms", stage, dur.as_secs_f64() * 1000.0);
}

Rust 原生 Hook Trait

在 Rust 中,开发者可以直接实现 SearchHook trait 来创建自定义 Hook:

use triviumdb::hook::{SearchHook, HookContext};
use triviumdb::database::SearchConfig;
use triviumdb::node::SearchHit;

struct MyHook;

impl SearchHook for MyHook {
    fn on_pre_search(
        &self,
        query_vector: &mut Vec<f32>,
        config: &mut SearchConfig,
        ctx: &mut HookContext,
    ) {
        // 修改查询向量、调整配置等
        ctx.custom_data = serde_json::json!({"user_id": "u_12345"});
    }

    fn on_rerank(
        &self,
        results: &mut Vec<SearchHit>,
        _ctx: &mut HookContext,
    ) -> Option<Vec<SearchHit>> {
        // 自定义重排序逻辑
        results.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap());
        None // 返回 None 表示使用原地修改,返回 Some 替换结果
    }
}

// 注册 Hook
db.set_hook(MyHook);

💡 零开销设计:未注册 Hook 时,默认的 NoopHook 的所有方法均为空实现,编译器会将它们完全内联消除,对无 Hook 的普通检索完全零开销。

⚠️ FFI 插件安全提示FfiHook 加载的动态库将在进程内执行任意代码,请确保动态库来源可信。


元数据过滤

filter_where — 高级条件过滤

使用类 MongoDB 语法对所有节点的 Payload 进行条件过滤。返回匹配的 NodeView 列表。

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大于等于数字或字符串{"name": {"$gte": "Bob"}}
$lt小于数字或字符串{"age": {"$lt": 30}}
$lte小于等于数字或字符串{"name": {"$lte": "David"}}
$before早于RFC3339 字符串{"createdAt": {"$before": "2026-08-22T00:00:00Z"}}
$beforeEq早于或等于RFC3339 字符串{"createdAt": {"$beforeEq": "2026-08-22T00:00:00Z"}}
$after晚于RFC3339 字符串{"createdAt": {"$after": "2026-08-21T00:00:00Z"}}
$afterEq晚于或等于RFC3339 字符串{"createdAt": {"$afterEq": "2026-08-21T00:00:00Z"}}
$in包含于列表数组{"role": {"$in": ["admin", "mod"]}}
$nin不在列表中数组{"status": {"$nin": ["banned", "deleted"]}}
$startsWith前缀匹配字符串{"folder": {"$startsWith": "/地理"}}
$contains包含子串字符串{"tag": {"$contains": "重要"}}
$exists字段是否存在布尔{"email": {"$exists": true}}
$size数组长度正整数{"tags": {"$size": 3}}
$all数组包含所有数组{"tags": {"$all": ["A", "B"]}}
$type字段类型字符串{"age": {"$type": "number"}}
$and逻辑与条件数组{"$and": [{...}, {...}]}
$or逻辑或条件数组{"$or": [{...}, {...}]}

数字只与数字比较,字符串只与字符串按原始 Unicode 字典序比较,类型不同视为不匹配。RFC3339 操作符会解析时区并按绝对时间比较;非法查询操作数直接报错,节点中的脏时间字段视为不匹配。范围查询当前不会使用属性 Hash 索引加速。

字符串匹配示例(v0.7.2 新增):

# 前缀匹配:匹配 /地理 及其所有子路径
results = db.filter_where({"folder": {"$startsWith": "/地理"}})

# 多前缀 OR 组合:匹配多个路径前缀
results = db.filter_where({
    "$or": [
        {"folder": {"$startsWith": "/地理"}},
        {"folder": {"$startsWith": "/天文"}}
    ]
})

# 子串包含
results = db.filter_where({"description": {"$contains": "关键词"}})

# search() 中使用 payload_filter 前缀过滤
results = db.search(
    query_vector=[0.1, ...],
    payload_filter={"folder": {"$startsWith": "/地理"}}
)

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);

TQL、Prepared 与一等值

TQL 公共 API 暴露的是一套 自由 DIY 混合查询能力,而不是单一的混合搜索函数。调用者可以用一条查询按需组合向量源、属性索引、图扩展、图算法、路径、集合代数、迭代、聚合与重排;Prepared TQL 则让同一管线安全地重复绑定业务参数。

tql / tql_values

支持 MATCH、OPTIONAL MATCH、FIND、SEARCH、WITH 管线、聚合、图算法、路径与集合代数。Rust tql() 只适合纯节点绑定;标量、聚合、Path/List 必须使用 tql_values()。Python/Node 的 tql 已自动承载一等值。

Python:

# 图遍历
rows = db.tql('MATCH (a)-[:knows]->(b) WHERE b.age > 18 RETURN b')
for row in rows:
    node = row.row["b"]    # {"id": ..., "payload": {...}, "num_edges": ...}
    print(node["payload"])

# 文档过滤
rows = db.tql('FIND {type: "event", heat: {$gte: 0.7}} RETURN *')

# 带内联属性 + WHERE
rows = db.tql('MATCH (a {id: 1})-[]->(b) WHERE b.score >= 0.8 RETURN a, b')

Node.js:

const rows = db.tql('MATCH (a)-[:knows]->(b) WHERE b.age > 18 RETURN b')
rows.forEach(row => console.log(row.b.payload))

Rust:

let rows = db.tql("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);
    }
}

prepare_tql / execute_prepared_tql

prepared = db.prepare_tql(
    "FIND {kind: \"note\"} RETURN $bonus + 1 AS score"
)
rows = db.execute_prepared_tql(prepared, {"bonus": 4})
const prepared = db.prepareTql('FIND {kind: "note"} RETURN $bonus + 1 AS score')
const rows = db.executePreparedTql(prepared, { bonus: 4 })

Prepared 参数只接受 null/bool/string/number;缺参、额外参数、数组/对象和非有限数值明确失败。

一等值映射

TQL 值PythonNode
Nodedictobject,NodeId 为字符串
Int/Float/String/Bool原生标量原生标量
Pathlist[int]string[],避免 u64 精度损失
Listlistarray
NullNonenull

tql_mut — 执行 TQL 写操作

支持 CREATE / SET / DELETE / DETACH DELETE 语法,返回受影响行数和新创建的节点 ID。

Python:

# 创建节点
result = db.tql_mut('CREATE (a {name: "Alice", age: 30})')
print(result["affected"])      # 1
print(result["created_ids"])   # [1]

# 更新属性
db.tql_mut('MATCH (a {name: "Alice"}) SET a.age == 31')

# 删除节点
db.tql_mut('MATCH (a {name: "Alice"}) DELETE a')

# 删除节点及其所有关联边
db.tql_mut('MATCH (a {type: "temp"}) DETACH DELETE a')

Node.js:

const result = db.tqlMut('CREATE (a {name: "Alice", age: 30})')
console.log(result.affected)     // 1
console.log(result.createdIds)   // [1]

返回值:

字段类型说明
affectedint受影响的节点数
created_ids / createdIdslist[int] / number[]CREATE 新建的节点 ID 列表

语法规范:

Query      := MATCH Pattern (WHERE Condition)? RETURN ReturnList
            | MATCH Pattern (WHERE Condition)? (SET SetExpr | DELETE Ident | DETACH DELETE Ident)
            | CREATE NodePat
            | FIND JsonFilter RETURN ReturnList
Pattern    := NodePat (EdgePat NodePat)*
NodePat    := '(' Ident? ('{' PropList '}')? ')'
EdgePat    := '-[' (':' Ident)? ']->' 
Condition  := CompareExpr ((AND | OR) CompareExpr)*
ReturnList := Ident (',' Ident)* | '*'

💡 当起始节点已知 ID 时,强烈建议将 id 写入节点属性过滤器。主键 idO(1) 哈希短路扫描,而 type 等非主键字段会触发 O(N) 全表扫描(除非已建立属性索引)。

MATCH 使用显式有向模式;管线 EXPAND 和 Reachability 另行支持 OUTGOING/INCOMING/BOTH。


持久化索引管理与观测

四类属性索引均持久化到 .pidx 并由 Planner 透明选择:

类型PythonNodeRust
Hashcreate_indexcreateIndexcreate_index
Ordered ARTcreate_ordered_indexcreateOrderedIndexcreate_ordered_index
Composite ARTcreate_composite_indexcreateCompositeIndexcreate_composite_index
Roaring Bitmapcreate_bitmap_indexcreateBitmapIndexcreate_bitmap_index

删除接口使用对应 drop_* 名称。index_info/indexInfo 返回 kind、完整 fields、entry/distinct/null 计数并稳定排序。

create_index — 创建 Hash 属性索引

对指定的 JSON Payload 字段建立 O(1) 倒排索引。创建时自动回填全表现有数据,后续 insert / update_payload / delete 自动维护索引一致性。

Python:

db.create_index("name")    # 之后 tql('FIND {name: "Alice"} RETURN *') 使用 O(1) 索引
db.create_index("type")

Node.js:

db.createIndex('name')
db.createIndex('type')

Rust:

db.create_index("name");

drop_index — 删除 Hash 属性索引

删除指定字段的索引。查询仍然可用,只是退化为 O(N) 全表扫描。

Python:

db.drop_index("name")

Node.js:

db.dropIndex('name')

存储格式观测与迁移错误

storage_info/storageInfo 是只读诊断入口,返回产品版本、.tdb 当前/最低版本、WAL/Property/Graph/QuIVer/Text/Manifest 格式、dim、node count、访问模式、估算内存和 sidecar 存在状态。它不会创建、修复或重写文件。

info = db.storage_info()
print(info["database_format_current"])  # 7
print(info["property_index_format"])    # 4
print(info["sidecars"])

历史 API 不再静默翻译:tql_mut 读查询、patch 普通对象简写、Node 数字构造参数分别返回迁移错误;Rust open_with_sync 已删除。历史无头 WAL 返回 UnsupportedWalVersion;带记录的旧版本 WAL 需用旧内核恢复并 flush。恰好只有版本头、没有记录的旧 WAL 可由 ReadWrite 打开流程原子升级,ReadOnly/Immutable 保持零写并要求先由 Writer 完成升级。

持久化与压缩

flush — 手动落盘

将当前内存中的全部数据写入 .tdb 文件。安全写入流程:先写临时文件 → fsync → 原子 rename → 清除 WAL。

Python:

db.flush()

WAL 同步模式

通过构造函数参数或运行时方法切换 WAL(Write-Ahead Log)的同步策略:

模式安全性性能说明
"full"★★★最慢每条写入后 fsync,断电零丢失
"normal"★★☆均衡flush 到 OS 缓冲区,操作系统崩溃可能丢少量数据(默认
"off"★☆☆最快不主动 flush,仅适合测试/批量导入

运行时切换:

db.set_sync_mode("full")   # 切到最安全模式
db.set_sync_mode("off")    # 批量导入时临时提速

enable_auto_compaction — 后台自动压缩

启动后台守护线程,定时在后台串行化执行数据压缩与全量落盘(包含 flush + WAL 截断清理)。

Python:

db.enable_auto_compaction(interval_secs=30)  # 间隔必须大于 0
# db.enable_auto_compaction(interval_secs=0)  # ValueError:拒绝忙循环
db.disable_auto_compaction()                 # 停止后台压缩线程

Rust:

db.enable_auto_compaction(Duration::from_secs(30));
db.disable_auto_compaction();

compact — 手动强制压实 (Manual Compaction)

主动触发一次全量数据重写与压实。此调用会阻塞当前线程,直到所有的内存数据被安全落盘,并彻底截断清理旧的 WAL 文件。 为了极致的崩溃安全性,执行压实时会短暂阻塞前台读写。强烈建议在关闭了自动压缩后,于业务低峰期(如凌晨调度)执行此方法。

Python:

db.compact()

Rust:

db.compact()?;

内存管理

set_memory_limit — 内存预算控制

设置 MemTable 内存使用上限。当估算内存超过限额时,写操作完成后自动触发 flush。

Python:

db.set_memory_limit(mb=256)  # 限制为 256 MB
db.set_memory_limit(mb=0)    # 取消限制(默认)

estimated_memory — 查询当前内存占用

Python:

usage_bytes = db.estimated_memory()
print(f"当前内存占用: {usage_bytes / 1024 / 1024:.1f} MB")

文本索引与稀疏检索

index_text — 建立全文稀疏索引

对指定节点的长文本内容提取 BM25 特征,用于后续的混合检索召回。需在节点 insert 后调用。

Python:

db.index_text(id=42, text="Rust 在嵌入式领域取得突破")

index_keyword — 建立精确关键词索引

建立基于 AC 自动机 (Aho-Corasick) 的精确词汇匹配索引,极速锁定特征锚点。

Python:

db.index_keyword(id=42, keyword="Rust")

build_text_index — 编译倒排字典树

在数据初始化批量调用完毕后,必须调用此方法完成底层 AC 自动机的编译与全局文本 IDF 频率汇算。之后方可进行 search_hybrid 混合检索。

Python:

db.build_text_index()

工具方法

all_node_ids — 获取全部节点 ID

返回当前数据库中所有活跃节点的 ID 列表(顺序不定)。可用于遍历全库或批量操作。

Python:

ids = db.all_node_ids()          # 返回 list[int]
print(f"共 {len(ids)} 个节点")

Rust:

let ids = db.all_node_ids();     // Vec<NodeId>

QuIVer 自动索引说明

TriviumDB v0.7.0 起采用自研的 QuIVer SOTA 级 ANN 图索引,全自动双引擎向量索引路由,无需手动 rebuild_index() 接口:

条件检索引擎召回行为
< 1 万节点 或 QuIVer 未就绪BruteForce100% 精确召回,零误差
≥ 1 万节点 + 索引就绪QuIVer (BQ + Vamana)BQ 签名 + 图导航 + f32 精排,Recall@10 > 97%

QuIVer 索引支持增量 Insert/Delete/Update,无需全量重建。索引以 .tdb.quiver 持久化,并通过 .tdb.quiver.meta 的主数据代际、文件尺寸、节点数、维度与 CRC 校验后 mmap 加载。TextIndex 对应 .tdb.text.tdb.text.meta

批量导入可在构造时设置 auto_build_quiver=False,或运行时调用 set_auto_build_quiver(False),避免导入中途因查询触发索引构建;导入完成后重新开启并执行一次查询,或显式调用 Rust build_quiver_index()flush() 只负责持久化,不会隐式构建 QuIVer。无状态评测或独立查询之间可调用 clear_search_state() 清空疲劳状态。

💡 如果你的业务对 100% 召回率有强需求(如金融/医疗),可以通过 force_brute_force: true 强制使用 BruteForce。


维度迁移

文件格式兼容边界:当前 .tdb 为 v7,可读取 v5–v7;可写句柄在下次 flush/close 原子升级到当前格式。早于 v5 的主文件必须通过旧内核导出迁移;未来版本明确拒绝。sidecar 独立版本化:.pidx v4.gidx v2.text v2.quiver v1

当需要更换 Embedding 模型(维度发生变化)时,使用 migrate 将旧库的结构迁移到新维度。

migrate — 迁移到新维度

将当前数据库的所有节点 Payload、图谱边复制到一个全新的数据库文件中,向量以零向量占位(因为维度变了,旧向量无法直接复用)。

参数:

参数类型说明
new_pathstr新数据库文件路径
new_dimint新的向量维度

返回值: 所有已迁移节点的 ID 列表(list[int]

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:

// 迁移结构
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 / Python)

TriviumDB 提供轻量级事务,采用验证前置(Dry-Run)架构:所有操作先缓冲在内存中,commit() 分两阶段执行——首先在纯内存验证全部约束(维度、节点存在性、ID 冲突),全部通过后才一次性写入。

特性:

  • commit() 返回 Err 时,底层数据没有被修改一个字节,可加入日志后安全重试
  • 在同一事务内,insert_with_id(999) 后立即 link(..., 999) 是完全合法的(虚拟状态叠加给 999 号打过标记)
  • rollback()(或直接 drop 事务对象)将丢弃所有缓冲操作
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();

Python 通过 db.transaction() 暴露同一套缓冲提交语义;Node.js 当前未公开事务构建器。


Pythonic 魔术方法

语法等价调用说明
len(db)db.node_count()当前活跃节点数
42 in dbdb.contains(42)节点是否存在
print(db)db.__repr__()输出如 TriviumDB(dtype=f32, nodes=100, dim=1536)
with db:__enter__ / __exit__退出时自动 flush()

数据类型说明

NodeView

节点的完整视图,通过 get()filter_where() 返回。

属性 (Python)属性 (Rust)类型说明
ididu64全局唯一节点 ID
vectorvectorlist[float] / Vec<T>节点的特征向量
payloadpayloaddict / serde_json::ValueJSON 元数据
edgesedgeslist[Edge] / Vec<Edge>详细出边列表(包含 target_id, label, weight)
num_edgesedges.len()int / usize快速获取出边数量

SearchHit

向量检索命中结果,通过 search() 返回。

属性类型说明
idu64命中节点 ID
scoref32相似度得分
payloaddict节点元数据

QueryRow

Cypher 查询结果行,通过 query() 返回。

属性类型说明
rowdict[str, dict]变量名 → 节点摘要字典

Edge (Rust)

图谱边的内部结构。

字段类型说明
target_idNodeId (u64)目标节点 ID
labelString关系类型标签
weightf32权重(支持负值)

HookContext

Hook 管线执行上下文,通过 search_with_context() 返回。

属性 (Python)属性 (Node.js)属性 (Rust)类型说明
timingstimingsstage_timingsdict / Object / Vec<(String, Duration)>各管线阶段的耗时(Python/JS 单位毫秒)
custom_datacustomDatacustom_datadict / Object / serde_json::ValueHook 注入的自定义数据
abortedabortedabortbool管线是否被 Hook 提前终止