Up
This commit is contained in:
parent
0b89e64773
commit
b29141c47c
@ -18,41 +18,81 @@ The operators are: atom scan, semijoin, and natural join.
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
`Q(X) :- edge(X, X), labeled(X).` (labeled self-loops):
|
The rule below returns the authors of every bestseller along with the book's price.
|
||||||
|
It uses all three operators: `scan_atom` for the three input tables, `semijoin` to keep only authors of bestsellers, and `natural_join` to attach each
|
||||||
|
book's price.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Q(name, book, dollars) :- author(name, book), bestseller(book), price(book, dollars).
|
||||||
|
```
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use query_ops::atom::{AtomPattern, Term, scan_atom};
|
use query_ops::atom::{AtomPattern, Term, scan_atom};
|
||||||
use query_ops::join::semijoin;
|
use query_ops::join::{natural_join, semijoin};
|
||||||
use query_ops::table::Table;
|
use query_ops::table::Table;
|
||||||
use query_ops::value::Value;
|
use query_ops::value::Value;
|
||||||
|
|
||||||
|
fn s(x: &str) -> Value {
|
||||||
|
Value::Str(x.to_string())
|
||||||
|
}
|
||||||
|
fn i(x: i64) -> Value {
|
||||||
|
Value::Int(x)
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let edge = Table::from_rows(
|
let author = Table::from_rows(
|
||||||
2,
|
2,
|
||||||
vec![
|
vec![
|
||||||
vec![Value::Int(1), Value::Int(2)],
|
vec![s("Alice"), s("Foo")],
|
||||||
vec![Value::Int(3), Value::Int(3)], // self-loop on 3
|
vec![s("Bob"), s("Bar")],
|
||||||
vec![Value::Int(2), Value::Int(2)], // self-loop on 2
|
vec![s("Alice"), s("Baz")],
|
||||||
|
vec![s("Carol"), s("Qux")],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let bestseller = Table::from_rows(1, vec![vec![s("Foo")], vec![s("Baz")]]);
|
||||||
|
let price = Table::from_rows(
|
||||||
|
2,
|
||||||
|
vec![
|
||||||
|
vec![s("Foo"), i(25)],
|
||||||
|
vec![s("Bar"), i(15)],
|
||||||
|
vec![s("Baz"), i(30)],
|
||||||
|
vec![s("Qux"), i(20)],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
let labeled = Table::from_rows(1, vec![vec![Value::Int(2)]]);
|
|
||||||
|
|
||||||
let self_loops = scan_atom(
|
let author_rel = scan_atom(
|
||||||
&edge,
|
&author,
|
||||||
&AtomPattern {
|
&AtomPattern {
|
||||||
columns: vec![Term::Var("X".to_string()), Term::Var("X".to_string())],
|
columns: vec![Term::Var("name".to_string()), Term::Var("book".to_string())],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let labeled_x = scan_atom(
|
let bestseller_rel = scan_atom(
|
||||||
&labeled,
|
&bestseller,
|
||||||
&AtomPattern {
|
&AtomPattern {
|
||||||
columns: vec![Term::Var("X".to_string())],
|
columns: vec![Term::Var("book".to_string())],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let price_rel = scan_atom(
|
||||||
|
&price,
|
||||||
|
&AtomPattern {
|
||||||
|
columns: vec![Term::Var("book".to_string()), Term::Var("dollars".to_string())],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let result = semijoin(&self_loops, &labeled_x);
|
|
||||||
|
|
||||||
assert_eq!(result.columns, vec!["X".to_string()]);
|
let authors_of_bestsellers = semijoin(&author_rel, &bestseller_rel);
|
||||||
assert_eq!(result.rows, vec![vec![Value::Int(2)]]);
|
let result = natural_join(&authors_of_bestsellers, &price_rel);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result.columns,
|
||||||
|
vec!["name".to_string(), "book".to_string(), "dollars".to_string()],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result.rows,
|
||||||
|
vec![
|
||||||
|
vec![s("Alice"), s("Foo"), i(25)],
|
||||||
|
vec![s("Alice"), s("Baz"), i(30)],
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -1,77 +1,91 @@
|
|||||||
//! Hand-written query plans composed from `scan_atom`, `semijoin`, and `natural_join`.
|
//! Hand-written query plan composed from `scan_atom`, `semijoin`, and `natural_join`.
|
||||||
//!
|
//!
|
||||||
//! Schema:
|
//! Schema:
|
||||||
//! - `edge(src, dst)`: directed edges
|
//! - `author(name, book)`: who wrote each book
|
||||||
//! - `labeled(node)`: a set of labeled nodes
|
//! - `bestseller(book)`: the set of bestseller titles
|
||||||
|
//! - `price(book, dollars)`: price of each book
|
||||||
//!
|
//!
|
||||||
//! Two rules are executed against the same fixture:
|
//! Rule:
|
||||||
//! - `Q1(X) :- edge(X, X), labeled(X).` (labeled self-loops)
|
//! - `Q(name, book, dollars) :- author(name, book), bestseller(book), price(book, dollars).`
|
||||||
//! - `Q2(X, Y) :- edge(X, Y), labeled(Y).` (edges whose destination is labeled)
|
//! ("Authors of bestsellers along with each book's price.")
|
||||||
|
//!
|
||||||
|
//! The plan first scans each input table, then narrows `author` to authors of
|
||||||
|
//! bestsellers via a semijoin against `bestseller`, then attaches each book's
|
||||||
|
//! price via a natural join against `price`.
|
||||||
|
|
||||||
use query_ops::atom::{scan_atom, AtomPattern, Term};
|
use query_ops::atom::{AtomPattern, Term, scan_atom};
|
||||||
use query_ops::join::{natural_join, semijoin};
|
use query_ops::join::{natural_join, semijoin};
|
||||||
use query_ops::table::Table;
|
use query_ops::table::Table;
|
||||||
use query_ops::value::Value;
|
use query_ops::value::Value;
|
||||||
|
|
||||||
fn var(name: &str) -> Term {
|
fn s(x: &str) -> Value {
|
||||||
Term::Var(name.to_string())
|
Value::Str(x.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int(value: i64) -> Value {
|
fn i(x: i64) -> Value {
|
||||||
Value::Int(value)
|
Value::Int(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn labeled_self_loops_and_edges_into_labeled_nodes() {
|
fn authors_of_bestsellers_with_price() {
|
||||||
let edge = Table::from_rows(
|
let author = Table::from_rows(
|
||||||
2,
|
2,
|
||||||
vec![
|
vec![
|
||||||
vec![int(1), int(2)],
|
vec![s("Alice"), s("Foo")],
|
||||||
vec![int(2), int(3)],
|
vec![s("Bob"), s("Bar")],
|
||||||
vec![int(3), int(3)],
|
vec![s("Alice"), s("Baz")],
|
||||||
vec![int(4), int(1)],
|
vec![s("Carol"), s("Qux")],
|
||||||
vec![int(2), int(2)],
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
let labeled = Table::from_rows(1, vec![vec![int(2)], vec![int(3)]]);
|
let bestseller = Table::from_rows(1, vec![vec![s("Foo")], vec![s("Baz")]]);
|
||||||
|
let price = Table::from_rows(
|
||||||
let self_loops = scan_atom(
|
2,
|
||||||
&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![
|
||||||
vec![int(1), int(2)],
|
vec![s("Foo"), i(25)],
|
||||||
vec![int(2), int(3)],
|
vec![s("Bar"), i(15)],
|
||||||
vec![int(3), int(3)],
|
vec![s("Baz"), i(30)],
|
||||||
vec![int(2), int(2)],
|
vec![s("Qux"), i(20)],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let author_rel = scan_atom(
|
||||||
|
&author,
|
||||||
|
&AtomPattern {
|
||||||
|
columns: vec![Term::Var("name".to_string()), Term::Var("book".to_string())],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let bestseller_rel = scan_atom(
|
||||||
|
&bestseller,
|
||||||
|
&AtomPattern {
|
||||||
|
columns: vec![Term::Var("book".to_string())],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let price_rel = scan_atom(
|
||||||
|
&price,
|
||||||
|
&AtomPattern {
|
||||||
|
columns: vec![
|
||||||
|
Term::Var("book".to_string()),
|
||||||
|
Term::Var("dollars".to_string()),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let authors_of_bestsellers = semijoin(&author_rel, &bestseller_rel);
|
||||||
|
let result = natural_join(&authors_of_bestsellers, &price_rel);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result.columns,
|
||||||
|
vec![
|
||||||
|
"name".to_string(),
|
||||||
|
"book".to_string(),
|
||||||
|
"dollars".to_string()
|
||||||
|
],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result.rows,
|
||||||
|
vec![
|
||||||
|
vec![s("Alice"), s("Foo"), i(25)],
|
||||||
|
vec![s("Alice"), s("Baz"), i(30)],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user