use taladb::index::{encode_compound_keys, encode_index_keys};
use taladb::{Database, Filter, Ulid, document::Value};
fn array_of(n: usize) -> Value {
Value::Array((0..n).map(|i| Value::Int(i as i64)).collect())
}
fn encode_cost(n: usize, compound: bool) -> std::time::Duration {
let id = Ulid::new();
let arr = array_of(n);
let tenant = Value::Str("acme".into());
let start = std::time::Instant::now();
for _ in 0..3 {
if compound {
let values: Vec<&Value> = vec![&tenant, &arr];
let keys = encode_compound_keys(&values, id).expect("encode");
assert_eq!(keys.len(), n);
} else {
let keys = encode_index_keys(&arr, id);
assert_eq!(keys.len(), n);
}
}
start.elapsed()
}
fn assert_linear(compound: bool, label: &str) {
let _ = encode_cost(1_000, compound);
let small = encode_cost(2_000, compound);
let large = encode_cost(16_000, compound);
let ratio = large.as_secs_f64() / small.as_secs_f64().max(1e-9);
println!("{label}: 2k={small:?} 16k={large:?} ratio={ratio:.1}x (linear≈8x, quadratic≈64x)");
assert!(
ratio < 20.0,
"{label}: 8x the elements cost {ratio:.1}x the time — key dedup has gone \
quadratic in the array length again (2k={small:?}, 16k={large:?})"
);
}
#[test]
fn single_field_index_keys_scale_linearly() {
assert_linear(false, "single-field");
}
#[test]
fn compound_index_keys_scale_linearly() {
assert_linear(true, "compound");
}
#[test]
fn duplicate_elements_still_collapse_to_one_key() {
let id = Ulid::new();
let dupes = Value::Array(vec![
Value::Str("a".into()),
Value::Str("a".into()),
Value::Str("b".into()),
Value::Str("a".into()),
]);
assert_eq!(
encode_index_keys(&dupes, id).len(),
2,
"['a','a','b','a'] must yield exactly the keys for 'a' and 'b'"
);
let tenant = Value::Str("acme".into());
let values: Vec<&Value> = vec![&tenant, &dupes];
assert_eq!(
encode_compound_keys(&values, id).expect("encode").len(),
2,
"compound keys must deduplicate on the array member too"
);
}
#[test]
fn key_order_follows_element_order() {
let id = Ulid::new();
let ascending = encode_index_keys(&array_of(64), id);
let mut sorted = ascending.clone();
sorted.sort();
assert_eq!(
ascending, sorted,
"keys for an ascending Int array must come back in ascending byte order"
);
}
#[test]
fn a_repeated_element_matches_its_document_once() {
let db = Database::open_in_memory().expect("open");
let col = db.collection("items").expect("collection");
col.create_index("tags").expect("index");
col.insert(vec![(
"tags".to_string(),
Value::Array(vec![
Value::Str("a".into()),
Value::Str("a".into()),
Value::Str("b".into()),
]),
)])
.expect("insert");
let needle = Filter::Eq("tags".into(), Value::Str("a".into()));
assert_eq!(col.find(needle.clone()).expect("find").len(), 1);
assert_eq!(
col.count(needle).expect("count"),
1,
"count reads index entries directly, so it must agree with find"
);
}