use manifoldb::{Database, Entity, EntityId, Value};
#[test]
fn test_bulk_delete_empty() {
let db = Database::in_memory().expect("failed to create db");
let entity_ids: Vec<EntityId> = Vec::new();
let deleted = db.bulk_delete_entities(&entity_ids).expect("bulk delete failed");
assert_eq!(deleted, 0);
}
#[test]
fn test_bulk_delete_single_entity() {
let db = Database::in_memory().expect("failed to create db");
let entities =
vec![Entity::new(EntityId::new(0)).with_label("Person").with_property("name", "Alice")];
let ids = db.bulk_insert_entities(&entities).expect("bulk insert failed");
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(ids[0]).expect("get failed").is_some());
}
let deleted = db.bulk_delete_entities(&ids).expect("bulk delete failed");
assert_eq!(deleted, 1);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(ids[0]).expect("get failed").is_none());
}
}
#[test]
fn test_bulk_delete_multiple_entities() {
let db = Database::in_memory().expect("failed to create db");
let entities: Vec<Entity> = (0..100)
.map(|i| {
Entity::new(EntityId::new(0)).with_label("Document").with_property("index", i as i64)
})
.collect();
let ids = db.bulk_insert_entities(&entities).expect("bulk insert failed");
assert_eq!(ids.len(), 100);
let deleted = db.bulk_delete_entities(&ids).expect("bulk delete failed");
assert_eq!(deleted, 100);
{
let tx = db.begin_read().expect("failed to begin read");
for id in &ids {
assert!(tx.get_entity(*id).expect("get failed").is_none());
}
}
}
#[test]
fn test_bulk_delete_partial() {
let db = Database::in_memory().expect("failed to create db");
let entities: Vec<Entity> = (0..100)
.map(|i| Entity::new(EntityId::new(0)).with_label("Item").with_property("index", i as i64))
.collect();
let ids = db.bulk_insert_entities(&entities).expect("bulk insert failed");
let to_delete: Vec<EntityId> = ids[0..50].to_vec();
let deleted = db.bulk_delete_entities(&to_delete).expect("bulk delete failed");
assert_eq!(deleted, 50);
{
let tx = db.begin_read().expect("failed to begin read");
for (i, id) in ids.iter().enumerate() {
let exists = tx.get_entity(*id).expect("get failed").is_some();
if i < 50 {
assert!(!exists, "entity at index {} should be deleted", i);
} else {
assert!(exists, "entity at index {} should exist", i);
}
}
}
}
#[test]
fn test_bulk_delete_nonexistent() {
let db = Database::in_memory().expect("failed to create db");
let ids = vec![EntityId::new(999), EntityId::new(1000), EntityId::new(1001)];
let deleted = db.bulk_delete_entities(&ids).expect("bulk delete failed");
assert_eq!(deleted, 0);
}
#[test]
fn test_bulk_delete_mixed_existing_nonexisting() {
let db = Database::in_memory().expect("failed to create db");
let entities: Vec<Entity> =
(0..5).map(|i| Entity::new(EntityId::new(0)).with_property("index", i as i64)).collect();
let ids = db.bulk_insert_entities(&entities).expect("bulk insert failed");
let to_delete = vec![
ids[0],
EntityId::new(9999), ids[2],
EntityId::new(9998), ids[4],
];
let deleted = db.bulk_delete_entities(&to_delete).expect("bulk delete failed");
assert_eq!(deleted, 3);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(ids[0]).expect("get").is_none());
assert!(tx.get_entity(ids[1]).expect("get").is_some());
assert!(tx.get_entity(ids[2]).expect("get").is_none());
assert!(tx.get_entity(ids[3]).expect("get").is_some());
assert!(tx.get_entity(ids[4]).expect("get").is_none());
}
}
#[test]
fn test_bulk_delete_cleans_property_indexes() {
let db = Database::in_memory().expect("failed to create db");
db.execute("CREATE TABLE products (sku TEXT, name TEXT)").expect("create table failed");
db.execute("CREATE INDEX idx_products_sku ON products (sku)").expect("create index failed");
db.execute("INSERT INTO products (sku, name) VALUES ('SKU-001', 'Widget')")
.expect("insert 1 failed");
db.execute("INSERT INTO products (sku, name) VALUES ('SKU-002', 'Gadget')")
.expect("insert 2 failed");
db.execute("INSERT INTO products (sku, name) VALUES ('SKU-003', 'Gizmo')")
.expect("insert 3 failed");
let result = db.query("SELECT * FROM products WHERE sku = 'SKU-001'").expect("query failed");
assert_eq!(result.len(), 1);
let entity_id = {
let tx = db.begin_read().expect("begin read");
let mut found_id = None;
for i in 1..=10 {
if let Some(entity) = tx.get_entity(EntityId::new(i)).expect("get entity") {
eprintln!("Entity {}: {:?}", i, entity.properties);
if entity.get_property("sku") == Some(&Value::String("SKU-001".to_string())) {
found_id = Some(entity.id);
eprintln!("Found SKU-001 at entity ID {}", i);
}
}
}
found_id.expect("entity with SKU-001 not found")
};
let deleted = db.bulk_delete_entities(&[entity_id]).expect("bulk delete failed");
assert_eq!(deleted, 1);
{
let tx = db.begin_read().expect("begin read");
eprintln!("After delete, checking entity {}:", entity_id.as_u64());
let entity_check = tx.get_entity(entity_id).expect("get entity");
eprintln!("Result: {:?}", entity_check);
assert!(entity_check.is_none(), "entity should be deleted");
}
let all_result = db.query("SELECT * FROM products").expect("query all failed");
assert_eq!(all_result.len(), 2, "should have 2 products after deleting 1");
let result = db.query("SELECT * FROM products WHERE sku = 'SKU-001'").expect("query failed");
for row in &result {
eprintln!("Found row: {:?}", row);
}
assert_eq!(result.len(), 0);
let result = db.query("SELECT * FROM products WHERE sku = 'SKU-002'").expect("query failed");
assert_eq!(result.len(), 1);
let result = db.query("SELECT * FROM products WHERE sku = 'SKU-003'").expect("query failed");
assert_eq!(result.len(), 1);
}
#[test]
fn test_bulk_delete_all_cleans_index() {
let db = Database::in_memory().expect("failed to create db");
db.execute("CREATE TABLE items (category TEXT)").expect("create table failed");
db.execute("CREATE INDEX idx_items_category ON items (category)").expect("create index failed");
db.execute("INSERT INTO items (category) VALUES ('electronics')").expect("insert 1 failed");
db.execute("INSERT INTO items (category) VALUES ('electronics')").expect("insert 2 failed");
db.execute("INSERT INTO items (category) VALUES ('clothing')").expect("insert 3 failed");
let result =
db.query("SELECT * FROM items WHERE category = 'electronics'").expect("query failed");
assert_eq!(result.len(), 2);
let entity_ids: Vec<EntityId> = {
let tx = db.begin_read().expect("begin read");
let mut ids = Vec::new();
for i in 1..=10 {
if tx.get_entity(EntityId::new(i)).expect("get entity").is_some() {
ids.push(EntityId::new(i));
}
}
ids
};
assert_eq!(entity_ids.len(), 3);
let deleted = db.bulk_delete_entities(&entity_ids).expect("bulk delete failed");
assert_eq!(deleted, 3);
let result =
db.query("SELECT * FROM items WHERE category = 'electronics'").expect("query failed");
assert_eq!(result.len(), 0);
let result = db.query("SELECT * FROM items WHERE category = 'clothing'").expect("query failed");
assert_eq!(result.len(), 0);
}
#[test]
fn test_bulk_delete_cascades_edges() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let person1 = tx.create_entity().expect("failed to create").with_label("Person");
let person2 = tx.create_entity().expect("failed to create").with_label("Person");
let person3 = tx.create_entity().expect("failed to create").with_label("Person");
tx.put_entity(&person1).expect("put failed");
tx.put_entity(&person2).expect("put failed");
tx.put_entity(&person3).expect("put failed");
let edge1 = tx.create_edge(person1.id, person2.id, "FOLLOWS").expect("create edge");
let edge2 = tx.create_edge(person2.id, person3.id, "FOLLOWS").expect("create edge");
let edge3 = tx.create_edge(person1.id, person3.id, "FOLLOWS").expect("create edge");
tx.put_edge(&edge1).expect("put edge");
tx.put_edge(&edge2).expect("put edge");
tx.put_edge(&edge3).expect("put edge");
tx.commit().expect("commit failed");
let deleted = db.bulk_delete_entities(&[person1.id]).expect("bulk delete failed");
assert_eq!(deleted, 1);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(person1.id).expect("get").is_none());
assert!(tx.get_entity(person2.id).expect("get").is_some());
assert!(tx.get_entity(person3.id).expect("get").is_some());
assert!(tx.get_edge(edge1.id).expect("get").is_none());
assert!(tx.get_edge(edge3.id).expect("get").is_none());
assert!(tx.get_edge(edge2.id).expect("get").is_some());
}
}
#[test]
fn test_bulk_delete_cascades_incoming_edges() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let person1 = tx.create_entity().expect("failed to create").with_label("Person");
let person2 = tx.create_entity().expect("failed to create").with_label("Person");
let person3 = tx.create_entity().expect("failed to create").with_label("Person");
tx.put_entity(&person1).expect("put failed");
tx.put_entity(&person2).expect("put failed");
tx.put_entity(&person3).expect("put failed");
let edge1 = tx.create_edge(person1.id, person2.id, "FOLLOWS").expect("create edge");
let edge2 = tx.create_edge(person3.id, person2.id, "FOLLOWS").expect("create edge");
tx.put_edge(&edge1).expect("put edge");
tx.put_edge(&edge2).expect("put edge");
tx.commit().expect("commit failed");
let deleted = db.bulk_delete_entities(&[person2.id]).expect("bulk delete failed");
assert_eq!(deleted, 1);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(person2.id).expect("get").is_none());
assert!(tx.get_entity(person1.id).expect("get").is_some());
assert!(tx.get_entity(person3.id).expect("get").is_some());
assert!(tx.get_edge(edge1.id).expect("get").is_none());
assert!(tx.get_edge(edge2.id).expect("get").is_none());
}
}
#[test]
fn test_bulk_delete_self_loop_edge() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity = tx.create_entity().expect("failed to create").with_label("Node");
tx.put_entity(&entity).expect("put failed");
let edge = tx.create_edge(entity.id, entity.id, "LINKS").expect("create edge");
tx.put_edge(&edge).expect("put edge");
tx.commit().expect("commit failed");
let deleted = db.bulk_delete_entities(&[entity.id]).expect("bulk delete failed");
assert_eq!(deleted, 1);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(entity.id).expect("get").is_none());
assert!(tx.get_edge(edge.id).expect("get").is_none());
}
}
#[test]
fn test_bulk_delete_checked_no_edges() {
let db = Database::in_memory().expect("failed to create db");
let entities: Vec<Entity> =
(0..3).map(|i| Entity::new(EntityId::new(0)).with_property("index", i as i64)).collect();
let ids = db.bulk_insert_entities(&entities).expect("bulk insert failed");
let deleted = db.bulk_delete_entities_checked(&ids).expect("bulk delete failed");
assert_eq!(deleted, 3);
{
let tx = db.begin_read().expect("failed to begin read");
for id in &ids {
assert!(tx.get_entity(*id).expect("get").is_none());
}
}
}
#[test]
fn test_bulk_delete_checked_with_edges_fails() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity1 = tx.create_entity().expect("failed to create");
let entity2 = tx.create_entity().expect("failed to create");
tx.put_entity(&entity1).expect("put failed");
tx.put_entity(&entity2).expect("put failed");
let edge = tx.create_edge(entity1.id, entity2.id, "LINKS").expect("create edge");
tx.put_edge(&edge).expect("put edge");
tx.commit().expect("commit failed");
let result = db.bulk_delete_entities_checked(&[entity1.id]);
assert!(result.is_err());
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(entity1.id).expect("get").is_some());
assert!(tx.get_entity(entity2.id).expect("get").is_some());
assert!(tx.get_edge(edge.id).expect("get").is_some());
}
}
#[test]
fn test_bulk_delete_checked_is_atomic() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let mut entities = Vec::with_capacity(5);
for _ in 0..5 {
let entity = tx.create_entity().expect("failed to create");
entities.push(entity.clone());
tx.put_entity(&entity).expect("put failed");
}
let edge = tx.create_edge(entities[2].id, entities[3].id, "LINKS").expect("create edge");
tx.put_edge(&edge).expect("put edge");
tx.commit().expect("commit failed");
let ids: Vec<EntityId> = entities.iter().map(|e| e.id).collect();
let result = db.bulk_delete_entities_checked(&ids);
assert!(result.is_err());
{
let tx = db.begin_read().expect("failed to begin read");
for id in &ids {
assert!(
tx.get_entity(*id).expect("get").is_some(),
"entity {} should still exist after atomic failure",
id.as_u64()
);
}
}
}
#[test]
fn test_bulk_delete_large_batch() {
let db = Database::in_memory().expect("failed to create db");
let entities: Vec<Entity> = (0..10_000)
.map(|i| Entity::new(EntityId::new(0)).with_label("Item").with_property("index", i as i64))
.collect();
let ids = db.bulk_insert_entities(&entities).expect("bulk insert failed");
let deleted = db.bulk_delete_entities(&ids).expect("bulk delete failed");
assert_eq!(deleted, 10_000);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(ids[0]).expect("get").is_none());
assert!(tx.get_entity(ids[5000]).expect("get").is_none());
assert!(tx.get_entity(ids[9999]).expect("get").is_none());
}
}
#[test]
fn test_bulk_delete_with_many_edges() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let center = tx.create_entity().expect("failed").with_label("Center");
tx.put_entity(¢er).expect("put failed");
for _ in 0..100 {
let leaf = tx.create_entity().expect("failed").with_label("Leaf");
tx.put_entity(&leaf).expect("put failed");
let edge = tx.create_edge(center.id, leaf.id, "CONNECTS").expect("create edge");
tx.put_edge(&edge).expect("put edge");
}
tx.commit().expect("commit failed");
let deleted = db.bulk_delete_entities(&[center.id]).expect("bulk delete failed");
assert_eq!(deleted, 1);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(center.id).expect("get").is_none());
let edges = tx.get_outgoing_edges(center.id).expect("get edges");
assert!(edges.is_empty());
}
}
#[test]
fn test_bulk_delete_preserves_id_sequence() {
let db = Database::in_memory().expect("failed to create db");
let entities: Vec<Entity> =
(0..5).map(|_| Entity::new(EntityId::new(0)).with_label("Item")).collect();
let ids = db.bulk_insert_entities(&entities).expect("bulk insert failed");
assert_eq!(ids[0].as_u64(), 1);
assert_eq!(ids[4].as_u64(), 5);
db.bulk_delete_entities(&ids).expect("bulk delete failed");
let more_entities: Vec<Entity> =
(0..3).map(|_| Entity::new(EntityId::new(0)).with_label("NewItem")).collect();
let new_ids = db.bulk_insert_entities(&more_entities).expect("bulk insert failed");
assert_eq!(new_ids[0].as_u64(), 6);
assert_eq!(new_ids[2].as_u64(), 8);
}
#[test]
fn test_delete_entity_cascades_to_vectors() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity = tx.create_entity().expect("failed to create").with_label("Document");
let entity_id = entity.id;
tx.put_entity(&entity).expect("put failed");
tx.commit().expect("commit failed");
let vectors = vec![
(entity_id, "text_embedding".to_string(), vec![0.1f32; 384]),
(entity_id, "image_embedding".to_string(), vec![0.2f32; 512]),
];
let inserted = db.bulk_insert_vectors("documents", &vectors).expect("insert vectors failed");
assert_eq!(inserted, 2);
let to_check = vec![(entity_id, "text_embedding".to_string())];
let check_count = db.bulk_delete_vectors(&to_check).expect("check failed");
assert_eq!(check_count, 1, "text_embedding should exist before entity delete");
let vectors = vec![(entity_id, "text_embedding".to_string(), vec![0.1f32; 384])];
db.bulk_insert_vectors("documents", &vectors).expect("re-insert failed");
let deleted = db.bulk_delete_entities(&[entity_id]).expect("bulk delete failed");
assert_eq!(deleted, 1);
{
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(entity_id).expect("get").is_none());
}
}
#[test]
fn test_delete_entity_cascades_multiple_named_vectors() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity = tx.create_entity().expect("failed to create").with_label("Multimodal");
let entity_id = entity.id;
tx.put_entity(&entity).expect("put failed");
tx.commit().expect("commit failed");
let vectors = vec![
(entity_id, "text".to_string(), vec![0.1f32; 128]),
(entity_id, "image".to_string(), vec![0.2f32; 256]),
(entity_id, "audio".to_string(), vec![0.3f32; 64]),
(entity_id, "video".to_string(), vec![0.4f32; 512]),
];
let inserted = db.bulk_insert_vectors("multimodal", &vectors).expect("insert failed");
assert_eq!(inserted, 4);
let deleted = db.bulk_delete_entities(&[entity_id]).expect("delete failed");
assert_eq!(deleted, 1);
let mut tx = db.begin().expect("failed to begin");
let new_entity = tx.create_entity().expect("failed to create").with_label("Fresh");
tx.put_entity(&new_entity).expect("put failed");
tx.commit().expect("commit failed");
let to_check = vec![(new_entity.id, "text".to_string())];
let count = db.bulk_delete_vectors(&to_check).expect("check failed");
assert_eq!(count, 0, "new entity should have no vectors");
}
#[test]
fn test_delete_multiple_entities_cascades_vectors() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let mut entity_ids = Vec::new();
for _ in 0..5 {
let entity = tx.create_entity().expect("failed to create").with_label("Document");
entity_ids.push(entity.id);
tx.put_entity(&entity).expect("put failed");
}
tx.commit().expect("commit failed");
let vectors: Vec<_> = entity_ids
.iter()
.flat_map(|&id| {
vec![
(id, "embedding_a".to_string(), vec![0.1f32; 128]),
(id, "embedding_b".to_string(), vec![0.2f32; 128]),
]
})
.collect();
let inserted = db.bulk_insert_vectors("documents", &vectors).expect("insert failed");
assert_eq!(inserted, 10);
let deleted = db.bulk_delete_entities(&entity_ids).expect("delete failed");
assert_eq!(deleted, 5);
let tx = db.begin_read().expect("failed to begin read");
for id in &entity_ids {
assert!(tx.get_entity(*id).expect("get").is_none());
}
}
#[test]
fn test_delete_entity_from_multiple_collections() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity = tx.create_entity().expect("failed to create").with_label("Shared");
let entity_id = entity.id;
tx.put_entity(&entity).expect("put failed");
tx.commit().expect("commit failed");
let vectors1 = vec![(entity_id, "text".to_string(), vec![0.1f32; 128])];
db.bulk_insert_vectors("collection_a", &vectors1).expect("insert a failed");
let vectors2 = vec![(entity_id, "image".to_string(), vec![0.2f32; 256])];
db.bulk_insert_vectors("collection_b", &vectors2).expect("insert b failed");
let vectors3 = vec![(entity_id, "audio".to_string(), vec![0.3f32; 64])];
db.bulk_insert_vectors("collection_c", &vectors3).expect("insert c failed");
let deleted = db.bulk_delete_entities(&[entity_id]).expect("delete failed");
assert_eq!(deleted, 1);
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(entity_id).expect("get").is_none());
}
#[test]
fn test_delete_entity_no_vectors_still_works() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity = tx.create_entity().expect("failed to create").with_label("NoVectors");
let entity_id = entity.id;
tx.put_entity(&entity).expect("put failed");
tx.commit().expect("commit failed");
let deleted = db.bulk_delete_entities(&[entity_id]).expect("delete failed");
assert_eq!(deleted, 1);
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(entity_id).expect("get").is_none());
}
#[test]
fn test_checked_delete_cascades_vectors_too() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity = tx.create_entity().expect("failed to create").with_label("Document");
let entity_id = entity.id;
tx.put_entity(&entity).expect("put failed");
tx.commit().expect("commit failed");
let vectors = vec![(entity_id, "embedding".to_string(), vec![0.1f32; 128])];
db.bulk_insert_vectors("documents", &vectors).expect("insert failed");
let deleted = db.bulk_delete_entities_checked(&[entity_id]).expect("checked delete failed");
assert_eq!(deleted, 1);
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(entity_id).expect("get").is_none());
}
#[test]
fn test_delete_preserves_other_entity_vectors() {
let db = Database::in_memory().expect("failed to create db");
let mut tx = db.begin().expect("failed to begin");
let entity1 = tx.create_entity().expect("failed to create").with_label("Document");
let entity2 = tx.create_entity().expect("failed to create").with_label("Document");
let entity1_id = entity1.id;
let entity2_id = entity2.id;
tx.put_entity(&entity1).expect("put failed");
tx.put_entity(&entity2).expect("put failed");
tx.commit().expect("commit failed");
let vectors = vec![
(entity1_id, "embedding".to_string(), vec![0.1f32; 128]),
(entity2_id, "embedding".to_string(), vec![0.2f32; 128]),
];
db.bulk_insert_vectors("documents", &vectors).expect("insert failed");
let deleted = db.bulk_delete_entities(&[entity1_id]).expect("delete failed");
assert_eq!(deleted, 1);
let tx = db.begin_read().expect("failed to begin read");
assert!(tx.get_entity(entity1_id).expect("get").is_none());
assert!(tx.get_entity(entity2_id).expect("get").is_some());
drop(tx);
let to_delete = vec![(entity2_id, "embedding".to_string())];
let count = db.bulk_delete_vectors(&to_delete).expect("delete failed");
assert_eq!(count, 1, "entity2's vector should still exist");
}