use taladb::Database;
use taladb::document::Value;
use taladb::vector::VectorMetric;
fn vec_field(v: &[f32]) -> Value {
Value::Array(v.iter().map(|f| Value::Float(f64::from(*f))).collect())
}
fn indexed_collection(db: &Database) -> taladb::Collection {
let col = db.collection("docs").unwrap();
col.create_vector_index("embedding", 4, Some(VectorMetric::Cosine), None)
.unwrap();
col
}
#[test]
fn insert_with_a_wrong_dimension_vector_is_rejected() {
let db = Database::open_in_memory().unwrap();
let col = indexed_collection(&db);
let err = col
.insert(vec![("embedding".into(), vec_field(&[1.0, 0.0, 0.0]))])
.unwrap_err();
assert!(
matches!(
err,
taladb::TalaDbError::VectorDimensionMismatch {
expected: 4,
got: 3
}
),
"expected a dimension mismatch, got {err:?}"
);
assert_eq!(
col.count(taladb::Filter::All).unwrap(),
0,
"the rejected write must not leave the document behind"
);
}
#[test]
fn insert_many_rejects_the_whole_batch() {
let db = Database::open_in_memory().unwrap();
let col = indexed_collection(&db);
let err = col.insert_many(vec![
vec![("embedding".into(), vec_field(&[1.0, 0.0, 0.0, 0.0]))],
vec![("embedding".into(), vec_field(&[0.0, 1.0]))],
]);
assert!(err.is_err(), "a bad vector anywhere fails the batch");
assert_eq!(
col.count(taladb::Filter::All).unwrap(),
0,
"the transaction rolls back — no partial batch is committed"
);
}
#[test]
fn update_to_a_wrong_dimension_vector_is_rejected_and_leaves_the_old_one() {
let db = Database::open_in_memory().unwrap();
let col = indexed_collection(&db);
col.insert(vec![
("embedding".into(), vec_field(&[1.0, 0.0, 0.0, 0.0])),
("k".into(), Value::Str("a".into())),
])
.unwrap();
let err = col.update_one(
taladb::Filter::Eq("k".into(), Value::Str("a".into())),
taladb::Update::Set(vec![("embedding".into(), vec_field(&[1.0, 2.0]))]),
);
assert!(
err.is_err(),
"a shrinking update is a dimension mismatch too"
);
let hits = col
.find_nearest("embedding", &[1.0, 0.0, 0.0, 0.0], 10, None)
.unwrap();
assert_eq!(hits.len(), 1, "the original document is still indexed");
assert_eq!(
hits[0].document.get("embedding"),
Some(&vec_field(&[1.0, 0.0, 0.0, 0.0])),
"the stored vector is unchanged"
);
}
#[test]
fn a_missing_or_null_vector_field_is_still_allowed() {
let db = Database::open_in_memory().unwrap();
let col = indexed_collection(&db);
col.insert(vec![("k".into(), Value::Str("no-embedding".into()))])
.unwrap();
col.insert(vec![
("k".into(), Value::Str("not-embedded-yet".into())),
("embedding".into(), Value::Null),
])
.unwrap();
assert_eq!(
col.count(taladb::Filter::All).unwrap(),
2,
"documents awaiting an embedding are ordinary documents"
);
assert!(
col.find_nearest("embedding", &[1.0, 0.0, 0.0, 0.0], 10, None)
.unwrap()
.is_empty(),
"neither is in the vector index"
);
}
#[test]
fn a_matching_vector_still_writes_and_searches() {
let db = Database::open_in_memory().unwrap();
let col = indexed_collection(&db);
col.insert(vec![("embedding".into(), vec_field(&[1.0, 0.0, 0.0, 0.0]))])
.unwrap();
col.insert(vec![("embedding".into(), vec_field(&[0.0, 1.0, 0.0, 0.0]))])
.unwrap();
let hits = col
.find_nearest("embedding", &[1.0, 0.0, 0.0, 0.0], 1, None)
.unwrap();
assert_eq!(hits.len(), 1);
assert!(hits[0].score > 0.99, "the identical vector ranks first");
}
#[test]
fn top_k_of_zero_returns_nothing() {
let db = Database::open_in_memory().unwrap();
let col = indexed_collection(&db);
for i in 0..5 {
col.insert(vec![(
"embedding".into(),
vec_field(&[i as f32, 1.0, 0.0, 0.0]),
)])
.unwrap();
}
let hits = col
.find_nearest("embedding", &[1.0, 1.0, 0.0, 0.0], 0, None)
.unwrap();
assert!(hits.is_empty(), "got {} results for top_k = 0", hits.len());
}