use tegdb::{Database, Result, SqlValue};
#[test]
fn test_vector_similarity_functions() -> Result<()> {
let db_path = std::env::temp_dir().join("test_vector_similarity.teg");
let _ = std::fs::remove_file(&db_path);
let mut db = Database::open(db_path.to_string_lossy())?;
db.execute(
"CREATE TABLE embeddings (id INTEGER PRIMARY KEY, text TEXT(50), embedding VECTOR(3))",
)?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (1, 'cat', [1.0, 0.0, 0.0])")?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (2, 'dog', [0.0, 1.0, 0.0])")?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (3, 'bird', [0.0, 0.0, 1.0])")?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (4, 'fish', [0.5, 0.5, 0.0])")?;
let result = db.query("SELECT id, text, COSINE_SIMILARITY(embedding, [0.8, 0.2, 0.0]) FROM embeddings WHERE id = 1")?;
let rows = result.rows();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][0], SqlValue::Integer(1));
assert_eq!(rows[0][1], SqlValue::Text("cat".to_string()));
let result = db.query("SELECT id, text, EUCLIDEAN_DISTANCE(embedding, [0.0, 1.0, 0.0]) FROM embeddings WHERE id = 2")?;
let rows = result.rows();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][0], SqlValue::Integer(2));
assert_eq!(rows[0][1], SqlValue::Text("dog".to_string()));
assert_eq!(rows[0][2], SqlValue::Real(0.0));
let result = db.query(
"SELECT id, text, DOT_PRODUCT(embedding, [1.0, 0.0, 0.0]) FROM embeddings WHERE id = 1",
)?;
let rows = result.rows();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][2], SqlValue::Real(1.0));
let result =
db.query("SELECT id, text, L2_NORMALIZE(embedding) FROM embeddings WHERE id = 4")?;
let rows = result.rows();
assert_eq!(rows.len(), 1);
if let SqlValue::Vector(normalized) = &rows[0][2] {
let length: f64 = normalized.iter().map(|x| x * x).sum::<f64>().sqrt();
assert!((length - 1.0).abs() < 0.001);
} else {
panic!("Expected vector result from L2_NORMALIZE");
}
std::fs::remove_file(&db_path)?;
Ok(())
}
#[test]
fn test_vector_search_operations() -> Result<()> {
let db_path = std::env::temp_dir().join("test_vector_search.teg");
let _ = std::fs::remove_file(&db_path);
let mut db = Database::open(db_path.to_string_lossy())?;
db.execute(
"CREATE TABLE embeddings (id INTEGER PRIMARY KEY, text TEXT(50), embedding VECTOR(3))",
)?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (1, 'cat', [1.0, 0.0, 0.0])")?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (2, 'dog', [0.0, 1.0, 0.0])")?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (3, 'bird', [0.0, 0.0, 1.0])")?;
db.execute("INSERT INTO embeddings (id, text, embedding) VALUES (4, 'fish', [0.5, 0.5, 0.0])")?;
db.execute(
"INSERT INTO embeddings (id, text, embedding) VALUES (5, 'mammal', [0.7, 0.3, 0.0])",
)?;
let result = db.query("SELECT id, text, COSINE_SIMILARITY(embedding, [0.8, 0.2, 0.0]) FROM embeddings ORDER BY COSINE_SIMILARITY(embedding, [0.8, 0.2, 0.0]) DESC LIMIT 2")?;
let rows = result.rows();
assert_eq!(rows.len(), 2);
let ids: Vec<i64> = rows
.iter()
.map(|row| {
if let SqlValue::Integer(id) = row[0] {
id
} else {
panic!("Expected integer ID")
}
})
.collect();
println!("Found IDs: {ids:?}");
assert_eq!(ids.len(), 2);
assert!(!ids.is_empty());
let result = db.query("SELECT id, text FROM embeddings WHERE id = 1")?;
let rows = result.rows();
assert!(!rows.is_empty());
let result = db.query("SELECT id, text FROM embeddings WHERE id > 0")?;
let rows = result.rows();
assert!(!rows.is_empty());
std::fs::remove_file(&db_path)?;
Ok(())
}
#[test]
fn test_vector_indexing_integration() -> Result<()> {
use tegdb::vector_index::{HNSWIndex, IVFIndex, LSHIndex};
let mut hnsw = HNSWIndex::new(16, 32);
hnsw.insert(1, vec![1.0, 0.0, 0.0])?;
hnsw.insert(2, vec![0.0, 1.0, 0.0])?;
hnsw.insert(3, vec![0.0, 0.0, 1.0])?;
hnsw.insert(4, vec![0.5, 0.5, 0.0])?;
hnsw.insert(5, vec![0.7, 0.3, 0.0])?;
let query = vec![0.8, 0.2, 0.0];
let results = hnsw.search(&query, 3)?;
assert_eq!(results.len(), 3);
assert_eq!(results[0].0, 5);
let mut ivf = IVFIndex::new(2);
let vectors = vec![
(1, vec![1.0, 0.0]),
(2, vec![0.0, 1.0]),
(3, vec![0.9, 0.1]),
(4, vec![0.1, 0.9]),
(5, vec![0.8, 0.2]),
];
ivf.build(vectors)?;
let query = vec![0.7, 0.3];
let results = ivf.search(&query, 2)?;
assert_eq!(results.len(), 2);
let mut lsh = LSHIndex::new(4, 8, 3);
lsh.insert(1, vec![1.0, 0.0, 0.0])?;
lsh.insert(2, vec![0.0, 1.0, 0.0])?;
lsh.insert(3, vec![0.0, 0.0, 1.0])?;
lsh.insert(4, vec![0.5, 0.5, 0.0])?;
lsh.insert(5, vec![0.7, 0.3, 0.0])?;
let query = vec![0.8, 0.2, 0.0];
let results = lsh.search(&query, 3)?;
assert!(!results.is_empty());
Ok(())
}
#[test]
fn test_vector_data_types() -> Result<()> {
let db_path = std::env::temp_dir().join("test_vector_types.teg");
let _ = std::fs::remove_file(&db_path);
let mut db = Database::open(db_path.to_string_lossy())?;
db.execute("CREATE TABLE vectors (id INTEGER PRIMARY KEY, vec2 VECTOR(2), vec3 VECTOR(3), vec4 VECTOR(4))")?;
db.execute("INSERT INTO vectors (id, vec2, vec3, vec4) VALUES (1, [1.0, 2.0], [1.0, 2.0, 3.0], [1.0, 2.0, 3.0, 4.0])")?;
db.execute("INSERT INTO vectors (id, vec2, vec3, vec4) VALUES (2, [0.5, 1.5], [0.5, 1.5, 2.5], [0.5, 1.5, 2.5, 3.5])")?;
let result = db.query("SELECT id, vec2, vec3, vec4 FROM vectors ORDER BY id")?;
let rows = result.rows();
assert_eq!(rows.len(), 2);
assert_eq!(rows[0][0], SqlValue::Integer(1));
if let SqlValue::Vector(vec2) = &rows[0][1] {
assert_eq!(vec2.len(), 2);
assert_eq!(vec2[0], 1.0);
assert_eq!(vec2[1], 2.0);
} else {
panic!("Expected vector for vec2");
}
if let SqlValue::Vector(vec3) = &rows[0][2] {
assert_eq!(vec3.len(), 3);
assert_eq!(vec3[0], 1.0);
assert_eq!(vec3[1], 2.0);
assert_eq!(vec3[2], 3.0);
} else {
panic!("Expected vector for vec3");
}
if let SqlValue::Vector(vec4) = &rows[0][3] {
assert_eq!(vec4.len(), 4);
assert_eq!(vec4[0], 1.0);
assert_eq!(vec4[1], 2.0);
assert_eq!(vec4[2], 3.0);
assert_eq!(vec4[3], 4.0);
} else {
panic!("Expected vector for vec4");
}
std::fs::remove_file(&db_path)?;
Ok(())
}
#[test]
fn test_vector_edge_cases() -> Result<()> {
let db_path = std::env::temp_dir().join("test_vector_edge.teg");
let _ = std::fs::remove_file(&db_path);
let mut db = Database::open(db_path.to_string_lossy())?;
db.execute("CREATE TABLE edge_cases (id INTEGER PRIMARY KEY, vec VECTOR(3))")?;
db.execute("INSERT INTO edge_cases (id, vec) VALUES (1, [0.0, 0.0, 0.0])")?;
db.execute("INSERT INTO edge_cases (id, vec) VALUES (2, [1.0, 0.0, 0.0])")?;
db.execute("INSERT INTO edge_cases (id, vec) VALUES (3, [0.0, 1.0, 0.0])")?;
db.execute("INSERT INTO edge_cases (id, vec) VALUES (4, [0.0, 0.0, 1.0])")?;
db.execute("INSERT INTO edge_cases (id, vec) VALUES (5, [-1.0, -2.0, -3.0])")?;
db.execute("INSERT INTO edge_cases (id, vec) VALUES (6, [0.0001, 0.0002, 0.0003])")?;
db.execute("INSERT INTO edge_cases (id, vec) VALUES (7, [1000.0, 2000.0, 3000.0])")?;
let result =
db.query("SELECT COSINE_SIMILARITY(vec, [1.0, 0.0, 0.0]) FROM edge_cases WHERE id = 2")?;
let rows = result.rows();
assert_eq!(rows.len(), 1);
let result = db.query("SELECT L2_NORMALIZE(vec) FROM edge_cases WHERE id = 2")?;
let rows = result.rows();
assert_eq!(rows.len(), 1);
let result =
db.query("SELECT DOT_PRODUCT(vec, [1.0, 1.0, 1.0]) FROM edge_cases WHERE id = 5")?;
let rows = result.rows();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0][0], SqlValue::Real(-6.0));
std::fs::remove_file(&db_path)?;
Ok(())
}
#[test]
fn test_vector_index_operations() -> Result<()> {
use tegdb::vector_index::HNSWIndex;
let mut index = HNSWIndex::new(16, 32);
assert!(index.is_empty());
assert_eq!(index.len(), 0);
let results = index.search(&[1.0, 0.0, 0.0], 5)?;
assert!(results.is_empty());
index.insert(1, vec![1.0, 0.0, 0.0])?;
assert!(!index.is_empty());
assert_eq!(index.len(), 1);
index.insert(2, vec![0.0, 1.0, 0.0])?;
assert_eq!(index.len(), 2);
let results = index.search(&[0.8, 0.2, 0.0], 2)?;
assert!(!results.is_empty());
assert_eq!(index.len(), 2);
Ok(())
}