use graphdblite::{Database, Value};
#[test]
fn composite_index_full_lifecycle() {
let dir = tempfile::tempdir().unwrap();
let mut db = Database::open(dir.path().join("composite.db")).unwrap();
db.execute("CREATE INDEX ON :Person(tenant_id, ext_id)")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 1, ext_id: 'a', name: 'Alice'})")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 1, ext_id: 'b', name: 'Bob'})")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 2, ext_id: 'a', name: 'Carol'})")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 9, name: 'Partial'})")
.unwrap();
let rows = db
.execute("MATCH (p:Person {tenant_id: 1, ext_id: 'a'}) RETURN p.name AS n")
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].get("n"), Some(&Value::String("Alice".into())));
let rows = db
.execute("MATCH (p:Person {tenant_id: 1}) RETURN count(p) AS n")
.unwrap();
assert_eq!(rows[0].get("n"), Some(&Value::I64(2)));
db.execute("MATCH (p:Person {tenant_id: 2, ext_id: 'a'}) SET p.tenant_id = 99")
.unwrap();
let rows = db
.execute("MATCH (p:Person {tenant_id: 99, ext_id: 'a'}) RETURN p.name AS n")
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].get("n"), Some(&Value::String("Carol".into())));
let rows = db
.execute("MATCH (p:Person {tenant_id: 2}) RETURN count(p) AS n")
.unwrap();
assert_eq!(rows[0].get("n"), Some(&Value::I64(0)));
db.execute("DROP INDEX ON :Person(tenant_id, ext_id)")
.unwrap();
db.execute("CREATE INDEX ON :Person(tenant_id, ext_id)")
.unwrap();
let rows = db
.execute("CALL db.indexes() YIELD label, property, kind RETURN label, property, kind")
.unwrap();
assert_eq!(rows.len(), 2);
for row in &rows {
assert_eq!(row.get("label"), Some(&Value::String("Person".into())));
assert_eq!(row.get("kind"), Some(&Value::String("btree".into())));
}
let rows = db
.execute("MATCH (p:Person {tenant_id: 1}) RETURN count(p) AS n")
.unwrap();
assert_eq!(rows[0].get("n"), Some(&Value::I64(2)));
let rows = db
.execute("MATCH (p:Person {tenant_id: 99, ext_id: 'a'}) RETURN p.name AS n")
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].get("n"), Some(&Value::String("Carol".into())));
}
#[test]
fn composite_index_picked_for_where_clause_equality() {
let dir = tempfile::tempdir().unwrap();
let mut db = Database::open(dir.path().join("composite_where.db")).unwrap();
db.execute("CREATE INDEX ON :Person(tenant_id, ext_id)")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 1, ext_id: 'a', name: 'Alice'})")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 1, ext_id: 'b', name: 'Bob'})")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 2, ext_id: 'a', name: 'Carol'})")
.unwrap();
let rows = db
.execute("MATCH (p:Person) WHERE p.tenant_id = 1 AND p.ext_id = 'a' RETURN p.name AS n")
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].get("n"), Some(&Value::String("Alice".into())));
let rows = db
.execute("MATCH (p:Person) WHERE p.tenant_id = 1 RETURN count(p) AS n")
.unwrap();
assert_eq!(rows[0].get("n"), Some(&Value::I64(2)));
let rows = db
.execute(
"MATCH (p:Person) \
WHERE p.tenant_id = 1 AND p.ext_id <> 'a' \
RETURN p.name AS n",
)
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].get("n"), Some(&Value::String("Bob".into())));
}
#[test]
fn composite_index_cleared_on_cypher_delete() {
let dir = tempfile::tempdir().unwrap();
let mut db = Database::open(dir.path().join("composite_delete.db")).unwrap();
db.execute("CREATE INDEX ON :Person(tenant_id, ext_id)")
.unwrap();
db.execute("CREATE (:Person {tenant_id: 1, ext_id: 'a'})")
.unwrap();
db.execute("MATCH (p:Person {tenant_id: 1, ext_id: 'a'}) DELETE p")
.unwrap();
let rows = db
.execute("MATCH (p:Person {tenant_id: 1, ext_id: 'a'}) RETURN count(p) AS n")
.unwrap();
assert_eq!(rows[0].get("n"), Some(&Value::I64(0)));
}