#[cfg(test)]
mod tests {
use crate::collection::types::Collection;
use crate::DistanceMetric;
use tempfile::TempDir;
fn create_test_collection() -> (Collection, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let collection =
Collection::create(temp_dir.path().to_path_buf(), 128, DistanceMetric::Cosine)
.expect("Failed to create collection");
(collection, temp_dir)
}
#[test]
fn test_create_property_index_success() {
let (collection, _temp) = create_test_collection();
let result = collection.create_property_index("Person", "email");
assert!(result.is_ok());
assert!(collection.has_property_index("Person", "email"));
}
#[test]
fn test_create_property_index_idempotent() {
let (collection, _temp) = create_test_collection();
collection.create_property_index("Person", "email").unwrap();
let result = collection.create_property_index("Person", "email");
assert!(result.is_ok());
assert!(collection.has_property_index("Person", "email"));
}
#[test]
fn test_create_range_index_success() {
let (collection, _temp) = create_test_collection();
let result = collection.create_range_index("Event", "timestamp");
assert!(result.is_ok());
assert!(collection.has_range_index("Event", "timestamp"));
}
#[test]
fn test_create_range_index_idempotent() {
let (collection, _temp) = create_test_collection();
collection.create_range_index("Event", "timestamp").unwrap();
let result = collection.create_range_index("Event", "timestamp");
assert!(result.is_ok());
assert!(collection.has_range_index("Event", "timestamp"));
}
#[test]
fn test_has_property_index_false_when_not_exists() {
let (collection, _temp) = create_test_collection();
assert!(!collection.has_property_index("NonExistent", "field"));
}
#[test]
fn test_has_range_index_false_when_not_exists() {
let (collection, _temp) = create_test_collection();
assert!(!collection.has_range_index("NonExistent", "field"));
}
#[test]
fn test_list_indexes_empty_initially() {
let (collection, _temp) = create_test_collection();
let indexes = collection.list_indexes();
assert!(indexes.is_empty());
}
#[test]
fn test_list_indexes_with_property_index() {
let (collection, _temp) = create_test_collection();
collection.create_property_index("Person", "email").unwrap();
let indexes = collection.list_indexes();
assert_eq!(indexes.len(), 1);
assert_eq!(indexes[0].label, "Person");
assert_eq!(indexes[0].property, "email");
assert_eq!(indexes[0].index_type, "hash");
}
#[test]
fn test_list_indexes_with_range_index() {
let (collection, _temp) = create_test_collection();
collection.create_range_index("Event", "timestamp").unwrap();
let indexes = collection.list_indexes();
assert_eq!(indexes.len(), 1);
assert_eq!(indexes[0].label, "Event");
assert_eq!(indexes[0].property, "timestamp");
assert_eq!(indexes[0].index_type, "range");
}
#[test]
fn test_list_indexes_mixed() {
let (collection, _temp) = create_test_collection();
collection.create_property_index("Person", "email").unwrap();
collection.create_range_index("Event", "timestamp").unwrap();
let indexes = collection.list_indexes();
assert_eq!(indexes.len(), 2);
let has_hash = indexes.iter().any(|i| i.index_type == "hash");
let has_range = indexes.iter().any(|i| i.index_type == "range");
assert!(has_hash);
assert!(has_range);
}
#[test]
fn test_drop_index_property_success() {
let (collection, _temp) = create_test_collection();
collection.create_property_index("Person", "email").unwrap();
assert!(collection.has_property_index("Person", "email"));
let result = collection.drop_index("Person", "email");
assert!(result.is_ok());
assert!(result.unwrap());
assert!(!collection.has_property_index("Person", "email"));
}
#[test]
fn test_drop_index_range_success() {
let (collection, _temp) = create_test_collection();
collection.create_range_index("Event", "timestamp").unwrap();
assert!(collection.has_range_index("Event", "timestamp"));
let result = collection.drop_index("Event", "timestamp");
assert!(result.is_ok());
assert!(result.unwrap());
assert!(!collection.has_range_index("Event", "timestamp"));
}
#[test]
fn test_drop_index_returns_false_when_not_exists() {
let (collection, _temp) = create_test_collection();
let result = collection.drop_index("NonExistent", "field");
assert!(result.is_ok());
assert!(!result.unwrap()); }
#[test]
fn test_indexes_memory_usage_initial() {
let (collection, _temp) = create_test_collection();
let memory = collection.indexes_memory_usage();
let _ = memory;
}
#[test]
fn test_indexes_memory_usage_after_creation() {
let (collection, _temp) = create_test_collection();
let initial_memory = collection.indexes_memory_usage();
collection.create_property_index("Person", "email").unwrap();
collection.create_range_index("Event", "timestamp").unwrap();
let after_memory = collection.indexes_memory_usage();
assert!(after_memory >= initial_memory);
}
#[test]
fn test_build_prefilter_bitmap_returns_none_without_index() {
let (collection, _temp) = create_test_collection();
let filter = crate::filter::Filter::new(crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_none(), "no secondary index => no bitmap");
}
#[test]
fn test_build_prefilter_bitmap_returns_bitmap_with_index() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
{
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, 5, 10],
);
}
}
let filter = crate::filter::Filter::new(crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "indexed field should produce a bitmap");
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 3);
assert!(bm.contains(1));
assert!(bm.contains(5));
assert!(bm.contains(10));
}
#[test]
fn test_build_prefilter_bitmap_and_intersection() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
collection
.create_index("status")
.expect("test: index creation");
{
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, 5, 10, 20],
);
}
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("status") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("active".to_string()),
vec![5, 10, 30],
);
}
}
let filter = crate::filter::Filter::new(crate::filter::Condition::And {
conditions: vec![
crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
},
crate::filter::Condition::Eq {
field: "status".to_string(),
value: serde_json::Value::String("active".to_string()),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"AND of indexed fields should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 2);
assert!(bm.contains(5));
assert!(bm.contains(10));
}
fn populate_price_index(collection: &Collection) {
use crate::index::secondary::F64Key;
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("price") {
let mut t = tree.write();
for (price, id) in [(10, 1u64), (20, 2), (30, 3), (40, 4), (50, 5)] {
t.insert(
crate::index::JsonValue::Number(F64Key::from(f64::from(price))),
vec![id],
);
}
}
}
#[test]
fn test_build_prefilter_bitmap_gt() {
let (collection, _temp) = create_test_collection();
collection
.create_index("price")
.expect("test: index creation");
populate_price_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Gt {
field: "price".to_string(),
value: serde_json::json!(30),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"Gt on indexed field should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 2);
assert!(bm.contains(4));
assert!(bm.contains(5));
}
#[test]
fn test_build_prefilter_bitmap_gte() {
let (collection, _temp) = create_test_collection();
collection
.create_index("price")
.expect("test: index creation");
populate_price_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Gte {
field: "price".to_string(),
value: serde_json::json!(30),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"Gte on indexed field should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 3);
assert!(bm.contains(3));
assert!(bm.contains(4));
assert!(bm.contains(5));
}
#[test]
fn test_build_prefilter_bitmap_lt() {
let (collection, _temp) = create_test_collection();
collection
.create_index("price")
.expect("test: index creation");
populate_price_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Lt {
field: "price".to_string(),
value: serde_json::json!(30),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"Lt on indexed field should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 2);
assert!(bm.contains(1));
assert!(bm.contains(2));
}
#[test]
fn test_build_prefilter_bitmap_lte() {
let (collection, _temp) = create_test_collection();
collection
.create_index("price")
.expect("test: index creation");
populate_price_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Lte {
field: "price".to_string(),
value: serde_json::json!(30),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"Lte on indexed field should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 3);
assert!(bm.contains(1));
assert!(bm.contains(2));
assert!(bm.contains(3));
}
#[test]
fn test_build_prefilter_bitmap_between_via_and() {
let (collection, _temp) = create_test_collection();
collection
.create_index("price")
.expect("test: index creation");
populate_price_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::And {
conditions: vec![
crate::filter::Condition::Gte {
field: "price".to_string(),
value: serde_json::json!(20),
},
crate::filter::Condition::Lte {
field: "price".to_string(),
value: serde_json::json!(40),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"BETWEEN on indexed field should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 3);
assert!(bm.contains(2));
assert!(bm.contains(3));
assert!(bm.contains(4));
}
#[test]
fn test_build_prefilter_bitmap_range_no_index() {
let (collection, _temp) = create_test_collection();
let filter = crate::filter::Filter::new(crate::filter::Condition::Gt {
field: "price".to_string(),
value: serde_json::json!(100),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_none(), "no index => None for range condition");
}
#[test]
fn test_build_prefilter_bitmap_range_empty_result() {
let (collection, _temp) = create_test_collection();
collection
.create_index("price")
.expect("test: index creation");
populate_price_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Gt {
field: "price".to_string(),
value: serde_json::json!(999),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "indexed field should produce bitmap");
let bm = bitmap.unwrap();
assert!(bm.is_empty(), "no matches => empty bitmap");
}
#[test]
fn test_build_prefilter_bitmap_or_union() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
{
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, 5],
);
t.insert(
crate::index::JsonValue::String("science".to_string()),
vec![3, 7],
);
}
}
let filter = crate::filter::Filter::new(crate::filter::Condition::Or {
conditions: vec![
crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
},
crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("science".to_string()),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"OR of indexed fields should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 4);
assert!(bm.contains(1));
assert!(bm.contains(3));
assert!(bm.contains(5));
assert!(bm.contains(7));
}
#[test]
fn test_build_prefilter_bitmap_or_with_non_indexed_child() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
{
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, 5],
);
}
}
let filter = crate::filter::Filter::new(crate::filter::Condition::Or {
conditions: vec![
crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
},
crate::filter::Condition::Eq {
field: "unindexed_field".to_string(),
value: serde_json::Value::String("value".to_string()),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_none(),
"OR with non-indexed child must return None"
);
}
#[test]
fn test_build_prefilter_bitmap_not_returns_none() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
let filter = crate::filter::Filter::new(crate::filter::Condition::Not {
condition: Box::new(crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
}),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_none(), "NOT returns None (needs universe bitmap)");
}
#[test]
fn test_build_prefilter_bitmap_neq_uses_universe_subtraction() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
{
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, 5, 10],
);
t.insert(
crate::index::JsonValue::String("science".to_string()),
vec![2, 7],
);
}
}
let filter = crate::filter::Filter::new(crate::filter::Condition::Neq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "Neq should return Some (universe - eq)");
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 2, "Neq should exclude tech points");
assert!(bm.contains(2));
assert!(bm.contains(7));
}
#[test]
fn test_build_prefilter_bitmap_and_with_range_and_eq() {
let (collection, _temp) = create_test_collection();
collection
.create_index("price")
.expect("test: index creation");
collection
.create_index("category")
.expect("test: index creation");
populate_price_index(&collection);
{
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, 2, 3, 4, 5],
);
}
}
let filter = crate::filter::Filter::new(crate::filter::Condition::And {
conditions: vec![
crate::filter::Condition::Gt {
field: "price".to_string(),
value: serde_json::json!(20),
},
crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"AND of indexed range+eq should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 3);
assert!(bm.contains(3));
assert!(bm.contains(4));
assert!(bm.contains(5));
}
#[test]
fn test_build_prefilter_bitmap_empty_for_missing_value() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
let filter = crate::filter::Filter::new(crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("nonexistent".to_string()),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "indexed field should produce a bitmap");
let bm = bitmap.unwrap();
assert!(bm.is_empty(), "no entries => empty bitmap");
}
#[test]
fn test_index_info_struct() {
use crate::collection::core::index_management::IndexInfo;
let info = IndexInfo {
label: "Test".to_string(),
property: "field".to_string(),
index_type: "hash".to_string(),
cardinality: 100,
memory_bytes: 1024,
};
assert_eq!(info.label, "Test");
assert_eq!(info.property, "field");
assert_eq!(info.index_type, "hash");
assert_eq!(info.cardinality, 100);
assert_eq!(info.memory_bytes, 1024);
let cloned = info.clone();
assert_eq!(cloned.label, info.label);
let debug_str = format!("{:?}", info);
assert!(debug_str.contains("IndexInfo"));
}
fn populate_category_index(collection: &Collection) {
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, 5, 10],
);
t.insert(
crate::index::JsonValue::String("science".to_string()),
vec![2, 7],
);
t.insert(crate::index::JsonValue::String("art".to_string()), vec![3]);
}
}
#[test]
fn test_in_bitmap_empty_list_returns_empty() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
let filter = crate::filter::Filter::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "empty IN list should return Some(empty)");
assert!(bitmap.unwrap().is_empty(), "empty IN list => empty bitmap");
}
#[test]
fn test_in_bitmap_no_index_returns_none() {
let (collection, _temp) = create_test_collection();
let filter = crate::filter::Filter::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![serde_json::Value::String("tech".to_string())],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_none(), "no secondary index => None");
}
#[test]
fn test_in_bitmap_nonexistent_values_skipped() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![
serde_json::Value::String("nonexistent".to_string()),
serde_json::Value::String("also_missing".to_string()),
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"indexed field should produce bitmap even with missing values"
);
assert!(
bitmap.unwrap().is_empty(),
"nonexistent values => empty bitmap"
);
}
#[test]
fn test_in_bitmap_single_value_matches_eq() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let in_filter = crate::filter::Filter::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![serde_json::Value::String("tech".to_string())],
});
let eq_filter = crate::filter::Filter::new(crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
});
let in_bm = collection.build_prefilter_bitmap(&in_filter).unwrap();
let eq_bm = collection.build_prefilter_bitmap(&eq_filter).unwrap();
assert_eq!(in_bm, eq_bm, "IN(single) should equal Eq bitmap");
}
#[test]
fn test_in_bitmap_multiple_values_union() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![
serde_json::Value::String("tech".to_string()),
serde_json::Value::String("science".to_string()),
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"IN on indexed field should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 5);
assert!(bm.contains(1));
assert!(bm.contains(2));
assert!(bm.contains(5));
assert!(bm.contains(7));
assert!(bm.contains(10));
}
#[test]
fn test_in_bitmap_mixed_existing_missing_values() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![
serde_json::Value::String("tech".to_string()),
serde_json::Value::String("nonexistent".to_string()),
serde_json::Value::String("art".to_string()),
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some());
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 4);
assert!(bm.contains(1));
assert!(bm.contains(3));
assert!(bm.contains(5));
assert!(bm.contains(10));
}
#[test]
fn test_in_bitmap_with_u64_overflow_ids() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
let large_id = u64::from(u32::MAX) + 1;
{
let indexes = collection.secondary_indexes.read();
if let Some(crate::index::SecondaryIndex::BTree(tree)) = indexes.get("category") {
let mut t = tree.write();
t.insert(
crate::index::JsonValue::String("tech".to_string()),
vec![1, large_id, 5],
);
}
}
let filter = crate::filter::Filter::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![serde_json::Value::String("tech".to_string())],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some());
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 2);
assert!(bm.contains(1));
assert!(bm.contains(5));
}
#[test]
fn test_not_in_bitmap_returns_universe_minus_in() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Not {
condition: Box::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![
serde_json::Value::String("tech".to_string()),
serde_json::Value::String("science".to_string()),
],
}),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_some(),
"NOT IN on indexed field should produce bitmap"
);
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 1);
assert!(bm.contains(3), "only art ID=3 should remain");
}
#[test]
fn test_not_in_bitmap_no_index_returns_none() {
let (collection, _temp) = create_test_collection();
let filter = crate::filter::Filter::new(crate::filter::Condition::Not {
condition: Box::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![serde_json::Value::String("tech".to_string())],
}),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_none(), "no secondary index => None");
}
#[test]
fn test_not_in_empty_list_returns_universe() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Not {
condition: Box::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![],
}),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "NOT IN () should return full universe");
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 6);
assert!(bm.contains(1));
assert!(bm.contains(2));
assert!(bm.contains(3));
assert!(bm.contains(5));
assert!(bm.contains(7));
assert!(bm.contains(10));
}
#[test]
fn test_not_in_all_values_returns_empty() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Not {
condition: Box::new(crate::filter::Condition::In {
field: "category".to_string(),
values: vec![
serde_json::Value::String("tech".to_string()),
serde_json::Value::String("science".to_string()),
serde_json::Value::String("art".to_string()),
],
}),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "NOT IN (all) should return Some(empty)");
assert!(
bitmap.unwrap().is_empty(),
"excluding all values => empty bitmap"
);
}
#[test]
fn test_not_wrapping_non_in_returns_none() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Not {
condition: Box::new(crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("tech".to_string()),
}),
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_none(), "Not wrapping Eq should return None");
}
#[test]
fn test_and_with_in_and_eq_intersects() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::And {
conditions: vec![
crate::filter::Condition::In {
field: "category".to_string(),
values: vec![
serde_json::Value::String("tech".to_string()),
serde_json::Value::String("science".to_string()),
],
},
crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("art".to_string()),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "AND(In, Eq) on indexed field => Some");
assert!(
bitmap.unwrap().is_empty(),
"no overlap between IN and Eq => empty bitmap"
);
}
#[test]
fn test_and_with_in_and_range_intersects() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
collection
.create_index("price")
.expect("test: index creation");
populate_category_index(&collection);
populate_price_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::And {
conditions: vec![
crate::filter::Condition::In {
field: "category".to_string(),
values: vec![
serde_json::Value::String("tech".to_string()),
serde_json::Value::String("science".to_string()),
],
},
crate::filter::Condition::Gte {
field: "price".to_string(),
value: serde_json::json!(5),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "AND(In, Gte) on indexed fields => Some");
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 3);
assert!(bm.contains(1));
assert!(bm.contains(2));
assert!(bm.contains(5));
}
#[test]
fn test_or_with_in_and_eq_unions() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Or {
conditions: vec![
crate::filter::Condition::In {
field: "category".to_string(),
values: vec![serde_json::Value::String("tech".to_string())],
},
crate::filter::Condition::Eq {
field: "category".to_string(),
value: serde_json::Value::String("science".to_string()),
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(bitmap.is_some(), "OR(In, Eq) on indexed field => Some");
let bm = bitmap.unwrap();
assert_eq!(bm.len(), 5);
assert!(bm.contains(1));
assert!(bm.contains(2));
assert!(bm.contains(5));
assert!(bm.contains(7));
assert!(bm.contains(10));
}
#[test]
fn test_or_with_in_unindexed_returns_none() {
let (collection, _temp) = create_test_collection();
collection
.create_index("category")
.expect("test: index creation");
populate_category_index(&collection);
let filter = crate::filter::Filter::new(crate::filter::Condition::Or {
conditions: vec![
crate::filter::Condition::In {
field: "category".to_string(),
values: vec![serde_json::Value::String("tech".to_string())],
},
crate::filter::Condition::In {
field: "tags".to_string(),
values: vec![serde_json::Value::String("rust".to_string())],
},
],
});
let bitmap = collection.build_prefilter_bitmap(&filter);
assert!(
bitmap.is_none(),
"OR with unindexed IN child must return None"
);
}
}