#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::sync::Arc;
use quiver_embed::{Database, Descriptor, DistanceMetric, Dtype, SearchParams};
use serde_json::json;
fn seed(db: &mut Database) {
db.create_collection("c", Descriptor::new(4, Dtype::F32, DistanceMetric::Cosine))
.unwrap();
for i in 0..64u32 {
db.upsert(
"c",
&format!("p{i}"),
&[i as f32, 1.0, 0.0, 0.0],
&json!({ "i": i }),
)
.unwrap();
}
db.ensure_indexed("c").unwrap();
}
#[test]
fn snapshot_serves_prior_until_rebuilt() {
let tmp = tempfile::tempdir().unwrap();
let mut db = Database::open(tmp.path()).unwrap();
seed(&mut db);
let q = [3.0, 1.0, 0.0, 0.0];
let hits = db
.search_snapshot("c", &q, &SearchParams::default())
.unwrap();
assert!(!hits.is_empty());
assert!(!db.needs_rebuild("c").unwrap());
db.upsert("c", "p3", &[100.0, 1.0, 0.0, 0.0], &json!({ "i": 3 }))
.unwrap();
assert!(db.needs_rebuild("c").unwrap());
assert!(
!db.search_snapshot("c", &q, &SearchParams::default())
.unwrap()
.is_empty()
);
db.ensure_indexed("c").unwrap();
assert!(!db.needs_rebuild("c").unwrap());
assert!(
!db.search_snapshot("c", &q, &SearchParams::default())
.unwrap()
.is_empty()
);
db.upsert("c", "p4", &[200.0, 1.0, 0.0, 0.0], &json!({ "i": 4 }))
.unwrap();
assert!(
!db.search("c", &q, &SearchParams::default())
.unwrap()
.is_empty()
);
assert!(!db.needs_rebuild("c").unwrap());
db.upsert("c", "p5", &[300.0, 1.0, 0.0, 0.0], &json!({ "i": 5 }))
.unwrap();
assert!(
!db.hybrid_search("c", Some(&q), None, None, &SearchParams::default(), 60.0)
.unwrap()
.is_empty()
);
}
#[test]
fn many_readers_share_one_snapshot_concurrently() {
let tmp = tempfile::tempdir().unwrap();
let mut db = Database::open(tmp.path()).unwrap();
seed(&mut db);
let q = [3.0, 1.0, 0.0, 0.0];
let want = db
.search_snapshot("c", &q, &SearchParams::default())
.unwrap()[0]
.id
.clone();
let db = Arc::new(db);
let mut handles = Vec::new();
for _ in 0..8 {
let db = Arc::clone(&db);
let want = want.clone();
handles.push(std::thread::spawn(move || {
for _ in 0..250 {
let hits = db
.search_snapshot("c", &q, &SearchParams::default())
.unwrap();
assert_eq!(
hits[0].id, want,
"every concurrent reader sees the same result"
);
}
}));
}
for h in handles {
h.join().unwrap();
}
}