use aletheiadb::AletheiaDB;
use aletheiadb::Error;
use aletheiadb::WriteOps;
use aletheiadb::core::id::NodeId;
use aletheiadb::core::property::PropertyMapBuilder;
use aletheiadb::index::vector::temporal::{
DriftMetric, RetentionPolicy, SnapshotStrategy, TemporalVectorConfig,
};
use aletheiadb::index::vector::{DistanceMetric, HnswConfig};
use std::sync::Arc;
use std::thread;
#[test]
fn test_enable_vector_index() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let config2 = HnswConfig::new(3, DistanceMetric::Cosine);
assert!(db.enable_vector_index("embedding", config2).is_err());
}
#[test]
fn test_find_similar_basic() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let doc1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Rust Programming")
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let doc2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Rust Advanced")
.insert_vector("embedding", &[0.9f32, 0.1, 0.0])
.build(),
)
.unwrap();
let doc3 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Python Basics")
.insert_vector("embedding", &[0.0f32, 1.0, 0.0])
.build(),
)
.unwrap();
let similar = db.find_similar(doc1, 2).unwrap();
assert_eq!(similar.len(), 2);
assert_eq!(similar[0].0, doc2);
assert!(similar[0].1 > 0.9);
assert_eq!(similar[1].0, doc3);
assert!(similar[1].1 < 0.5); }
#[test]
fn test_find_similar_with_label() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let doc1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let doc2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.9f32, 0.1, 0.0])
.build(),
)
.unwrap();
let _person1 = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.95f32, 0.05, 0.0])
.build(),
)
.unwrap();
let similar = db.find_similar_with_label(doc1, "Document", 5).unwrap();
assert_eq!(similar.len(), 1);
assert_eq!(similar[0].0, doc2);
}
#[test]
fn test_vector_index_not_enabled() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
assert!(db.find_similar(node_id, 5).is_err());
}
#[test]
fn test_vector_index_with_euclidean_distance() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Euclidean).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let doc1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let doc2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let doc3 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[10.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let similar = db.find_similar(doc1, 2).unwrap();
assert_eq!(similar.len(), 2);
assert_eq!(similar[0].0, doc2);
assert_eq!(similar[1].0, doc3);
}
#[test]
fn test_vector_index_with_large_k() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let mut node_ids = Vec::new();
for i in 0..5 {
let node_id = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[i as f32, 0.0, 0.0])
.build(),
)
.unwrap();
node_ids.push(node_id);
}
let similar = db.find_similar(node_ids[0], 10).unwrap();
assert!(similar.len() <= 4);
}
#[test]
fn test_transaction_nodes_are_indexed() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let (doc1, doc2, _doc3) = db
.write(|tx| {
let d1 = tx.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)?;
let d2 = tx.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.9f32, 0.1, 0.0])
.build(),
)?;
let d3 = tx.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.0f32, 1.0, 0.0])
.build(),
)?;
Ok::<_, Error>((d1, d2, d3))
})
.unwrap();
let similar = db.find_similar(doc1, 2).unwrap();
assert_eq!(similar.len(), 2);
assert_eq!(similar[0].0, doc2); assert!(similar[0].1 > 0.9); }
#[test]
fn test_find_similar_by_embedding() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let doc1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Rust Programming")
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let doc2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Rust Advanced")
.insert_vector("embedding", &[0.9f32, 0.1, 0.0])
.build(),
)
.unwrap();
let _doc3 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Python Basics")
.insert_vector("embedding", &[0.0f32, 1.0, 0.0])
.build(),
)
.unwrap();
let query_embedding = [0.95f32, 0.05, 0.0];
let similar = db.find_similar_by_embedding(&query_embedding, 2).unwrap();
assert_eq!(similar.len(), 2);
assert_eq!(similar[0].0, doc1); assert!(similar[0].1 > 0.99); assert_eq!(similar[1].0, doc2);
}
#[test]
fn test_find_similar_by_embedding_with_label() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let doc1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let doc2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.9f32, 0.1, 0.0])
.build(),
)
.unwrap();
let _person1 = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.95f32, 0.05, 0.0])
.build(),
)
.unwrap();
let query_embedding = [1.0f32, 0.0, 0.0];
let similar = db
.find_similar_by_embedding_with_label(&query_embedding, "Document", 5)
.unwrap();
assert_eq!(similar.len(), 2);
assert!(similar.iter().any(|(id, _)| *id == doc1));
assert!(similar.iter().any(|(id, _)| *id == doc2));
}
#[test]
fn test_find_similar_by_embedding_dimension_mismatch() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
db.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let wrong_embedding = [1.0f32, 0.0, 0.0, 0.0];
let result = db.find_similar_by_embedding(&wrong_embedding, 5);
assert!(result.is_err());
}
#[test]
fn test_find_similar_empty_database() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let query_embedding = [1.0f32, 0.0, 0.0];
let results = db.find_similar_by_embedding(&query_embedding, 10).unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_find_similar_k_zero() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
db.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let query_embedding = [1.0f32, 0.0, 0.0];
let results = db.find_similar_by_embedding(&query_embedding, 0).unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_concurrent_vector_indexing() {
let db = Arc::new(AletheiaDB::new().unwrap());
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(1000);
db.enable_vector_index("embedding", config).unwrap();
let mut handles = vec![];
for i in 0..10 {
let db_clone = Arc::clone(&db);
let handle = thread::spawn(move || {
let base = (i as f32 + 1.0) / 10.0;
db_clone
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[base, base, base, base])
.build(),
)
.unwrap()
});
handles.push(handle);
}
let node_ids: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
for node_id in &node_ids {
let results = db.find_similar(*node_id, 5).unwrap();
assert!(
results.len() >= 4,
"Expected >=4 results for node {:?}, got {}",
node_id,
results.len()
);
assert!(
results.iter().all(|(id, _)| *id != *node_id),
"Query node {:?} should not appear in its own results",
node_id
);
for (_, score) in &results {
assert!(
(0.0..=1.0).contains(score),
"Similarity score {} out of range",
score
);
}
}
assert_eq!(db.node_count(), 10);
}
#[test]
fn test_find_similar_with_missing_property() {
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(3, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config).unwrap();
let doc1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0f32, 0.0, 0.0])
.build(),
)
.unwrap();
let _doc2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.9f32, 0.1, 0.0])
.build(),
)
.unwrap();
let _doc_no_vector = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "No embedding")
.build(),
)
.unwrap();
let results = db.find_similar(doc1, 5).unwrap();
assert_eq!(results.len(), 1); }
#[test]
fn test_vector_index_builder_basic() {
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.expect("Should enable vector index via builder");
assert!(db.has_vector_index("embedding"));
}
#[test]
fn test_vector_index_builder_multiple_properties() {
let db = AletheiaDB::new().unwrap();
db.vector_index("title_embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
db.vector_index("body_embedding")
.hnsw(HnswConfig::new(8, DistanceMetric::Euclidean).with_capacity(100))
.enable()
.unwrap();
assert!(db.has_vector_index("title_embedding"));
assert!(db.has_vector_index("body_embedding"));
let indexes = db.list_vector_indexes();
assert_eq!(indexes.len(), 2);
}
#[test]
fn test_vector_index_builder_missing_hnsw_fails() {
let db = AletheiaDB::new().unwrap();
let result = db.vector_index("embedding").enable();
assert!(result.is_err(), "Should fail without HNSW config");
}
#[test]
fn test_vector_index_builder_with_temporal() {
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(10),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config), })
.enable()
.expect("Should enable both current and temporal index");
assert!(db.has_vector_index("embedding"));
assert!(db.is_temporal_vector_index_enabled());
}
#[test]
fn test_vector_index_builder_functional() {
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.9f32, 0.1, 0.0, 0.0];
let node1 = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let _node2 = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
let results = db.find_similar(node1, 1).unwrap();
assert_eq!(results.len(), 1);
}
#[test]
fn test_vector_index_builder_same_property_twice_fails() {
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
let result = db
.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable();
assert!(
result.is_err(),
"Should not allow re-enabling same property"
);
}
#[test]
fn test_find_similar_in_explicit_property() {
let db = AletheiaDB::new().unwrap();
db.vector_index("title_embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
db.vector_index("body_embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
let title_v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let title_v2 = vec![0.9f32, 0.1, 0.0, 0.0];
let body_v1 = vec![0.0f32, 1.0, 0.0, 0.0];
let body_v2 = vec![0.0f32, 0.9, 0.1, 0.0];
let node1 = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &title_v1)
.insert_vector("body_embedding", &body_v1)
.build(),
)
.unwrap();
let _node2 = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &title_v2)
.insert_vector("body_embedding", &body_v2)
.build(),
)
.unwrap();
let title_results = db.find_similar_in("title_embedding", node1, 1).unwrap();
assert_eq!(title_results.len(), 1);
let body_results = db.find_similar_in("body_embedding", node1, 1).unwrap();
assert_eq!(body_results.len(), 1);
}
#[test]
fn test_search_vectors_in_explicit_property() {
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.9f32, 0.1, 0.0, 0.0];
db.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
db.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
let query = vec![1.0f32, 0.0, 0.0, 0.0];
let results = db.search_vectors_in("embedding", &query, 2).unwrap();
assert_eq!(results.len(), 2);
}
#[test]
fn test_find_similar_in_nonexistent_property_fails() {
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let node1 = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let result = db.find_similar_in("nonexistent", node1, 1);
assert!(result.is_err());
}
#[test]
fn test_find_similar_as_of_in_explicit_property() {
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("content_embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1), retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let _node1 = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("content_embedding", &v1)
.build(),
)
.unwrap();
let timestamp = aletheiadb::core::temporal::time::now();
let query = vec![0.9f32, 0.1, 0.0, 0.0];
let results = db
.find_similar_as_of_in("content_embedding", &query, 10, timestamp)
.unwrap();
assert!(!results.is_empty());
}
#[test]
fn test_find_similar_as_of_in_wrong_property_fails() {
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let timestamp = aletheiadb::core::temporal::time::now();
let query = vec![1.0f32, 0.0, 0.0, 0.0];
let result = db.find_similar_as_of_in("wrong_property", &query, 10, timestamp);
assert!(
result.is_err(),
"Should fail when property doesn't match temporal index"
);
}
#[test]
fn test_find_similar_as_of_in_no_temporal_index() {
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
let timestamp = aletheiadb::core::temporal::time::now();
let query = vec![1.0f32, 0.0, 0.0, 0.0];
let result = db.find_similar_as_of_in("embedding", &query, 10, timestamp);
assert!(
result.is_err(),
"Should fail when temporal index not enabled"
);
}
#[test]
fn test_track_drift_in_explicit_property() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("content_embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1), retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let node_id = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("content_embedding", &v1)
.build(),
)
.unwrap();
let reference = vec![1.0f32, 0.0, 0.0, 0.0];
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let result = db.track_drift_in("content_embedding", node_id, &reference, time_range);
assert!(
result.is_ok(),
"track_drift_in should succeed with correct property"
);
}
#[test]
fn test_track_drift_in_wrong_property_fails() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let node_id = NodeId::new(1).unwrap();
let reference = vec![1.0f32, 0.0, 0.0, 0.0];
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let result = db.track_drift_in("wrong_property", node_id, &reference, time_range);
assert!(
result.is_err(),
"Should fail when property doesn't match temporal index"
);
}
#[test]
fn test_track_drift_in_no_temporal_index() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100))
.enable()
.unwrap();
let node_id = NodeId::new(1).unwrap();
let reference = vec![1.0f32, 0.0, 0.0, 0.0];
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let result = db.track_drift_in("embedding", node_id, &reference, time_range);
assert!(
result.is_err(),
"Should fail when temporal index not enabled"
);
}
#[test]
fn test_semantic_evolution_in_explicit_property() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("content_embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let node_id = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("content_embedding", &v1)
.build(),
)
.unwrap();
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let result = db.semantic_evolution_in("content_embedding", node_id, time_range);
assert!(result.is_ok(), "semantic_evolution_in should succeed");
}
#[test]
fn test_semantic_evolution_in_wrong_property_fails() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let node_id = NodeId::new(1).unwrap();
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let result = db.semantic_evolution_in("wrong_property", node_id, time_range);
assert!(
result.is_err(),
"Should fail when property doesn't match temporal index"
);
}
#[test]
fn test_find_drift_in_explicit_property() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("content_embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let _node_id = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("content_embedding", &v1)
.build(),
)
.unwrap();
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let result = db.find_drift_in("content_embedding", 0.1, time_range, DriftMetric::Cosine);
assert!(result.is_ok(), "find_drift_in should succeed");
}
#[test]
fn test_find_drift_in_wrong_property_fails() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let result = db.find_drift_in("wrong_property", 0.1, time_range, DriftMetric::Cosine);
assert!(
result.is_err(),
"Should fail when property doesn't match temporal index"
);
}
#[test]
fn test_multi_property_temporal_indexes() {
use aletheiadb::core::temporal::TimeRange;
let db = AletheiaDB::new().unwrap();
let hnsw_config1 = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("title_embedding")
.hnsw(hnsw_config1.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config1),
})
.enable()
.expect("Should enable first temporal index");
let hnsw_config2 = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("content_embedding")
.hnsw(hnsw_config2.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config2),
})
.enable()
.expect("Should enable second temporal index");
let title_emb = vec![1.0f32, 0.0, 0.0, 0.0];
let content_emb = vec![0.0f32, 1.0, 0.0, 0.0];
let node_id = db
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &title_emb)
.insert_vector("content_embedding", &content_emb)
.build(),
)
.unwrap();
let time_range = TimeRange::new(0.into(), i64::MAX.into()).unwrap();
let query = vec![0.9f32, 0.1, 0.0, 0.0];
let result1 = db.find_similar_as_of_in(
"title_embedding",
&query,
10,
aletheiadb::core::temporal::time::now(),
);
assert!(
result1.is_ok(),
"First temporal index should still work: {:?}",
result1.err()
);
let result2 = db.find_similar_as_of_in(
"content_embedding",
&query,
10,
aletheiadb::core::temporal::time::now(),
);
assert!(
result2.is_ok(),
"Second temporal index should work: {:?}",
result2.err()
);
let reference = vec![1.0f32, 0.0, 0.0, 0.0];
let drift1 = db.track_drift_in("title_embedding", node_id, &reference, time_range);
assert!(
drift1.is_ok(),
"track_drift_in for first property should work"
);
let drift2 = db.track_drift_in("content_embedding", node_id, &reference, time_range);
assert!(
drift2.is_ok(),
"track_drift_in for second property should work"
);
}
#[test]
fn test_temporal_query_nonexistent_property() {
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding")
.hnsw(hnsw_config.clone())
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: Some(hnsw_config),
})
.enable()
.expect("Should enable temporal index");
let query = vec![1.0f32, 0.0, 0.0, 0.0];
let result = db.find_similar_as_of_in(
"nonexistent_property",
&query,
10,
aletheiadb::core::temporal::time::now(),
);
assert!(
result.is_err(),
"Should fail for property without temporal index"
);
}
#[test]
fn test_temporal_config_without_hnsw_config() {
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", hnsw_config).unwrap();
let temporal_config = TemporalVectorConfig::default_temporal_only();
let result = db.enable_temporal_vector_index("embedding", temporal_config);
assert!(
result.is_ok(),
"Should succeed when vector index exists and hnsw_config is None"
);
}
#[test]
fn test_temporal_config_without_hnsw_config_requires_existing_index() {
let db = AletheiaDB::new().unwrap();
let temporal_config = TemporalVectorConfig::default_temporal_only();
let result = db.enable_temporal_vector_index("embedding", temporal_config);
assert!(
result.is_err(),
"Should fail when no vector index exists and hnsw_config is None"
);
}
#[test]
fn test_temporal_config_default_temporal_only() {
let config = TemporalVectorConfig::default_temporal_only();
assert!(config.hnsw_config.is_none(), "hnsw_config should be None");
assert_eq!(
config.snapshot_strategy,
SnapshotStrategy::TransactionInterval(10)
);
assert_eq!(config.retention_policy, RetentionPolicy::KeepN(100));
assert_eq!(config.max_snapshots, 100);
assert_eq!(config.full_snapshot_interval, 10);
}
#[test]
fn test_list_temporal_vector_indexes() {
let db = AletheiaDB::new().unwrap();
assert!(db.list_temporal_vector_indexes().is_empty());
let hnsw_config1 = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding1")
.hnsw(hnsw_config1)
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(10),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: None, })
.enable()
.expect("Should enable first temporal index");
let indexes = db.list_temporal_vector_indexes();
assert_eq!(indexes.len(), 1);
assert!(indexes.contains(&"embedding1".to_string()));
let hnsw_config2 = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.vector_index("embedding2")
.hnsw(hnsw_config2)
.temporal(TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(10),
retention_policy: RetentionPolicy::KeepN(100),
max_snapshots: 100,
full_snapshot_interval: 10,
hnsw_config: None,
})
.enable()
.expect("Should enable second temporal index");
let indexes = db.list_temporal_vector_indexes();
assert_eq!(indexes.len(), 2);
assert!(indexes.contains(&"embedding1".to_string()));
assert!(indexes.contains(&"embedding2".to_string()));
}
#[test]
fn test_concurrent_vector_operations_with_multiple_properties() {
let db = Arc::new(AletheiaDB::new().unwrap());
let hnsw_config1 = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
let hnsw_config2 = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("title_embedding", hnsw_config1)
.unwrap();
db.enable_vector_index("content_embedding", hnsw_config2)
.unwrap();
let mut handles = vec![];
for i in 0..10 {
let db_clone = Arc::clone(&db);
let handle = thread::spawn(move || {
let base = (i as f32 + 1.0) / 10.0;
let title_emb = vec![base, 0.0, 0.0, 0.0];
let content_emb = vec![0.0, base, 0.0, 0.0];
db_clone
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("id", i as i64)
.insert_vector("title_embedding", &title_emb)
.insert_vector("content_embedding", &content_emb)
.build(),
)
.unwrap()
});
handles.push(handle);
}
let node_ids: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(db.node_count(), 10);
let title_query = vec![0.5f32, 0.0, 0.0, 0.0];
let title_results = db
.search_vectors_in("title_embedding", &title_query, 5)
.unwrap();
assert!(
!title_results.is_empty(),
"Should find results in title_embedding index"
);
let content_query = vec![0.0f32, 0.5, 0.0, 0.0];
let content_results = db
.search_vectors_in("content_embedding", &content_query, 5)
.unwrap();
assert!(
!content_results.is_empty(),
"Should find results in content_embedding index"
);
let similar_by_title = db
.search_vectors_in("title_embedding", &[0.5, 0.0, 0.0, 0.0], 10)
.unwrap();
let similar_by_content = db
.search_vectors_in("content_embedding", &[0.0, 0.5, 0.0, 0.0], 10)
.unwrap();
assert!(
!similar_by_title.is_empty(),
"Title index should have results"
);
assert!(
!similar_by_content.is_empty(),
"Content index should have results"
);
let _ = node_ids; }
#[test]
fn test_aletheiadb_is_vector_index_enabled_for() {
let db = AletheiaDB::new().unwrap();
assert!(!db.is_vector_index_enabled());
assert!(!db.is_vector_index_enabled_for("embedding"));
assert!(!db.is_vector_index_enabled_for("vector"));
let config = HnswConfig::new(128, DistanceMetric::Cosine);
db.enable_vector_index("embedding", config).unwrap();
assert!(db.is_vector_index_enabled());
assert!(db.is_vector_index_enabled_for("embedding"));
assert!(!db.is_vector_index_enabled_for("vector"));
let config2 = HnswConfig::new(256, DistanceMetric::Euclidean);
db.enable_vector_index("vector", config2).unwrap();
assert!(db.is_vector_index_enabled_for("vector"));
}
#[test]
fn test_max_vector_properties_limit() {
use aletheiadb::index::vector::{DistanceMetric, HnswConfig};
use aletheiadb::storage::DEFAULT_MAX_VECTOR_PROPERTIES;
let db = AletheiaDB::new().unwrap();
for i in 0..DEFAULT_MAX_VECTOR_PROPERTIES {
let config = HnswConfig::new(4, DistanceMetric::Cosine);
let result = db
.vector_index(&format!("property_{}", i))
.hnsw(config)
.enable();
assert!(
result.is_ok(),
"Should be able to enable index {} (limit is {})",
i,
DEFAULT_MAX_VECTOR_PROPERTIES
);
}
let indexes = db.list_vector_indexes();
assert_eq!(indexes.len(), DEFAULT_MAX_VECTOR_PROPERTIES);
let config = HnswConfig::new(4, DistanceMetric::Cosine);
let result = db.vector_index("one_too_many").hnsw(config).enable();
assert!(
result.is_err(),
"Should not be able to exceed the maximum vector property limit"
);
let err = result.unwrap_err();
let err_msg = err.to_string();
assert!(
err_msg.contains("Maximum") || err_msg.contains("maximum"),
"Error message should mention the maximum limit"
);
}