triviumdb 0.7.0

A high-performance memory-mmap hybrid search engine built for AI, combining dense vector, sparse text, graph relations, and JSON metadata.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
#![allow(non_snake_case)]
//! TQL (Trivium Query Language) 解析器集成测试
//!
//! 覆盖三种查询入口的解析正确性:
//! - MATCH: 图模式匹配(含可变长路径、多标签边)
//! - FIND: MongoDB 风格文档过滤
//! - SEARCH: 向量检索 + EXPAND
//! - WHERE: 统一谓词(Cypher 比较 + Mongo 文档过滤 + 混合)

use triviumdb::filter::Filter;
use triviumdb::query::tql_ast::*;
use triviumdb::query::tql_parser::parse_tql;

// ═══════════════════════════════════════════════════════════════════════
//  FIND 入口测试
// ═══════════════════════════════════════════════════════════════════════

#[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));
}

// ═══════════════════════════════════════════════════════════════════════
//  MATCH 入口测试
// ═══════════════════════════════════════════════════════════════════════

#[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操作符() {
    // Q1 决策 B: 内联属性支持 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");
    }
}

// ═══════════════════════════════════════════════════════════════════════
//  WHERE 统一谓词测试
// ═══════════════════════════════════════════════════════════════════════

#[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();
    // 结构应为 And(Or(x>1, y>2), z==3)
    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()); // FIND 场景下 var 为 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(_, _))));
}

// ═══════════════════════════════════════════════════════════════════════
//  SEARCH 入口测试
// ═══════════════════════════════════════════════════════════════════════

#[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));
}

// ═══════════════════════════════════════════════════════════════════════
//  RETURN 子句测试
// ═══════════════════════════════════════════════════════════════════════

#[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());
}