#![allow(non_snake_case)]
use triviumdb::database::{Config, Database, SearchConfig, StorageMode};
use triviumdb::filter::Filter;
const DIM: usize = 3;
fn tmp_db(name: &str) -> String {
let dir = std::env::temp_dir().join("triviumdb_test");
std::fs::create_dir_all(&dir).ok();
dir.join(format!("workflow_{}", name))
.to_string_lossy()
.to_string()
}
fn cleanup(path: &str) {
for ext in &["", ".wal", ".vec", ".lock", ".flush_ok"] {
std::fs::remove_file(format!("{}{}", path, ext)).ok();
}
}
#[test]
fn 测试_全业务链路_社交网络复杂流转() {
let path = tmp_db("social_network");
cleanup(&path);
let config = Config {
dim: DIM,
storage_mode: StorageMode::Mmap,
..Default::default()
};
let mut db = Database::<f32>::open_with_config(&path, config).unwrap();
let alice_id = 1;
let bob_id = 2;
let charlie_id = 3;
{
let mut tx = db.begin_tx();
tx.insert_with_id(
alice_id,
&[1.0, 0.0, 0.0],
serde_json::json!({"name": "Alice", "level": 10}),
);
tx.insert_with_id(
bob_id,
&[0.0, 1.0, 0.0],
serde_json::json!({"name": "Bob", "level": 20}),
);
tx.insert_with_id(
charlie_id,
&[0.0, 0.0, 1.0],
serde_json::json!({"name": "Charlie", "level": 30}),
);
tx.link(alice_id, bob_id, "follows", 1.0);
tx.link(bob_id, charlie_id, "follows", 0.5);
tx.commit().expect("第一阶段事务提交失败");
}
assert_eq!(db.node_count(), 3);
{
let mut tx = db.begin_tx();
tx.update_vector(alice_id, &[0.8, 0.2, 0.0]);
tx.update_payload(bob_id, serde_json::json!({"name": "Bob", "level": 25}));
tx.link(alice_id, charlie_id, "follows", 0.9);
tx.unlink(bob_id, charlie_id);
tx.commit().expect("第二阶段更新事务失败");
}
{
let mut tx = db.begin_tx();
let dave_id = 4;
tx.insert_with_id(
dave_id,
&[0.5, 0.5, 0.5],
serde_json::json!({"name": "Dave"}),
);
tx.link(alice_id, dave_id, "follows", 1.0);
}
assert_eq!(db.node_count(), 3);
{
let results = db
.tql(r#"MATCH (a {name: "Alice"})-[:follows]->(b) RETURN b"#)
.unwrap();
assert_eq!(results.len(), 2, "Alice 的两次关注结果应当独立可见");
let mix_results = db
.search_hybrid(
None,
Some(&[0.0, 0.0, 0.9]),
&SearchConfig {
top_k: 5,
min_score: -1.0, payload_filter: Some(Filter::Gt("level".into(), 15.0)),
..Default::default()
},
)
.unwrap();
assert_eq!(mix_results.len(), 2, "混合查询应当过滤掉 level=10 的 Alice");
assert_eq!(
mix_results[0].id, charlie_id,
"距离最近的应当是 Charlie 自身"
);
}
{
db.delete(bob_id).expect("独立接口删除失败");
db.compact().expect("压实异常");
assert!(!db.contains(bob_id), "节点记录应当已拔除");
assert_eq!(db.node_count(), 2, "此时图谱网络内只剩 2 人");
let rel_check = db
.tql(r#"MATCH (a {name: "Alice"})-[:follows]->(b) RETURN b"#)
.unwrap();
assert_eq!(
rel_check.len(),
1,
"悬空出边应当在 Delete -> Compact 后被彻底摧毁"
);
let survior_id = rel_check[0].get("b").unwrap().id;
assert_eq!(survior_id, charlie_id, "幸存的只有 Charlie");
}
cleanup(&path);
}
#[test]
fn 测试_独立写操作API_与无事务修改的一致性() {
let path = tmp_db("direct_api_mutation");
cleanup(&path);
let mut db = Database::<f32>::open(&path, DIM).unwrap();
let id1 = db
.insert(&[1.0, 1.0, 1.0], serde_json::json!({"status": "new"}))
.unwrap();
db.update_payload(id1, serde_json::json!({"status": "updated"}))
.unwrap();
db.update_vector(id1, &[2.0, 2.0, 2.0]).unwrap();
db.flush().unwrap();
let p = db.get_payload(id1).unwrap();
assert_eq!(p["status"], "updated");
let n = db.get(id1).unwrap();
assert_eq!(n.vector, vec![2.0, 2.0, 2.0]);
cleanup(&path);
}