#![allow(non_snake_case)]
use triviumdb::filter::Filter;
use triviumdb::query::tql_ast::*;
use triviumdb::query::tql_parser::parse_tql;
#[test]
fn FIND_简单等值匹配() {
let q = parse_tql(r#"FIND {type: "event"} RETURN *"#).unwrap();
assert!(matches!(q.entry, QueryEntry::Find { .. }));
assert!(matches!(q.returns, ReturnClause::All));
}
#[test]
fn FIND_多字段隐式AND() {
let q = parse_tql(r#"FIND {type: "event", region: "cn"} RETURN *"#).unwrap();
if let QueryEntry::Find { filter } = &q.entry {
assert!(matches!(filter, Filter::And(_)));
} else {
panic!("Expected Find entry");
}
}
#[test]
fn FIND_操作符语法() {
let q = parse_tql(r#"FIND {age: {$gt: 18}, heat: {$lte: 0.9}} RETURN *"#).unwrap();
if let QueryEntry::Find { filter } = &q.entry {
assert!(matches!(filter, Filter::And(_)));
} else {
panic!("Expected Find entry");
}
}
#[test]
fn FIND_In操作符() {
let q = parse_tql(r#"FIND {type: {$in: ["event", "incident"]}} RETURN *"#).unwrap();
if let QueryEntry::Find { filter } = &q.entry {
assert!(matches!(filter, Filter::In(_, _)));
} else {
panic!("Expected Find entry");
}
}
#[test]
fn FIND_Exists操作符() {
let q = parse_tql(r#"FIND {heat: {$exists: true}} RETURN *"#).unwrap();
if let QueryEntry::Find { filter } = &q.entry {
assert!(matches!(filter, Filter::Exists(_, true)));
} else {
panic!("Expected Find entry");
}
}
#[test]
fn FIND_All操作符() {
let q = parse_tql(r#"FIND {tags: {$all: ["AI", "security"]}} RETURN *"#).unwrap();
if let QueryEntry::Find { filter } = &q.entry {
assert!(matches!(filter, Filter::All(_, _)));
} else {
panic!("Expected Find entry");
}
}
#[test]
fn FIND_逻辑组合_Or() {
let q = parse_tql(r#"FIND {$or: [{type: "event"}, {type: "person"}]} RETURN *"#).unwrap();
if let QueryEntry::Find { filter } = &q.entry {
assert!(matches!(filter, Filter::Or(_)));
} else {
panic!("Expected Find entry with Or");
}
}
#[test]
fn FIND_带LIMIT_OFFSET() {
let q = parse_tql(r#"FIND {type: "event"} RETURN * LIMIT 10 OFFSET 20"#).unwrap();
assert_eq!(q.limit, Some(10));
assert_eq!(q.offset, Some(20));
}
#[test]
fn FIND_带ORDER_BY() {
let q = parse_tql(r#"FIND {type: "event"} RETURN * ORDER BY a.score DESC LIMIT 5"#).unwrap();
assert_eq!(q.order_by.len(), 1);
assert!(q.order_by[0].descending);
assert_eq!(q.limit, Some(5));
}
#[test]
fn MATCH_单节点() {
let q = parse_tql(r#"MATCH (a {name: "Alice"}) RETURN a"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
assert_eq!(pattern.nodes.len(), 1);
assert_eq!(pattern.edges.len(), 0);
assert_eq!(pattern.nodes[0].var, Some("a".into()));
assert!(pattern.nodes[0].filter.is_some());
} else {
panic!("Expected Match entry");
}
}
#[test]
fn MATCH_单跳路径() {
let q = parse_tql(r#"MATCH (a)-[:knows]->(b) RETURN b"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
assert_eq!(pattern.nodes.len(), 2);
assert_eq!(pattern.edges.len(), 1);
assert_eq!(pattern.edges[0].labels, vec!["knows"]);
assert!(pattern.edges[0].hop_range.is_none());
} else {
panic!("Expected Match entry");
}
}
#[test]
fn MATCH_多跳路径() {
let q = parse_tql(r#"MATCH (a)-[:knows]->(b)-[:works_at]->(c) RETURN c"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
assert_eq!(pattern.nodes.len(), 3);
assert_eq!(pattern.edges.len(), 2);
assert_eq!(pattern.edges[0].labels, vec!["knows"]);
assert_eq!(pattern.edges[1].labels, vec!["works_at"]);
} else {
panic!("Expected Match entry");
}
}
#[test]
fn MATCH_可变长路径() {
let q = parse_tql(r#"MATCH (a)-[:knows*1..3]->(b) RETURN b"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
let hop = pattern.edges[0].hop_range.unwrap();
assert_eq!(hop.min, 1);
assert_eq!(hop.max, 3);
} else {
panic!("Expected Match entry");
}
}
#[test]
fn MATCH_多标签边_管道分隔() {
let q = parse_tql(r#"MATCH (a)-[:knows|works_with]->(b) RETURN b"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
assert_eq!(pattern.edges[0].labels, vec!["knows", "works_with"]);
} else {
panic!("Expected Match entry");
}
}
#[test]
fn MATCH_多标签_可变长_组合() {
let q = parse_tql(r#"MATCH (a)-[:knows|derived_from*1..5]->(b) RETURN b"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
assert_eq!(pattern.edges[0].labels, vec!["knows", "derived_from"]);
let hop = pattern.edges[0].hop_range.unwrap();
assert_eq!(hop.min, 1);
assert_eq!(hop.max, 5);
} else {
panic!("Expected Match entry");
}
}
#[test]
fn MATCH_任意边() {
let q = parse_tql(r#"MATCH (a)-[]->(b) RETURN a, b"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
assert!(pattern.edges[0].labels.is_empty());
} else {
panic!("Expected Match entry");
}
}
#[test]
fn MATCH_内联Mongo操作符() {
let q = parse_tql(r#"MATCH (a {age: {$gt: 18}}) RETURN a"#).unwrap();
if let QueryEntry::Match { pattern } = &q.entry {
assert!(pattern.nodes[0].filter.is_some());
let f = pattern.nodes[0].filter.as_ref().unwrap();
assert!(matches!(f, Filter::Gt(_, _)));
} else {
panic!("Expected Match entry");
}
}
#[test]
fn WHERE_Cypher比较() {
let q = parse_tql(r#"MATCH (a)-[:knows]->(b) WHERE b.age > 20 RETURN b"#).unwrap();
assert!(q.predicate.is_some());
if let Some(Predicate::Compare { op, .. }) = &q.predicate {
assert!(matches!(op, TqlCompOp::Gt));
} else {
panic!("Expected Compare predicate");
}
}
#[test]
fn WHERE_AND组合() {
let q = parse_tql(r#"MATCH (a)-[:knows]->(b) WHERE a.name == "Alice" AND b.age > 20 RETURN b"#)
.unwrap();
assert!(matches!(q.predicate, Some(Predicate::And(_, _))));
}
#[test]
fn WHERE_OR组合() {
let q = parse_tql(r#"MATCH (a)-[:knows]->(b) WHERE a.score > 0.5 OR b.type == "vip" RETURN b"#)
.unwrap();
assert!(matches!(q.predicate, Some(Predicate::Or(_, _))));
}
#[test]
fn WHERE_括号优先级() {
let q =
parse_tql(r#"MATCH (a)-[]->(b) WHERE (a.x > 1 OR a.y > 2) AND b.z == 3 RETURN b"#).unwrap();
assert!(matches!(q.predicate, Some(Predicate::And(_, _))));
}
#[test]
fn WHERE_NOT() {
let q = parse_tql(r#"MATCH (a) WHERE NOT a.deleted == true RETURN a"#).unwrap();
assert!(matches!(q.predicate, Some(Predicate::Not(_))));
}
#[test]
fn WHERE_文档过滤() {
let q = parse_tql(r#"FIND {type: "event"} WHERE {heat: {$gte: 0.7}} RETURN *"#).unwrap();
if let Some(Predicate::DocFilter { var, .. }) = &q.predicate {
assert!(var.is_none()); } else {
panic!("Expected DocFilter predicate");
}
}
#[test]
fn WHERE_变量绑定MATCHES() {
let q = parse_tql(
r#"MATCH (a)-[:reports_to]->(boss) WHERE boss MATCHES {level: {$in: ["director", "vp"]}} RETURN boss"#
).unwrap();
if let Some(Predicate::DocFilter { var, filter }) = &q.predicate {
assert_eq!(var.as_deref(), Some("boss"));
assert!(matches!(filter, Filter::In(_, _)));
} else {
panic!("Expected DocFilter with var binding");
}
}
#[test]
fn WHERE_混合Cypher和MATCHES() {
let q = parse_tql(
r#"MATCH (a)-[:knows]->(b) WHERE a.region == "cn" AND b MATCHES {role: {$in: ["admin"]}} RETURN b"#
).unwrap();
assert!(matches!(q.predicate, Some(Predicate::And(_, _))));
}
#[test]
fn SEARCH_基础向量检索() {
let q = parse_tql(r#"SEARCH VECTOR [0.1, 0.2, 0.3] TOP 10 RETURN *"#).unwrap();
if let QueryEntry::Search {
vector,
top_k,
expand,
} = &q.entry
{
assert_eq!(vector.len(), 3);
assert!((vector[0] - 0.1).abs() < 1e-10);
assert_eq!(*top_k, 10);
assert!(expand.is_none());
} else {
panic!("Expected Search entry");
}
}
#[test]
fn SEARCH_带EXPAND() {
let q =
parse_tql(r#"SEARCH VECTOR [0.1, -0.2] TOP 20 EXPAND [:related*1..2] RETURN *"#).unwrap();
if let QueryEntry::Search { expand, .. } = &q.entry {
let ex = expand.as_ref().unwrap();
assert_eq!(ex.labels, vec!["related"]);
assert_eq!(ex.min_depth, 1);
assert_eq!(ex.max_depth, 2);
} else {
panic!("Expected Search entry");
}
}
#[test]
fn SEARCH_负数向量() {
let q = parse_tql(r#"SEARCH VECTOR [-0.5, 0.3, -1] TOP 5 RETURN *"#).unwrap();
if let QueryEntry::Search { vector, .. } = &q.entry {
assert!((vector[0] - (-0.5)).abs() < 1e-10);
assert!((vector[2] - (-1.0)).abs() < 1e-10);
} else {
panic!("Expected Search entry");
}
}
#[test]
fn SEARCH_带WHERE过滤() {
let q = parse_tql(r#"SEARCH VECTOR [0.1, 0.2] TOP 50 WHERE {type: "event"} RETURN *"#).unwrap();
assert!(q.predicate.is_some());
}
#[test]
fn SEARCH_完整三融合() {
let q = parse_tql(
r#"SEARCH VECTOR [0.1, 0.2] TOP 20 EXPAND [:related*1..2] WHERE {heat: {$gte: 0.7}} RETURN * ORDER BY a.score DESC LIMIT 10"#
).unwrap();
assert!(matches!(q.entry, QueryEntry::Search { .. }));
assert!(q.predicate.is_some());
assert_eq!(q.order_by.len(), 1);
assert!(q.order_by[0].descending);
assert_eq!(q.limit, Some(10));
}
#[test]
fn RETURN_星号() {
let q = parse_tql(r#"FIND {type: "event"} RETURN *"#).unwrap();
assert!(matches!(q.returns, ReturnClause::All));
}
#[test]
fn RETURN_多变量() {
let q = parse_tql(r#"MATCH (a)-[]->(b) RETURN a, b"#).unwrap();
if let ReturnClause::Variables(vars) = &q.returns {
assert_eq!(vars, &["a", "b"]);
} else {
panic!("Expected Variables return");
}
}
#[test]
fn 行注释被跳过() {
let q = parse_tql("FIND {type: \"event\"} -- 查找所有事件\nRETURN *").unwrap();
assert!(matches!(q.entry, QueryEntry::Find { .. }));
}
#[test]
fn 错误_缺少RETURN() {
let result = parse_tql(r#"FIND {type: "event"}"#);
assert!(result.is_err());
}
#[test]
fn 错误_空文档过滤() {
let result = parse_tql(r#"FIND {} RETURN *"#);
assert!(result.is_err());
}
#[test]
fn 错误_未知操作符() {
let result = parse_tql(r#"FIND {age: {$unknown: 5}} RETURN *"#);
assert!(result.is_err());
}
#[test]
fn 错误_跳数范围反转() {
let result = parse_tql(r#"MATCH (a)-[:knows*5..1]->(b) RETURN b"#);
assert!(result.is_err());
}
#[test]
fn 错误_匿名中间节点() {
let result = parse_tql(r#"MATCH (a)-[:knows]->()-[:works_at]->(c) RETURN c"#);
assert!(result.is_err());
}
#[test]
fn 错误_未知查询入口() {
let result = parse_tql(r#"SELECT * FROM nodes"#);
assert!(result.is_err());
}