mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-21 02:28:07 +02:00
test(engine): regression for multi-hop anti-join hop bounds
The bulk anti-join fast path answers via has_neighbors (one-hop existence), so
not { $p knows{2,2} $x } wrongly drops a node with a 1-hop neighbor but no
2-hop path. On a->b (sink) and c->d->e, only c has a 2-hop path; the query should
keep [a,b,d,e]. Fails on current code (left ["b","e"] — only the sinks).
This commit is contained in:
parent
3d99d3ee0e
commit
4aca3dfece
1 changed files with 45 additions and 0 deletions
|
|
@ -189,6 +189,51 @@ query no_nonacme_employer() {
|
||||||
assert_eq!(names_vec, vec!["p3", "p4"]);
|
assert_eq!(names_vec, vec!["p3", "p4"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression: a multi-hop anti-join must not take the bulk fast path. The fast
|
||||||
|
// path answers via `has_neighbors` (ONE-hop existence), so `not { $p knows{2,2}
|
||||||
|
// $x }` would wrongly drop a node that has a 1-hop neighbor but no 2-hop path.
|
||||||
|
// Graph: a->b (b is a sink, so a has no 2-hop path), c->d->e (c has a 2-hop
|
||||||
|
// path). Only c has a 2-hop knows path, so only c is removed.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn anti_join_respects_multi_hop_bounds() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let uri = dir.path().to_str().unwrap();
|
||||||
|
let data = r#"{"type":"Person","data":{"name":"a"}}
|
||||||
|
{"type":"Person","data":{"name":"b"}}
|
||||||
|
{"type":"Person","data":{"name":"c"}}
|
||||||
|
{"type":"Person","data":{"name":"d"}}
|
||||||
|
{"type":"Person","data":{"name":"e"}}
|
||||||
|
{"edge":"Knows","from":"a","to":"b"}
|
||||||
|
{"edge":"Knows","from":"c","to":"d"}
|
||||||
|
{"edge":"Knows","from":"d","to":"e"}"#;
|
||||||
|
let mut db = Omnigraph::init(uri, TEST_SCHEMA).await.unwrap();
|
||||||
|
load_jsonl(&mut db, data, LoadMode::Overwrite).await.unwrap();
|
||||||
|
|
||||||
|
let queries = r#"
|
||||||
|
query no_two_hop() {
|
||||||
|
match {
|
||||||
|
$p: Person
|
||||||
|
not { $p knows{2,2} $x }
|
||||||
|
}
|
||||||
|
return { $p.name }
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let result = query_main(&mut db, queries, "no_two_hop", &ParamMap::new())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let batch = result.concat_batches().unwrap();
|
||||||
|
let names = batch
|
||||||
|
.column(0)
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<StringArray>()
|
||||||
|
.unwrap();
|
||||||
|
let mut names_vec: Vec<&str> = (0..names.len()).map(|i| names.value(i)).collect();
|
||||||
|
names_vec.sort();
|
||||||
|
// Only c has a 2-hop knows path → removed; everyone else (incl. a, which has
|
||||||
|
// a 1-hop neighbor but no 2-hop path) is kept.
|
||||||
|
assert_eq!(names_vec, vec!["a", "b", "d", "e"]);
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Variable-length hops ───────────────────────────────────────────────────
|
// ─── Variable-length hops ───────────────────────────────────────────────────
|
||||||
|
|
||||||
const CHAIN_SCHEMA: &str = r#"
|
const CHAIN_SCHEMA: &str = r#"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue