#![cfg(all(test, feature = "persistence"))]
use serde_json::json;
use std::collections::HashMap;
use tempfile::TempDir;
use velesdb_core::{DistanceMetric, Point, StorageMode, VectorCollection};
fn mk(dir: &TempDir) -> VectorCollection {
VectorCollection::create(
dir.path().join("docs"),
"docs",
2,
DistanceMetric::Cosine,
StorageMode::Full,
)
.expect("create collection")
}
fn ids(rs: &[velesdb_core::point::SearchResult]) -> Vec<u64> {
rs.iter().map(|r| r.point.id).collect()
}
fn run_both(points: &[Point], lead: &str, sql: &str) -> (Vec<u64>, Vec<u64>) {
let da = TempDir::new().expect("dir");
let a = mk(&da);
a.upsert(points.to_vec()).expect("upsert");
let exhaustive = ids(&a
.execute_query_str(sql, &HashMap::new())
.expect("exhaustive"));
let db = TempDir::new().expect("dir");
let b = mk(&db);
b.upsert(points.to_vec()).expect("upsert");
b.create_index(lead).expect("create_index");
let indexed = ids(&b.execute_query_str(sql, &HashMap::new()).expect("index"));
(exhaustive, indexed)
}
fn cat_year_rows() -> Vec<Point> {
vec![
Point::new(1, vec![1.0, 0.0], Some(json!({ "cat": "a", "year": 2020 }))),
Point::new(2, vec![1.0, 0.0], Some(json!({ "cat": "b", "year": 2022 }))),
Point::new(3, vec![1.0, 0.0], Some(json!({ "cat": "a", "year": 2021 }))),
Point::new(4, vec![1.0, 0.0], Some(json!({ "cat": "b", "year": 2021 }))),
Point::new(5, vec![1.0, 0.0], Some(json!({ "cat": "a", "year": 2021 }))),
]
}
#[test]
fn lead_asc_secondary_desc_full_matches_exhaustive() {
let (exhaustive, indexed) = run_both(
&cat_year_rows(),
"cat",
"SELECT * FROM docs ORDER BY cat ASC, year DESC LIMIT 5",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![3, 5, 1, 2, 4]);
}
#[test]
fn lead_asc_prunes_trailing_bucket_for_topk() {
let (exhaustive, indexed) = run_both(
&cat_year_rows(),
"cat",
"SELECT * FROM docs ORDER BY cat ASC, year DESC LIMIT 2",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![3, 5]);
}
#[test]
fn lead_desc_secondary_asc_matches_exhaustive() {
let (exhaustive, indexed) = run_both(
&cat_year_rows(),
"cat",
"SELECT * FROM docs ORDER BY cat DESC, year ASC LIMIT 5",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![4, 2, 1, 3, 5]);
}
#[test]
fn offset_matches_exhaustive() {
let (exhaustive, indexed) = run_both(
&cat_year_rows(),
"cat",
"SELECT * FROM docs ORDER BY cat ASC, year DESC LIMIT 2 OFFSET 1",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![5, 1]);
}
#[test]
fn heterogeneous_lead_types_match_exhaustive() {
let points = vec![
Point::new(
1,
vec![1.0, 0.0],
Some(json!({ "cat": true, "year": 2020 })),
),
Point::new(2, vec![1.0, 0.0], Some(json!({ "cat": 5, "year": 2021 }))),
Point::new(3, vec![1.0, 0.0], Some(json!({ "cat": "x", "year": 2022 }))),
Point::new(
4,
vec![1.0, 0.0],
Some(json!({ "cat": true, "year": 2019 })),
),
Point::new(5, vec![1.0, 0.0], Some(json!({ "cat": 5, "year": 2023 }))),
];
let (exhaustive, indexed) = run_both(
&points,
"cat",
"SELECT * FROM docs ORDER BY cat ASC, year DESC LIMIT 5",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![1, 4, 5, 2, 3]);
}
#[test]
fn k_greater_than_n_matches_exhaustive() {
let (exhaustive, indexed) = run_both(
&cat_year_rows(),
"cat",
"SELECT * FROM docs ORDER BY cat ASC, year DESC LIMIT 100",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed.len(), 5);
}
#[test]
fn uncovered_lead_falls_back_and_matches_exhaustive() {
let mut points = cat_year_rows();
points.push(Point::new(9, vec![1.0, 0.0], Some(json!({ "year": 2099 }))));
let (exhaustive, indexed) = run_both(
&points,
"cat",
"SELECT * FROM docs ORDER BY cat ASC, year DESC LIMIT 6",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![9, 3, 5, 1, 2, 4]);
}
#[test]
fn multikey_with_where_declines_but_stays_correct() {
let (exhaustive, indexed) = run_both(
&cat_year_rows(),
"cat",
"SELECT * FROM docs WHERE year >= 2021 ORDER BY cat ASC, year DESC LIMIT 5",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![3, 5, 2, 4]);
}
#[test]
fn expired_row_in_prefix_falls_back_and_matches_exhaustive() {
let points = vec![
Point::new(1, vec![1.0, 0.0], Some(json!({ "cat": "a", "year": 2020 }))),
Point::new(2, vec![1.0, 0.0], Some(json!({ "cat": "b", "year": 2022 }))),
Point::new(
3,
vec![1.0, 0.0],
Some(json!({ "cat": "a", "year": 2021, "_veles_expires_at": 1 })),
),
Point::new(4, vec![1.0, 0.0], Some(json!({ "cat": "b", "year": 2021 }))),
Point::new(5, vec![1.0, 0.0], Some(json!({ "cat": "a", "year": 2019 }))),
];
let (exhaustive, indexed) = run_both(
&points,
"cat",
"SELECT * FROM docs ORDER BY cat ASC, year DESC LIMIT 4",
);
assert_eq!(exhaustive, indexed);
assert_eq!(indexed, vec![1, 5, 2, 4]);
}