2026-06-03 11:48:33 +02:00

78 lines
2.0 KiB
Rust

//! Hand-written query plans composed from `scan_atom`, `semijoin`, and `natural_join`.
//!
//! Schema:
//! - `edge(src, dst)`: directed edges
//! - `labeled(node)`: a set of labeled nodes
//!
//! Two rules are executed against the same fixture:
//! - `Q1(X) :- edge(X, X), labeled(X).` (labeled self-loops)
//! - `Q2(X, Y) :- edge(X, Y), labeled(Y).` (edges whose destination is labeled)
use query_ops::atom::{scan_atom, AtomPattern, Term};
use query_ops::join::{natural_join, semijoin};
use query_ops::table::Table;
use query_ops::value::Value;
fn var(name: &str) -> Term {
Term::Var(name.to_string())
}
fn int(value: i64) -> Value {
Value::Int(value)
}
#[test]
fn labeled_self_loops_and_edges_into_labeled_nodes() {
let edge = Table::from_rows(
2,
vec![
vec![int(1), int(2)],
vec![int(2), int(3)],
vec![int(3), int(3)],
vec![int(4), int(1)],
vec![int(2), int(2)],
],
);
let labeled = Table::from_rows(1, vec![vec![int(2)], vec![int(3)]]);
let self_loops = scan_atom(
&edge,
&AtomPattern {
columns: vec![var("X"), var("X")],
},
);
let labeled_x = scan_atom(
&labeled,
&AtomPattern {
columns: vec![var("X")],
},
);
let q1 = semijoin(&self_loops, &labeled_x);
assert_eq!(q1.columns, vec!["X".to_string()]);
assert_eq!(q1.rows, vec![vec![int(3)], vec![int(2)]]);
let edge_xy = scan_atom(
&edge,
&AtomPattern {
columns: vec![var("X"), var("Y")],
},
);
let labeled_y = scan_atom(
&labeled,
&AtomPattern {
columns: vec![var("Y")],
},
);
let q2 = natural_join(&edge_xy, &labeled_y);
assert_eq!(q2.columns, vec!["X".to_string(), "Y".to_string()]);
assert_eq!(
q2.rows,
vec![
vec![int(1), int(2)],
vec![int(2), int(3)],
vec![int(3), int(3)],
vec![int(2), int(2)],
],
);
}