use anyhow::Result;
use uni_db::{DataType, Uni};
#[tokio::test]
async fn test_where_property_simple() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob', age: 25})")
.await?;
tx.execute("CREATE (n:Person {name: 'Charlie', age: 35})")
.await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.age > 28 RETURN n.name ORDER BY n.name")
.await?;
assert_eq!(result.len(), 2, "Expected 2 results, got {}", result.len());
let name1: String = result.rows()[0].get("n.name")?;
let name2: String = result.rows()[1].get("n.name")?;
assert_eq!(name1, "Alice");
assert_eq!(name2, "Charlie");
Ok(())
}
#[tokio::test]
async fn test_where_equality() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob', age: 25})")
.await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.age = 30 RETURN n.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("n.name")?;
assert_eq!(name, "Alice");
Ok(())
}
#[tokio::test]
async fn test_where_string_equality() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice'})").await?;
tx.execute("CREATE (n:Person {name: 'Bob'})").await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.name = 'Alice' RETURN n.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("n.name")?;
assert_eq!(name, "Alice");
Ok(())
}
#[tokio::test]
async fn test_where_and_condition() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 25})")
.await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.name = 'Alice' AND n.age = 30 RETURN n")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_where_edge_property() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.edge_type("KNOWS", &["Person"], &["Person"])
.property("since", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (a:Person {name: 'Alice'})").await?;
tx.execute("CREATE (b:Person {name: 'Bob'})").await?;
tx.execute("CREATE (c:Person {name: 'Charlie'})").await?;
tx.execute("MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) CREATE (a)-[:KNOWS {since: 2020}]->(b)").await?;
tx.execute("MATCH (a:Person {name: 'Alice'}), (c:Person {name: 'Charlie'}) CREATE (a)-[:KNOWS {since: 2022}]->(c)").await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (a:Person)-[r:KNOWS]->(b:Person) WHERE r.since = 2020 RETURN b.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("b.name")?;
assert_eq!(name, "Bob");
Ok(())
}
#[tokio::test]
async fn test_where_or_condition() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob', age: 25})")
.await?;
tx.execute("CREATE (n:Person {name: 'Charlie', age: 35})")
.await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.age = 25 OR n.age = 35 RETURN n.name ORDER BY n.name")
.await?;
assert_eq!(result.len(), 2, "Expected 2 results, got {}", result.len());
let name1: String = result.rows()[0].get("n.name")?;
let name2: String = result.rows()[1].get("n.name")?;
assert_eq!(name1, "Bob");
assert_eq!(name2, "Charlie");
Ok(())
}
#[tokio::test]
async fn test_where_less_than() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob', age: 25})")
.await?;
tx.execute("CREATE (n:Person {name: 'Charlie', age: 35})")
.await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.age < 30 RETURN n.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("n.name")?;
assert_eq!(name, "Bob");
Ok(())
}
#[tokio::test]
async fn test_where_not() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob', age: 25})")
.await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE NOT n.age = 30 RETURN n.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("n.name")?;
assert_eq!(name, "Bob");
Ok(())
}
#[tokio::test]
async fn test_where_type_coercion() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Item")
.property("price", DataType::Float64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Item {price: 10.5})").await?;
tx.execute("CREATE (n:Item {price: 20.0})").await?;
tx.execute("CREATE (n:Item {price: 5.5})").await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Item) WHERE n.price > 10 RETURN n.price ORDER BY n.price")
.await?;
assert_eq!(result.len(), 2, "Expected 2 results, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_where_label_predicate() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("A")
.property("id", DataType::Int64)
.label("B")
.property("id", DataType::Int64)
.label("C")
.property("id", DataType::Int64)
.property("a", DataType::String)
.edge_type("ADMIN", &["A", "B", "C"], &["A", "B", "C"])
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (a:A {id: 0}), (b:B {id: 1}), (c:C {id: 2, a: 'A'})")
.await?;
tx.execute("MATCH (a:A {id: 0}), (b:B {id: 1}) CREATE (b)-[:ADMIN]->(a)")
.await?;
tx.execute("MATCH (b:B {id: 1}), (c:C {id: 2}) CREATE (b)-[:ADMIN]->(c)")
.await?;
tx.commit().await?;
let result_all = db
.session()
.query("MATCH (n) RETURN n, labels(n) as lbls")
.await?;
eprintln!("All nodes ({}):", result_all.len());
for row in result_all.rows() {
let n_val = row.value("n");
let lbls_val = row.value("lbls");
eprintln!(" n={:?}, labels={:?}", n_val, lbls_val);
}
let result = db.session().query("MATCH (n) WHERE n:A RETURN n").await?;
eprintln!("Nodes with label A: {}", result.len());
for row in result.rows() {
let n_val = row.value("n");
eprintln!(" n={:?}", n_val);
}
assert_eq!(
result.len(),
1,
"Expected 1 node with label A, got {}",
result.len()
);
let result = db
.session()
.query("MATCH (a)-[:ADMIN]-(b) WHERE a:A RETURN a.id, b.id")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let a_id: i64 = result.rows()[0].get("a.id")?;
let b_id: i64 = result.rows()[0].get("b.id")?;
assert_eq!(a_id, 0);
assert_eq!(b_id, 1);
Ok(())
}
#[tokio::test]
async fn test_where_is_null() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property_nullable("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob'})").await?; tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.age IS NULL RETURN n.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("n.name")?;
assert_eq!(name, "Bob");
Ok(())
}
#[tokio::test]
async fn test_where_is_not_null() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.property_nullable("age", DataType::Int64)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (n:Person {name: 'Alice', age: 30})")
.await?;
tx.execute("CREATE (n:Person {name: 'Bob'})").await?; tx.commit().await?;
let result = db
.session()
.query("MATCH (n:Person) WHERE n.age IS NOT NULL RETURN n.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("n.name")?;
assert_eq!(name, "Alice");
Ok(())
}
#[tokio::test]
async fn test_where_undirected_edge() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.edge_type("KNOWS", &["Person"], &["Person"])
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})")
.await?;
tx.execute(
"MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}) CREATE (a)-[:KNOWS]->(b)",
)
.await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (a)-[:KNOWS]-(b) WHERE a.name = 'Alice' RETURN b.name")
.await?;
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
let name: String = result.rows()[0].get("b.name")?;
assert_eq!(name, "Bob");
Ok(())
}
#[tokio::test]
async fn test_where_start_node_property() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.label("Other")
.edge_type("T", &["Person"], &["Other"])
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (a:Person {name: 'Alice'})-[:T]->(:Other)")
.await?;
tx.execute("CREATE (b:Person {name: 'Bob'})-[:T]->(:Other)")
.await?;
tx.commit().await?;
let all = db
.session()
.query("MATCH (n:Person)-->() RETURN n.name")
.await?;
eprintln!("All Person nodes with outgoing edges: {} rows", all.len());
for row in all.rows() {
eprintln!(" n.name = {:?}", row.value("n.name"));
}
let result = db
.session()
.query("MATCH (n:Person)-->() WHERE n.name = 'Bob' RETURN n")
.await?;
eprintln!("After WHERE n.name = 'Bob': {} rows", result.len());
for row in result.rows() {
eprintln!(" n = {:?}", row.value("n"));
}
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_where_start_node_schemaless() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}), (c), (d)")
.await?;
tx.execute("MATCH (a:Person {name: 'Alice'}), (c) WHERE c.name IS NULL RETURN a, c LIMIT 1")
.await?;
tx.commit().await?;
let all_nodes = db
.session()
.query("MATCH (n) RETURN n, labels(n) as lbls")
.await?;
eprintln!("All nodes ({}): ", all_nodes.len());
for row in all_nodes.rows() {
eprintln!(" n={:?}, labels={:?}", row.value("n"), row.value("lbls"));
}
let unlabeled = db
.session()
.query("MATCH (n) WHERE NOT n:Person RETURN n")
.await?;
eprintln!("Unlabeled nodes ({}): ", unlabeled.len());
let db2 = Uni::in_memory().build().await?;
let session2 = db2.session();
let tx2 = session2.tx().await?;
tx2.execute("CREATE (a:Person {name: 'Alice'})-[:T]->(:Other)")
.await?;
tx2.execute("CREATE (b:Person {name: 'Bob'})-[:T]->(:Other)")
.await?;
tx2.commit().await?;
let all_nodes2 = db2
.session()
.query("MATCH (n) RETURN n, labels(n) as lbls")
.await?;
eprintln!("All nodes in db2 ({}): ", all_nodes2.len());
for row in all_nodes2.rows() {
eprintln!(" n={:?}, labels={:?}", row.value("n"), row.value("lbls"));
}
let all_edges2 = db2
.session()
.query("MATCH ()-[r]->() RETURN r, type(r) as t")
.await?;
eprintln!("All edges in db2 ({}): ", all_edges2.len());
for row in all_edges2.rows() {
eprintln!(" r={:?}, type={:?}", row.value("r"), row.value("t"));
}
let with_edges2 = db2
.session()
.query("MATCH (n:Person)-->() RETURN n.name")
.await?;
eprintln!(
"Person nodes with outgoing edges in db2 ({}): ",
with_edges2.len()
);
for row in with_edges2.rows() {
eprintln!(" n.name = {:?}", row.value("n.name"));
}
let result = db2
.session()
.query("MATCH (n:Person)-->() WHERE n.name = 'Bob' RETURN n")
.await?;
eprintln!("After WHERE n.name = 'Bob': {} rows", result.len());
for row in result.rows() {
eprintln!(" n = {:?}", row.value("n"));
}
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_schemaless_edge_basic() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:Test {name: 'foo'})").await?;
tx.commit().await?;
let nodes = db.session().query("MATCH (n) RETURN n").await?;
eprintln!("After CREATE (:Test): {} nodes", nodes.len());
let tx2 = session.tx().await?;
let create_result = tx2.execute("CREATE (:A)-[:REL]->(:B)").await;
if create_result.is_ok() {
tx2.commit().await?;
}
match &create_result {
Ok(_) => eprintln!("CREATE (:A)-[:REL]->(:B) succeeded"),
Err(e) => eprintln!("CREATE (:A)-[:REL]->(:B) FAILED: {:?}", e),
}
create_result?;
let nodes2 = db
.session()
.query("MATCH (n) RETURN n, labels(n) as l")
.await?;
eprintln!("After CREATE (:A)-[:REL]->(:B): {} nodes", nodes2.len());
for row in nodes2.rows() {
eprintln!(" node: {:?}, labels: {:?}", row.value("n"), row.value("l"));
}
let edges = db
.session()
.query("MATCH ()-[r]->() RETURN r, type(r) as t")
.await?;
eprintln!("Edges: {} rows", edges.len());
for row in edges.rows() {
eprintln!(" edge: {:?}, type: {:?}", row.value("r"), row.value("t"));
}
assert!(!edges.is_empty(), "Expected at least 1 edge, got 0");
Ok(())
}
#[tokio::test]
async fn test_where_type_predicate() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("A")
.property("name", DataType::String)
.label("B")
.property("name", DataType::String)
.label("C")
.property("name", DataType::String)
.edge_type("KNOWS", &["A"], &["B"])
.edge_type("HATES", &["A"], &["C"])
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (a:A {name: 'A'}), (b:B {name: 'B'}), (c:C {name: 'C'})")
.await?;
tx.execute("MATCH (a:A), (b:B) CREATE (a)-[:KNOWS]->(b)")
.await?;
tx.execute("MATCH (a:A), (c:C) CREATE (a)-[:HATES]->(c)")
.await?;
tx.commit().await?;
let all_edges = db
.session()
.query("MATCH (n {name: 'A'})-[r]->(x) RETURN type(r) as t, x.name")
.await?;
eprintln!("All edges from A ({}): ", all_edges.len());
for row in all_edges.rows() {
eprintln!(
" type={:?}, target={:?}",
row.value("t"),
row.value("x.name")
);
}
let result = db
.session()
.query("MATCH (n {name: 'A'})-[r]->(x) WHERE type(r) = 'KNOWS' RETURN x")
.await?;
eprintln!("After WHERE type(r) = 'KNOWS': {} rows", result.len());
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_where_edge_property_predicate() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("A")
.label("X") .label("B")
.edge_type("KNOWS", &["X"], &["A", "B"])
.property("name", DataType::String)
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:X)-[:KNOWS {name: 'monkey'}]->(:A)")
.await?;
tx.execute("MATCH (x:X) CREATE (x)-[:KNOWS {name: 'woot'}]->(:B)")
.await?;
tx.commit().await?;
let all_edges = db
.session()
.query("MATCH (node)-[r:KNOWS]->(a) RETURN r.name, labels(a) as lbls")
.await?;
eprintln!("All KNOWS edges ({}): ", all_edges.len());
for row in all_edges.rows() {
eprintln!(
" r.name={:?}, target labels={:?}",
row.value("r.name"),
row.value("lbls")
);
}
let result = db
.session()
.query("MATCH (node)-[r:KNOWS]->(a) WHERE r.name = 'monkey' RETURN a")
.await?;
eprintln!("After WHERE r.name = 'monkey': {} rows", result.len());
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_matchwhere5_filter_on_null() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Root")
.property("name", DataType::String)
.label("TextNode")
.property("var", DataType::String)
.label("IntNode")
.property("var", DataType::Int64)
.edge_type("T", &["Root"], &["TextNode", "IntNode"])
.apply()
.await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (r:Root {name: 'x'})").await?;
tx.execute("CREATE (t:TextNode {var: 'text'})").await?;
tx.execute("CREATE (i:IntNode {var: 0})").await?;
tx.execute("MATCH (r:Root), (t:TextNode) CREATE (r)-[:T]->(t)")
.await?;
tx.execute("MATCH (r:Root), (i:IntNode) CREATE (r)-[:T]->(i)")
.await?;
tx.commit().await?;
let all_nodes = db
.session()
.query("MATCH (n) RETURN labels(n) as lbls, n")
.await?;
eprintln!("All nodes ({}):", all_nodes.len());
for row in all_nodes.rows() {
eprintln!(" labels={:?}, n={:?}", row.value("lbls"), row.value("n"));
}
let all_edges = db
.session()
.query("MATCH (:Root {name: 'x'})-->(n) RETURN labels(n) as lbls, n")
.await?;
eprintln!("All edges from Root ({}):", all_edges.len());
for row in all_edges.rows() {
eprintln!(" labels={:?}, n={:?}", row.value("lbls"), row.value("n"));
}
let text_edges = db
.session()
.query("MATCH (:Root {name: 'x'})-->(i:TextNode) RETURN i")
.await?;
eprintln!("TextNode edges from Root ({}):", text_edges.len());
for row in text_edges.rows() {
eprintln!(" i={:?}", row.value("i"));
}
let result = db
.session()
.query("MATCH (:Root {name: 'x'})-->(i:TextNode) WHERE i.var > 'te' RETURN i")
.await?;
eprintln!("After WHERE i.var > 'te' ({}):", result.len());
for row in result.rows() {
eprintln!(" i={:?}", row.value("i"));
}
assert_eq!(result.len(), 1, "Expected 1 TextNode, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_labels_function_schemaless() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (a:MyLabel {name: 'test'})").await?;
tx.commit().await?;
let result = db
.session()
.query("MATCH (n:MyLabel) RETURN n, labels(n) as lbls")
.await?;
eprintln!("labels() result ({}):", result.len());
for row in result.rows() {
eprintln!(" n={:?}", row.value("n"));
eprintln!(" labels={:?}", row.value("lbls"));
}
assert_eq!(result.len(), 1, "Should find 1 node");
let labels: Vec<String> = result.rows()[0].get("lbls")?;
assert!(
labels.contains(&"MyLabel".to_string()),
"Node should have label MyLabel"
);
Ok(())
}
#[tokio::test]
async fn test_labels_after_traverse_schemaless() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:Root {name: 'x'})-[:CONNECTS]->(:Target {val: 1})")
.await?;
tx.commit().await?;
let source = db
.session()
.query("MATCH (n:Root) RETURN n, labels(n) as lbls")
.await?;
eprintln!("Source node ({}):", source.len());
for row in source.rows() {
eprintln!(" n={:?}, labels={:?}", row.value("n"), row.value("lbls"));
}
let target = db
.session()
.query("MATCH (:Root)-->(t) RETURN t, labels(t) as lbls")
.await?;
eprintln!("Target node after traverse ({}):", target.len());
for row in target.rows() {
eprintln!(" t={:?}, labels={:?}", row.value("t"), row.value("lbls"));
}
assert_eq!(target.len(), 1, "Should find 1 target node");
let target_labels: Vec<String> = target.rows()[0].get("lbls")?;
eprintln!("Target labels: {:?}", target_labels);
assert!(
target_labels.contains(&"Target".to_string()),
"Target node should have label Target, got: {:?}",
target_labels
);
Ok(())
}
#[tokio::test]
async fn test_matchwhere5_schemaless() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (root:Root {name: 'x'}), (child1:TextNode {var: 'text'}), (child2:IntNode {var: 0})").await?;
tx.commit().await?;
let all_nodes = db
.session()
.query("MATCH (n) RETURN labels(n) as lbls, n")
.await?;
eprintln!("After CREATE nodes - All nodes ({}):", all_nodes.len());
for row in all_nodes.rows() {
eprintln!(" labels={:?}, n={:?}", row.value("lbls"), row.value("n"));
}
let tx2 = session.tx().await?;
tx2.execute("MATCH (root:Root {name: 'x'}), (child1:TextNode {var: 'text'}) CREATE (root)-[:T]->(child1)").await?;
tx2.execute(
"MATCH (root:Root {name: 'x'}), (child2:IntNode {var: 0}) CREATE (root)-[:T]->(child2)",
)
.await?;
tx2.commit().await?;
let all_edges = db
.session()
.query("MATCH (:Root {name: 'x'})-->(n) RETURN labels(n) as lbls, n.var")
.await?;
eprintln!("All edges from Root ({}):", all_edges.len());
for row in all_edges.rows() {
eprintln!(
" labels={:?}, n.var={:?}",
row.value("lbls"),
row.value("n.var")
);
}
let text_edges = db
.session()
.query("MATCH (:Root {name: 'x'})-->(i:TextNode) RETURN i.var")
.await?;
eprintln!("TextNode edges from Root ({}):", text_edges.len());
for row in text_edges.rows() {
eprintln!(" i.var={:?}", row.value("i.var"));
}
let result = db
.session()
.query("MATCH (:Root {name: 'x'})-->(i:TextNode) WHERE i.var > 'te' RETURN i")
.await?;
eprintln!("After WHERE i.var > 'te' ({}):", result.len());
for row in result.rows() {
eprintln!(" i={:?}", row.value("i"));
}
assert_eq!(result.len(), 1, "Expected 1 TextNode, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_optional_match_where_false_label() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (s:Single), (a:A {num: 42}), (b:B {num: 46}), (c:C)")
.await?;
tx.execute("MATCH (s:Single), (a:A {num: 42}) CREATE (s)-[:REL]->(a)")
.await?;
tx.execute("MATCH (s:Single), (b:B {num: 46}) CREATE (s)-[:REL]->(b)")
.await?;
tx.execute("MATCH (a:A {num: 42}), (c:C) CREATE (a)-[:REL]->(c)")
.await?;
tx.execute("MATCH (b:B {num: 46}) CREATE (b)-[:LOOP]->(b)")
.await?;
tx.commit().await?;
let all_rels = db
.session()
.query("MATCH (n:Single)-[r]-(m) RETURN labels(m) as lbls, r")
.await?;
eprintln!("Single's relationships ({}):", all_rels.len());
for row in all_rels.rows() {
eprintln!(" m labels={:?}, r={:?}", row.value("lbls"), row.value("r"));
}
let result = db
.session()
.query("MATCH (n:Single) OPTIONAL MATCH (n)-[r]-(m) WHERE m:NonExistent RETURN r")
.await?;
eprintln!("OPTIONAL MATCH result ({} rows):", result.len());
for row in result.rows() {
eprintln!(" r={:?}", row.value("r"));
}
assert_eq!(result.len(), 1, "Expected 1 row, got {}", result.len());
let r = result.rows()[0].value("r");
eprintln!("r value: {:?}", r);
assert!(
r.is_none() || matches!(r, Some(uni_db::Value::Null)),
"Expected r=null"
);
Ok(())
}
#[tokio::test]
async fn test_optional_match_non_equality_join() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:X {val: 1})-[:E1]->(:Y {val: 2})-[:E2]->(:Z {val: 3})")
.await?;
tx.execute("CREATE (:X {val: 4})-[:E1]->(:Y {val: 5})")
.await?;
tx.execute("CREATE (:X {val: 6})").await?;
tx.commit().await?;
let x_nodes = db
.session()
.query("MATCH (x:X) RETURN x.val ORDER BY x.val")
.await?;
eprintln!("X nodes ({}):", x_nodes.len());
for row in x_nodes.rows() {
eprintln!(" x.val={:?}", row.value("x.val"));
}
assert_eq!(x_nodes.len(), 3, "Should have 3 X nodes");
let e1_edges = db
.session()
.query("MATCH (x:X)-[:E1]->(y:Y) RETURN x.val, y.val ORDER BY x.val")
.await?;
eprintln!("E1 edges ({}):", e1_edges.len());
for row in e1_edges.rows() {
eprintln!(
" x.val={:?} -> y.val={:?}",
row.value("x.val"),
row.value("y.val")
);
}
let opt_no_where = db
.session()
.query("MATCH (x:X) OPTIONAL MATCH (x)-[:E1]->(y:Y) RETURN x.val, y.val ORDER BY x.val")
.await?;
eprintln!("OPTIONAL MATCH without WHERE ({}):", opt_no_where.len());
for row in opt_no_where.rows() {
eprintln!(
" x.val={:?}, y.val={:?}",
row.value("x.val"),
row.value("y.val")
);
}
assert_eq!(
opt_no_where.len(),
3,
"OPTIONAL MATCH should return 3 rows (one per X)"
);
let result = db.session().query("MATCH (x:X) OPTIONAL MATCH (x)-[:E1]->(y:Y) WHERE x.val < y.val RETURN x.val, y.val ORDER BY x.val").await?;
eprintln!("OPTIONAL MATCH with WHERE ({}):", result.len());
for row in result.rows() {
eprintln!(
" x.val={:?}, y.val={:?}",
row.value("x.val"),
row.value("y.val")
);
}
assert_eq!(result.len(), 3, "Expected 3 rows, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_optional_match_multi_hop() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:X {val: 1})-[:E1]->(:Y {val: 2})-[:E2]->(:Z {val: 3})")
.await?;
tx.execute("CREATE (:X {val: 4})-[:E1]->(:Y {val: 5})")
.await?;
tx.execute("CREATE (:X {val: 6})").await?;
tx.commit().await?;
let x_nodes = db
.session()
.query("MATCH (x:X) RETURN x.val ORDER BY x.val")
.await?;
eprintln!("X nodes ({}):", x_nodes.len());
for row in x_nodes.rows() {
eprintln!(" x.val={:?}", row.value("x.val"));
}
let e1_edges = db
.session()
.query("MATCH (x:X)-[:E1]->(y:Y) RETURN x.val, y.val ORDER BY x.val")
.await?;
eprintln!("E1 edges ({}):", e1_edges.len());
for row in e1_edges.rows() {
eprintln!(
" x.val={:?} -> y.val={:?}",
row.value("x.val"),
row.value("y.val")
);
}
let e2_edges = db
.session()
.query("MATCH (y:Y)-[:E2]->(z:Z) RETURN y.val, z.val")
.await?;
eprintln!("E2 edges ({}):", e2_edges.len());
for row in e2_edges.rows() {
eprintln!(
" y.val={:?} -> z.val={:?}",
row.value("y.val"),
row.value("z.val")
);
}
let result = db.session().query("MATCH (x:X) OPTIONAL MATCH (x)-[:E1]->(y:Y)-[:E2]->(z:Z) WHERE x.val < z.val RETURN x.val, y.val, z.val ORDER BY x.val").await?;
eprintln!("Multi-hop OPTIONAL MATCH result ({}):", result.len());
for row in result.rows() {
eprintln!(
" x.val={:?}, y.val={:?}, z.val={:?}",
row.value("x.val"),
row.value("y.val"),
row.value("z.val")
);
}
assert_eq!(result.len(), 3, "Expected 3 rows, got {}", result.len());
let row_4 = result
.rows()
.iter()
.find(|r| matches!(r.value("x.val"), Some(uni_db::Value::Int(4))))
.expect("Should have row for x.val=4");
let y_val = row_4.value("y.val");
eprintln!("X(val:4) y.val = {:?}", y_val);
assert!(
y_val.is_none() || matches!(y_val, Some(uni_db::Value::Null)),
"For X(val:4), y should be NULL because full pattern (x)-[:E1]->(y)-[:E2]->(z) failed, got {:?}",
y_val
);
Ok(())
}
#[tokio::test]
async fn test_where_parameter_filter_return_edge() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:A)-[:T {name: 'bar'}]->(:B {name: 'me'})")
.await?;
tx.commit().await?;
let edges = db
.session()
.query("MATCH (a)-[r]->(b) RETURN a, r, b, r.name as r_name, b.name as b_name")
.await?;
eprintln!("All edges ({}):", edges.len());
for row in edges.rows() {
eprintln!(
" a={:?}, r={:?}, b={:?}, r.name={:?}, b.name={:?}",
row.value("a"),
row.value("r"),
row.value("b"),
row.value("r_name"),
row.value("b_name")
);
}
let result_hardcoded = db
.session()
.query("MATCH (a)-[r]->(b) WHERE b.name = 'me' RETURN r")
.await?;
eprintln!("Hardcoded filter result ({}):", result_hardcoded.len());
for row in result_hardcoded.rows() {
eprintln!(" r={:?}", row.value("r"));
}
let result = db
.session()
.query_with("MATCH (a)-[r]->(b) WHERE b.name = $param RETURN r")
.param("param", "me")
.fetch_all()
.await?;
eprintln!("Parameter filter result ({}):", result.len());
for row in result.rows() {
eprintln!(" r={:?}", row.value("r"));
}
assert_eq!(result.len(), 1, "Expected 1 result, got {}", result.len());
Ok(())
}
#[tokio::test]
async fn test_optional_match_reused_relationship() -> Result<()> {
let db = Uni::in_memory().build().await?;
let session = db.session();
let tx = session.tx().await?;
tx.execute("CREATE (:A)-[:T]->(:B)").await?;
tx.commit().await?;
let result = db
.session()
.query(
"
MATCH (a1)-[r]->()
WITH r, a1 LIMIT 1
OPTIONAL MATCH (a2)<-[r]-(b2)
WHERE a1 = a2
RETURN a1, r, b2, a2
",
)
.await?;
assert_eq!(result.len(), 1, "Expected 1 row, got {}", result.len());
let row = &result.rows()[0];
let a2_val = row.value("a2");
assert!(
a2_val.is_none() || matches!(a2_val, Some(uni_db::Value::Null)),
"a2 should be NULL because WHERE a1=a2 failed (A != B), got {:?}",
a2_val
);
Ok(())
}