78 lines
2.0 KiB
Rust
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)],
|
|
],
|
|
);
|
|
}
|