use mentat::{Binding, QueryExplanation, QueryResults, Queryable, Store, TypedValue};
fn store() -> Store {
let mut s = Store::open("").expect("open");
s.transact(
r#"[{:db/ident :m/name :db/valueType :db.type/string :db/cardinality :db.cardinality/one}
{:db/ident :m/heads :db/valueType :db.type/long :db/cardinality :db.cardinality/one}
{:db/ident :m/weapon :db/valueType :db.type/string :db/cardinality :db.cardinality/many}]"#,
)
.unwrap();
s.transact(
r#"[{:m/name "Medusa" :m/heads 1 :m/weapon "Stony gaze"}
{:m/name "Cyclops" :m/heads 1 :m/weapon ["Large club" "Mighty arms" "Stompy feet"]}
{:m/name "Chimera" :m/heads 1 :m/weapon "Stony gaze"}
{:m/name "Cerberus" :m/heads 3 :m/weapon ["Large club" "Deadly drool"]}]"#,
)
.unwrap();
s
}
fn sql(s: &Store, q: &str) -> String {
match s.q_explain(q, None).unwrap() {
QueryExplanation::ExecutionPlan { query, .. } => query.sql,
_ => panic!("expected a plan"),
}
}
fn rows(s: &Store, q: &str) -> Vec<Vec<TypedValue>> {
match s.q_once(q, None).unwrap().results {
QueryResults::Rel(r) => r
.into_iter()
.map(|row| row.into_iter().map(|b| b.into_scalar().unwrap()).collect())
.collect(),
QueryResults::Scalar(Some(Binding::Scalar(v))) => vec![vec![v]],
r => panic!("{r:?}"),
}
}
fn long(i: i64) -> TypedValue {
TypedValue::Long(i)
}
#[test]
fn test_count_key_is_plain_count() {
let s = store();
let q = "[:find ?h (count ?m) :order ?h :where [?m :m/heads ?h]]";
assert!(!sql(&s, q).contains("DISTINCT"), "{}", sql(&s, q));
assert_eq!(
rows(&s, q),
vec![vec![long(1), long(3)], vec![long(3), long(1)]]
);
let q = "[:find (count ?m) . :where [?m :m/name _]]";
assert!(!sql(&s, q).contains("DISTINCT"), "{}", sql(&s, q));
assert_eq!(rows(&s, q), vec![vec![long(4)]]);
let q = "[:find ?h (count ?m) :order ?h :where [?m :m/heads ?h] [?m :m/name _]]";
assert!(!sql(&s, q).contains("DISTINCT"), "{}", sql(&s, q));
assert_eq!(
rows(&s, q),
vec![vec![long(1), long(3)], vec![long(3), long(1)]]
);
}
#[test]
fn test_count_over_counting_cases_stay_set_counts() {
let s = store();
let q = "[:find (count ?w) . :where [_ :m/weapon ?w]]";
assert!(sql(&s, q).contains("count(DISTINCT"), "{}", sql(&s, q));
assert_eq!(rows(&s, q), vec![vec![long(5)]]);
let q = "[:find ?h (count ?m) :order ?h :where [?m :m/heads ?h] [?m :m/weapon _]]";
assert!(sql(&s, q).contains("count(DISTINCT"), "{}", sql(&s, q));
assert_eq!(
rows(&s, q),
vec![vec![long(1), long(3)], vec![long(3), long(1)]]
);
let q = "[:find (count ?h) . :where [_ :m/heads ?h]]";
assert_eq!(rows(&s, q), vec![vec![long(2)]]);
let q = "[:find (count ?h) . :with ?m :where [?m :m/heads ?h]]";
assert_eq!(rows(&s, q), vec![vec![long(4)]]);
let q = "[:find ?n (count ?w) :with ?m :order ?n :where [?m :m/name ?n] [?m :m/weapon ?w]]";
assert_eq!(
rows(&s, q),
vec![
vec!["Cerberus".into(), long(2)],
vec!["Chimera".into(), long(1)],
vec!["Cyclops".into(), long(3)],
vec!["Medusa".into(), long(1)],
]
);
}