use aletheiadb::Error;
use aletheiadb::{AletheiaDB, PropertyMapBuilder, WriteOps};
fn generate_embedding(dim: usize, seed: f32) -> Vec<f32> {
(0..dim).map(|i| (i as f32 + seed) / dim as f32).collect()
}
fn advance_time() {
std::thread::sleep(std::time::Duration::from_millis(10));
}
#[test]
fn test_create_node_with_vector_and_retrieve() {
let db = AletheiaDB::new().unwrap();
let embedding = vec![0.1f32, 0.2, 0.3, 0.4, 0.5];
let props = PropertyMapBuilder::new()
.insert("title", "Test Document")
.insert_vector("embedding", &embedding)
.build();
let node_id = db.create_node("Document", props).unwrap();
let node = db.get_node(node_id).unwrap();
assert_eq!(
node.get_property("title").and_then(|v| v.as_str()),
Some("Test Document")
);
assert_eq!(
node.get_property("embedding").and_then(|v| v.as_vector()),
Some(&embedding[..])
);
}
#[test]
fn test_update_vector_property_creates_version() {
let db = AletheiaDB::new().unwrap();
let embedding_v1 = vec![0.1f32, 0.2, 0.3];
let props = PropertyMapBuilder::new()
.insert("name", "Document")
.insert_vector("embedding", &embedding_v1)
.build();
let node_id = db.create_node("Document", props).unwrap();
let stats_before = db.historical_stats().unwrap();
let versions_before = stats_before.total_node_versions;
advance_time();
let embedding_v2 = vec![0.9f32, 0.8, 0.7];
{
let mut tx = db.write_transaction().unwrap();
let new_props = PropertyMapBuilder::new()
.insert("name", "Document")
.insert_vector("embedding", &embedding_v2)
.build();
tx.update_node(node_id, new_props).unwrap();
tx.commit().unwrap();
}
let current_node = db.get_node(node_id).unwrap();
assert_eq!(
current_node
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_v2[..])
);
let stats_after = db.historical_stats().unwrap();
assert!(
stats_after.total_node_versions > versions_before,
"Update should create new version"
);
}
#[test]
fn test_multiple_nodes_with_vectors_isolation() {
let db = AletheiaDB::new().unwrap();
let embedding_a = vec![1.0f32, 0.0, 0.0];
let embedding_b = vec![0.0f32, 1.0, 0.0];
let embedding_c = vec![0.0f32, 0.0, 1.0];
let node_a = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("name", "A")
.insert_vector("embedding", &embedding_a)
.build(),
)
.unwrap();
let node_b = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("name", "B")
.insert_vector("embedding", &embedding_b)
.build(),
)
.unwrap();
let node_c = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("name", "C")
.insert_vector("embedding", &embedding_c)
.build(),
)
.unwrap();
assert_eq!(
db.get_node(node_a)
.unwrap()
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_a[..])
);
assert_eq!(
db.get_node(node_b)
.unwrap()
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_b[..])
);
assert_eq!(
db.get_node(node_c)
.unwrap()
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_c[..])
);
let new_embedding = vec![0.5f32, 0.5, 0.5];
{
let mut tx = db.write_transaction().unwrap();
tx.update_node(
node_a,
PropertyMapBuilder::new()
.insert("name", "A")
.insert_vector("embedding", &new_embedding)
.build(),
)
.unwrap();
tx.commit().unwrap();
}
assert_eq!(
db.get_node(node_a)
.unwrap()
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&new_embedding[..])
);
assert_eq!(
db.get_node(node_b)
.unwrap()
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_b[..])
);
assert_eq!(
db.get_node(node_c)
.unwrap()
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_c[..])
);
}
#[test]
fn test_edge_with_vector_property() {
let db = AletheiaDB::new().unwrap();
let node_a = db
.create_node(
"Entity",
PropertyMapBuilder::new().insert("name", "A").build(),
)
.unwrap();
let node_b = db
.create_node(
"Entity",
PropertyMapBuilder::new().insert("name", "B").build(),
)
.unwrap();
let relationship_embedding = vec![0.8f32, 0.1, 0.1];
let edge_id = db
.create_edge(
node_a,
node_b,
"SIMILAR_TO",
PropertyMapBuilder::new()
.insert("weight", 0.95f64)
.insert_vector("embedding", &relationship_embedding)
.build(),
)
.unwrap();
let edge = db.get_edge(edge_id).unwrap();
assert_eq!(edge.source, node_a);
assert_eq!(edge.target, node_b);
assert_eq!(
edge.get_property("weight").and_then(|v| v.as_float()),
Some(0.95)
);
assert_eq!(
edge.get_property("embedding").and_then(|v| v.as_vector()),
Some(&relationship_embedding[..])
);
}
#[test]
fn test_update_edge_vector_property() {
let db = AletheiaDB::new().unwrap();
let node_a = db
.create_node("Entity", PropertyMapBuilder::new().build())
.unwrap();
let node_b = db
.create_node("Entity", PropertyMapBuilder::new().build())
.unwrap();
let embedding_v1 = vec![0.5f32, 0.5];
let edge_id = db
.create_edge(
node_a,
node_b,
"RELATES_TO",
PropertyMapBuilder::new()
.insert_vector("embedding", &embedding_v1)
.build(),
)
.unwrap();
let stats_before = db.historical_stats().unwrap();
let versions_before = stats_before.total_edge_versions;
advance_time();
let embedding_v2 = vec![0.9f32, 0.1];
{
let mut tx = db.write_transaction().unwrap();
tx.update_edge(
edge_id,
PropertyMapBuilder::new()
.insert_vector("embedding", &embedding_v2)
.build(),
)
.unwrap();
tx.commit().unwrap();
}
let current_edge = db.get_edge(edge_id).unwrap();
assert_eq!(
current_edge
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_v2[..])
);
let stats_after = db.historical_stats().unwrap();
assert!(
stats_after.total_edge_versions > versions_before,
"Update should create new edge version"
);
}
#[test]
fn test_large_vector_1000_dimensions() {
let db = AletheiaDB::new().unwrap();
const DIMENSIONS: usize = 1000;
let large_embedding = generate_embedding(DIMENSIONS, 0.0);
let node_id = db
.create_node(
"HighDimDoc",
PropertyMapBuilder::new()
.insert_vector("embedding", &large_embedding)
.build(),
)
.unwrap();
let node = db.get_node(node_id).unwrap();
let retrieved = node
.get_property("embedding")
.and_then(|v| v.as_vector())
.expect("Should have embedding");
assert_eq!(retrieved.len(), DIMENSIONS);
assert_eq!(retrieved, &large_embedding[..]);
}
#[test]
fn test_very_large_vector_4096_dimensions() {
let db = AletheiaDB::new().unwrap();
const DIMENSIONS: usize = 4096;
let embedding = generate_embedding(DIMENSIONS, 1.0);
let node_id = db
.create_node(
"VeryHighDimDoc",
PropertyMapBuilder::new()
.insert_vector("embedding", &embedding)
.build(),
)
.unwrap();
let node = db.get_node(node_id).unwrap();
let retrieved = node
.get_property("embedding")
.and_then(|v| v.as_vector())
.expect("Should have embedding");
assert_eq!(retrieved.len(), DIMENSIONS);
assert_eq!(retrieved, &embedding[..]);
}
macro_rules! test_embedding_dimension {
($test_name:ident, $dim:expr, $model:expr, $label:expr) => {
#[test]
fn $test_name() {
let db = AletheiaDB::new().unwrap();
const DIMENSIONS: usize = $dim;
let embedding = generate_embedding(DIMENSIONS, 0.0);
let node_id = db
.create_node(
$label,
PropertyMapBuilder::new()
.insert("model", $model)
.insert_vector("embedding", &embedding)
.build(),
)
.unwrap();
let node = db.get_node(node_id).unwrap();
let retrieved = node
.get_property("embedding")
.and_then(|v| v.as_vector())
.expect("Should have embedding");
assert_eq!(retrieved.len(), DIMENSIONS);
assert_eq!(
node.get_property("model").and_then(|v| v.as_str()),
Some($model)
);
}
};
}
test_embedding_dimension!(
test_common_embedding_dimensions_384,
384,
"all-MiniLM-L6-v2",
"MiniLMDoc"
);
test_embedding_dimension!(
test_common_embedding_dimensions_768,
768,
"all-mpnet-base-v2",
"BertDoc"
);
test_embedding_dimension!(
test_common_embedding_dimensions_1536,
1536,
"text-embedding-ada-002",
"OpenAIDoc"
);
test_embedding_dimension!(
test_common_embedding_dimensions_3072,
3072,
"text-embedding-3-large",
"OpenAI3LargeDoc"
);
#[test]
fn test_multiple_vector_updates_version_chain() {
let db = AletheiaDB::new().unwrap();
let embedding_v1 = vec![0.1f32, 0.2, 0.3];
let node_id = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &embedding_v1)
.build(),
)
.unwrap();
let stats_initial = db.historical_stats().unwrap();
advance_time();
let embedding_v2 = vec![0.4f32, 0.5, 0.6];
{
let mut tx = db.write_transaction().unwrap();
tx.update_node(
node_id,
PropertyMapBuilder::new()
.insert_vector("embedding", &embedding_v2)
.build(),
)
.unwrap();
tx.commit().unwrap();
}
advance_time();
let embedding_v3 = vec![0.7f32, 0.8, 0.9];
{
let mut tx = db.write_transaction().unwrap();
tx.update_node(
node_id,
PropertyMapBuilder::new()
.insert_vector("embedding", &embedding_v3)
.build(),
)
.unwrap();
tx.commit().unwrap();
}
assert_eq!(
db.get_node(node_id)
.unwrap()
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&embedding_v3[..])
);
let stats_final = db.historical_stats().unwrap();
assert_eq!(
stats_final.total_node_versions,
stats_initial.total_node_versions + 2,
"Should have 2 additional versions after 2 updates"
);
assert_eq!(stats_final.unique_nodes, 1);
}
#[test]
fn test_historical_stats_with_vectors() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &[0.1f32, 0.2])
.build(),
)
.unwrap();
for i in 1..5 {
let mut tx = db.write_transaction().unwrap();
tx.update_node(
node_id,
PropertyMapBuilder::new()
.insert_vector("embedding", &[i as f32 * 0.1, i as f32 * 0.2])
.build(),
)
.unwrap();
tx.commit().unwrap();
}
let stats = db.historical_stats().unwrap();
assert_eq!(
stats.total_node_versions, 5,
"Expected 1 create + 4 updates = 5 versions"
);
assert_eq!(stats.unique_nodes, 1);
let total = stats.node_anchor_count + stats.node_delta_count;
assert_eq!(total, 5, "Anchor + delta count should equal total versions");
assert!(
stats.node_anchor_count > 0,
"Should have at least one anchor version"
);
}
#[test]
fn test_empty_vector() {
let db = AletheiaDB::new().unwrap();
let empty_vec: Vec<f32> = vec![];
let node_id = db
.create_node(
"EmptyVecNode",
PropertyMapBuilder::new()
.insert_vector("embedding", &empty_vec)
.build(),
)
.unwrap();
let node = db.get_node(node_id).unwrap();
assert_eq!(
node.get_property("embedding").and_then(|v| v.as_vector()),
Some(&empty_vec[..])
);
}
#[test]
fn test_node_with_multiple_embeddings() {
let db = AletheiaDB::new().unwrap();
let text_embedding = vec![0.1f32, 0.2, 0.3, 0.4];
let image_embedding = vec![0.5f32, 0.6, 0.7, 0.8];
let combined_embedding = vec![0.9f32, 0.0, 0.1, 0.2];
let node_id = db
.create_node(
"MultimodalDoc",
PropertyMapBuilder::new()
.insert("content", "A picture of a cat")
.insert_vector("text_embedding", &text_embedding)
.insert_vector("image_embedding", &image_embedding)
.insert_vector("combined_embedding", &combined_embedding)
.build(),
)
.unwrap();
let node = db.get_node(node_id).unwrap();
assert_eq!(
node.get_property("text_embedding")
.and_then(|v| v.as_vector()),
Some(&text_embedding[..])
);
assert_eq!(
node.get_property("image_embedding")
.and_then(|v| v.as_vector()),
Some(&image_embedding[..])
);
assert_eq!(
node.get_property("combined_embedding")
.and_then(|v| v.as_vector()),
Some(&combined_embedding[..])
);
}
#[test]
fn test_graph_with_mixed_properties_and_vectors() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.insert_vector("profile_embedding", &[0.1f32, 0.2, 0.3])
.build(),
)
.unwrap();
let bob = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Bob")
.insert("age", 25i64)
.insert_vector("profile_embedding", &[0.4f32, 0.5, 0.6])
.build(),
)
.unwrap();
let _knows = db
.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new()
.insert("since", 2020i64)
.insert_vector("relationship_embedding", &[0.7f32, 0.8, 0.9])
.build(),
)
.unwrap();
assert_eq!(db.node_count(), 2);
assert_eq!(db.edge_count(), 1);
assert_eq!(db.out_degree(alice), 1);
assert_eq!(db.in_degree(bob), 1);
let alice_node = db.get_node(alice).unwrap();
assert_eq!(
alice_node
.get_property("profile_embedding")
.and_then(|v| v.as_vector()),
Some(&[0.1f32, 0.2, 0.3][..])
);
}
fn setup_indexed_db(dimensions: usize) -> AletheiaDB {
use aletheiadb::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(dimensions, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("embedding", config)
.expect("Failed to enable vector index");
db
}
#[test]
fn test_enable_vector_index() {
use aletheiadb::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
assert!(!db.is_vector_index_enabled());
let config = HnswConfig::new(384, DistanceMetric::Cosine);
db.enable_vector_index("embedding", config).unwrap();
assert!(db.is_vector_index_enabled());
}
#[test]
fn test_double_enable_vector_index_fails() {
use aletheiadb::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
let config = HnswConfig::new(384, DistanceMetric::Cosine);
db.enable_vector_index("embedding", config.clone()).unwrap();
let result = db.enable_vector_index("embedding", config);
assert!(result.is_err());
}
#[test]
fn test_find_similar_by_node_id() {
let db = setup_indexed_db(384);
let emb1 = generate_embedding(384, 1.0);
let emb2 = generate_embedding(384, 1.1); let emb3 = generate_embedding(384, 10.0);
let node1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Doc 1")
.insert_vector("embedding", &emb1)
.build(),
)
.unwrap();
let node2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Doc 2")
.insert_vector("embedding", &emb2)
.build(),
)
.unwrap();
let node3 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Doc 3")
.insert_vector("embedding", &emb3)
.build(),
)
.unwrap();
let results = db.find_similar(node1, 2).unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].0, node2);
assert_eq!(results[1].0, node3);
}
#[test]
fn test_find_similar_by_embedding() {
let db = setup_indexed_db(384);
let emb1 = generate_embedding(384, 1.0);
let emb2 = generate_embedding(384, 1.1);
db.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb1)
.build(),
)
.unwrap();
db.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb2)
.build(),
)
.unwrap();
let query = generate_embedding(384, 1.05);
let results = db.find_similar_by_embedding(&query, 2).unwrap();
assert_eq!(results.len(), 2);
assert!(results[0].1 > 0.0);
assert!(results[1].1 > 0.0);
}
#[test]
fn test_find_similar_with_label_filter() {
let db = setup_indexed_db(128);
let emb = generate_embedding(128, 1.0);
let doc1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb)
.build(),
)
.unwrap();
let doc2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb)
.build(),
)
.unwrap();
let _image = db
.create_node(
"Image",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb)
.build(),
)
.unwrap();
let results = db.find_similar_with_label(doc1, "Document", 10).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, doc2);
}
#[test]
fn test_update_node_updates_index() {
let db = setup_indexed_db(128);
let emb1 = generate_embedding(128, 1.0);
let emb2 = generate_embedding(128, 10.0);
let node_id = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb1)
.build(),
)
.unwrap();
let mut tx = db.write_transaction().unwrap();
tx.update_node(
node_id,
PropertyMapBuilder::new()
.insert_vector("embedding", &emb2)
.build(),
)
.unwrap();
tx.commit().unwrap();
let results = db.find_similar_by_embedding(&emb2, 1).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, node_id);
}
#[test]
fn test_delete_node_removes_from_index() {
let db = setup_indexed_db(128);
let emb1 = generate_embedding(128, 1.0);
let emb2 = generate_embedding(128, 2.0);
let node1 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Doc 1")
.insert_vector("embedding", &emb1)
.build(),
)
.unwrap();
let node2 = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Doc 2")
.insert_vector("embedding", &emb2)
.build(),
)
.unwrap();
let results_before = db.find_similar_by_embedding(&emb1, 10).unwrap();
assert_eq!(
results_before.len(),
2,
"Should have 2 nodes before deletion"
);
db.write(|tx| {
tx.delete_node(node1)?;
Ok::<_, Error>(())
})
.unwrap();
let results_after = db.find_similar_by_embedding(&emb1, 10).unwrap();
assert_eq!(
results_after.len(),
1,
"Should have only 1 node after deletion"
);
assert_eq!(results_after[0].0, node2, "Remaining node should be node2");
let similar_to_node2 = db.find_similar(node2, 10).unwrap();
assert!(
!similar_to_node2.iter().any(|(id, _)| *id == node1),
"Deleted node should not appear in similarity search"
);
}
#[test]
fn test_delete_node_memory_stability_repeated_cycles() {
let db = setup_indexed_db(128);
for i in 0..100 {
let emb = generate_embedding(128, i as f32);
let node_id = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb)
.build(),
)
.unwrap();
let results = db.find_similar(node_id, 1).unwrap();
assert_eq!(
results.len(),
0,
"Should find no similar nodes (only this node exists)"
);
db.write(|tx| {
tx.delete_node(node_id)?;
Ok::<_, Error>(())
})
.unwrap();
let results_after = db.find_similar_by_embedding(&emb, 10).unwrap();
assert_eq!(
results_after.len(),
0,
"Should find no nodes after deletion in cycle {}",
i
);
}
assert_eq!(
db.node_count(),
0,
"Database should be empty after all deletes"
);
let final_search = db
.find_similar_by_embedding(&generate_embedding(128, 0.0), 10)
.unwrap();
assert_eq!(final_search.len(), 0, "Index should be empty");
}
#[test]
fn test_deleted_nodes_not_in_similarity_results() {
let db = setup_indexed_db(128);
let ref_emb = generate_embedding(128, 5.0);
let ref_node = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "Reference")
.insert_vector("embedding", &ref_emb)
.build(),
)
.unwrap();
let similar_nodes: Vec<_> = (0..5)
.map(|i| {
let emb = generate_embedding(128, 5.0 + (i as f32 * 0.1));
db.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", format!("Similar {}", i))
.insert_vector("embedding", &emb)
.build(),
)
.unwrap()
})
.collect();
let results_before = db.find_similar(ref_node, 10).unwrap();
assert_eq!(
results_before.len(),
5,
"Should find all 5 similar nodes before deletion"
);
db.write(|tx| {
tx.delete_node(similar_nodes[0])?;
tx.delete_node(similar_nodes[2])?;
tx.delete_node(similar_nodes[4])?;
Ok::<_, Error>(())
})
.unwrap();
let results_after = db.find_similar(ref_node, 10).unwrap();
assert_eq!(
results_after.len(),
2,
"Should find only 2 remaining nodes after deletion"
);
let remaining_ids: Vec<_> = results_after.iter().map(|(id, _)| *id).collect();
assert!(
remaining_ids.contains(&similar_nodes[1]),
"Node 1 should still be in results"
);
assert!(
remaining_ids.contains(&similar_nodes[3]),
"Node 3 should still be in results"
);
assert!(
!remaining_ids.contains(&similar_nodes[0]),
"Deleted node 0 should not be in results"
);
assert!(
!remaining_ids.contains(&similar_nodes[2]),
"Deleted node 2 should not be in results"
);
assert!(
!remaining_ids.contains(&similar_nodes[4]),
"Deleted node 4 should not be in results"
);
}
#[test]
fn test_delete_node_removes_from_multiple_indexes() {
use aletheiadb::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
let config1 = HnswConfig::new(64, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("text_embedding", config1).unwrap();
let config2 = HnswConfig::new(64, DistanceMetric::Cosine).with_capacity(100);
db.enable_vector_index("image_embedding", config2).unwrap();
let text_emb = generate_embedding(64, 1.0);
let image_emb = generate_embedding(64, 2.0);
let node_id = db
.create_node(
"MultimodalDoc",
PropertyMapBuilder::new()
.insert("title", "Test")
.insert_vector("text_embedding", &text_emb)
.insert_vector("image_embedding", &image_emb)
.build(),
)
.unwrap();
let ref_node = db
.create_node(
"MultimodalDoc",
PropertyMapBuilder::new()
.insert("title", "Reference")
.insert_vector("text_embedding", &generate_embedding(64, 1.1))
.insert_vector("image_embedding", &generate_embedding(64, 2.1))
.build(),
)
.unwrap();
let results_before_text = db
.search_vectors_in("text_embedding", &text_emb, 10)
.unwrap();
assert_eq!(
results_before_text.len(),
2,
"Should find 2 nodes in text_embedding index before deletion"
);
let results_before_image = db
.search_vectors_in("image_embedding", &image_emb, 10)
.unwrap();
assert_eq!(
results_before_image.len(),
2,
"Should find 2 nodes in image_embedding index before deletion"
);
db.write(|tx| {
tx.delete_node(node_id)?;
Ok::<_, Error>(())
})
.unwrap();
let results_after_text = db
.search_vectors_in("text_embedding", &text_emb, 10)
.unwrap();
assert_eq!(
results_after_text.len(),
1,
"Should find only 1 node in text_embedding index after deletion"
);
assert_eq!(
results_after_text[0].0, ref_node,
"Remaining node should be reference node in text_embedding index"
);
let results_after_image = db
.search_vectors_in("image_embedding", &image_emb, 10)
.unwrap();
assert_eq!(
results_after_image.len(),
1,
"Should find only 1 node in image_embedding index after deletion"
);
assert_eq!(
results_after_image[0].0, ref_node,
"Remaining node should be reference node in image_embedding index"
);
}
#[test]
fn test_node_without_vector_property_not_indexed() {
let db = setup_indexed_db(128);
let _node_no_emb = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert("title", "No Embedding")
.build(),
)
.unwrap();
let emb = generate_embedding(128, 1.0);
let node_with_emb = db
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb)
.build(),
)
.unwrap();
let results = db.find_similar_by_embedding(&emb, 10).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, node_with_emb);
}
#[test]
fn test_find_similar_on_non_indexed_db_fails() {
use aletheiadb::core::id::NodeId;
let db = AletheiaDB::new().unwrap();
let node_id = NodeId::new(1).unwrap();
let result = db.find_similar(node_id, 10);
assert!(result.is_err());
}
#[test]
fn test_find_similar_with_invalid_node_id_fails() {
use aletheiadb::core::id::NodeId;
let db = setup_indexed_db(128);
let fake_id = NodeId::new(99999).unwrap();
let result = db.find_similar(fake_id, 10);
assert!(result.is_err());
}
#[test]
fn test_dimension_mismatch_in_indexed_property() {
let db = setup_indexed_db(128);
let emb128 = generate_embedding(128, 1.0);
db.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb128)
.build(),
)
.unwrap();
let emb256 = generate_embedding(256, 1.0);
let result = db.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb256)
.build(),
);
assert!(result.is_err());
}
#[test]
fn test_concurrent_index_operations() {
use std::sync::Arc;
use std::thread;
let db = Arc::new(setup_indexed_db(64));
let num_threads = 4;
let nodes_per_thread = 10;
let handles: Vec<_> = (0..num_threads)
.map(|thread_id| {
let db_clone = Arc::clone(&db);
thread::spawn(move || {
for i in 0..nodes_per_thread {
let emb = generate_embedding(64, (thread_id * 100 + i) as f32);
db_clone
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &emb)
.build(),
)
.unwrap();
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
let query = generate_embedding(64, 0.0);
let results = db.find_similar_by_embedding(&query, 100).unwrap();
assert_eq!(results.len(), (num_threads * nodes_per_thread) as usize);
}