use dashmap::DashMap;
use smallvec::{SmallVec, ToSmallVec};
use crate::common::{SortableFields, Value};
use crate::filter::{AndFilter, EqualsFilter, OrFilter};
use crate::{
collection::{FindOptions, FindPlan},
errors::{ErrorKind, NitriteError, NitriteResult},
filter::{
is_all_filter, is_and_filter, is_between_filter, is_equals_filter, is_or_filter,
is_text_filter, Filter, FilterProvider, IndexScanFilter,
},
index::IndexDescriptor,
SortOrder, DOC_ID,
};
use std::collections::{BTreeMap, HashMap};
use std::hash::{DefaultHasher, Hash, Hasher};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
type FilterVec = SmallVec<[Filter; 4]>;
#[derive(Clone)]
pub(crate) struct FindOptimizer {
inner: Arc<FindOptimizerInner>,
}
impl FindOptimizer {
pub fn new() -> Self {
FindOptimizer {
inner: Arc::new(FindOptimizerInner::new()),
}
}
pub fn create_find_plan(
&self,
filter: &Filter,
find_options: &FindOptions,
index_descriptors: &[IndexDescriptor],
) -> NitriteResult<FindPlan> {
self.inner.create_find_plan(filter, find_options, index_descriptors)
}
pub fn invalidate_cache(&self) {
self.inner.invalidate_cache();
}
pub fn invalidate_index_entries(&self, affected_index: &IndexDescriptor) {
self.inner.invalidate_index_entries(affected_index);
}
}
pub(crate) struct FindOptimizerInner {
query_cache: DashMap<u64, CachedPlan>,
cache_limit: usize,
last_index_version: AtomicU64,
}
struct CachedPlan {
plan: FindPlan,
used_index_descriptors: Vec<IndexDescriptor>,
}
impl FindOptimizerInner {
pub fn new() -> Self {
FindOptimizerInner {
query_cache: DashMap::new(),
cache_limit: 100,
last_index_version: AtomicU64::new(0),
}
}
pub fn create_find_plan(
&self,
filter: &Filter,
find_options: &FindOptions,
index_descriptors: &[IndexDescriptor],
) -> NitriteResult<FindPlan> {
let cache_key = self.compute_cache_key(filter, find_options);
if let Some(cached) = self.query_cache.get(&cache_key) {
let cached_plan = &cached.plan;
let all_indexes_valid = cached.used_index_descriptors.iter().all(|used_idx| {
index_descriptors.iter().any(|current_idx| {
current_idx.index_fields() == used_idx.index_fields() &&
current_idx.index_type() == used_idx.index_type()
})
});
if all_indexes_valid {
return Ok(cached_plan.clone());
}
self.query_cache.remove(&cache_key);
}
let mut find_plan = self.create_find_plan_internal(index_descriptors, filter)?;
self.read_sort_options(find_options, &mut find_plan)?;
self.read_limit_options(find_options, &mut find_plan)?;
if let Some(options) = find_options.collator_options {
find_plan.set_collator_options(options);
}
find_plan.set_distinct(find_options.distinct);
let mut used_indexes = Vec::new();
if let Some(idx) = find_plan.index_descriptor() {
used_indexes.push(idx.clone());
}
if let Some(sub_plans) = find_plan.sub_plans() {
for sub_plan in sub_plans {
if let Some(idx) = &sub_plan.index_descriptor() {
used_indexes.push(idx.clone());
}
}
}
if self.query_cache.len() < self.cache_limit {
let cached_plan = CachedPlan {
plan: find_plan.clone(),
used_index_descriptors: used_indexes,
};
self.query_cache.insert(cache_key, cached_plan);
}
Ok(find_plan)
}
pub fn invalidate_cache(&self) {
self.query_cache.clear();
self.last_index_version.fetch_add(1, Ordering::Relaxed);
}
pub fn invalidate_index_entries(&self, affected_index: &IndexDescriptor) {
self.query_cache.retain(|_, cached_plan| {
!cached_plan.used_index_descriptors.iter().any(|idx|
idx.index_fields() == affected_index.index_fields() &&
idx.index_type() == affected_index.index_type()
)
});
}
fn compute_cache_key(&self, filter: &Filter, find_options: &FindOptions) -> u64 {
let mut hasher = DefaultHasher::new();
self.last_index_version.load(Ordering::Relaxed).hash(&mut hasher);
filter.to_string().hash(&mut hasher);
if let Some(ref sort_by) = find_options.sort_by {
for (field, order) in sort_by.sorting_order() {
field.hash(&mut hasher);
(order as u8).hash(&mut hasher);
}
}
find_options.skip.hash(&mut hasher);
find_options.limit.hash(&mut hasher);
find_options.distinct.hash(&mut hasher);
hasher.finish()
}
fn create_find_plan_internal(
&self,
index_descriptors: &[IndexDescriptor],
filter: &Filter,
) -> NitriteResult<FindPlan> {
if is_and_filter(filter) {
let filters = self.flatten_and_filter(filter)?;
self.create_and_plan(index_descriptors, filters)
} else if is_or_filter(filter) {
let filters = SmallVec::from_vec(filter.logical_filters()?);
self.create_or_plan(index_descriptors, filters)
} else if is_between_filter(filter) {
let filters = SmallVec::from_vec(filter.logical_filters()?);
self.create_and_plan(index_descriptors, filters)
} else {
let mut filters = FilterVec::new();
filters.push(filter.clone());
self.create_and_plan(index_descriptors, filters)
}
}
fn flatten_and_filter(&self, filter: &Filter) -> NitriteResult<FilterVec> {
let and_filter = filter.as_any().downcast_ref::<AndFilter>()
.ok_or_else(|| NitriteError::new(
"Expected AndFilter but got different filter type",
ErrorKind::FilterError,
))?;
let logical_filters = and_filter.logical_filters()?;
let mut filters = FilterVec::new();
for f in logical_filters {
if is_and_filter(&f) {
filters.append(&mut self.flatten_and_filter(&f)?);
} else if is_between_filter(&f) {
filters.extend(f.logical_filters()?);
} else {
filters.push(f);
}
}
Ok(filters)
}
fn create_and_plan(
&self,
index_descriptors: &[IndexDescriptor],
filters: FilterVec,
) -> NitriteResult<FindPlan> {
let mut find_plan = FindPlan::new();
let mut index_scan_filters = FilterVec::new();
let mut full_scan_filters = FilterVec::new();
self.plan_id_filter(&mut find_plan, &filters)?;
if find_plan.by_id_filter().is_some() {
return Ok(find_plan);
}
self.plan_index_only_filter(
&mut find_plan,
&mut index_scan_filters,
index_descriptors,
&filters,
)?;
if index_scan_filters.is_empty() {
self.plan_index_scan_filter(
&mut find_plan,
&mut index_scan_filters,
index_descriptors,
&filters,
)?;
}
self.plan_full_scan_filter(
&mut find_plan,
&index_scan_filters,
&mut full_scan_filters,
&filters,
)?;
if index_scan_filters.len() == 1 {
find_plan.set_index_scan_filter(
IndexScanFilter::new(index_scan_filters.iter().cloned().collect()));
} else if index_scan_filters.len() > 1 {
find_plan.set_index_scan_filter(IndexScanFilter::new(index_scan_filters.to_vec()));
}
if full_scan_filters.len() == 1 {
find_plan.set_full_scan_filter(full_scan_filters[0].clone());
} else if full_scan_filters.len() > 1 {
find_plan.set_full_scan_filter(
Filter::new(AndFilter::new(full_scan_filters.to_vec())));
}
Ok(find_plan)
}
fn plan_id_filter(&self, find_plan: &mut FindPlan, filters: &[Filter]) -> NitriteResult<()> {
for filter in filters {
if is_equals_filter(filter) {
let equals_filter = filter.as_any().downcast_ref::<EqualsFilter>()
.ok_or_else(|| NitriteError::new(
"Failed to downcast filter to EqualsFilter",
ErrorKind::FilterError,
))?;
if equals_filter.get_field_name()? == DOC_ID {
find_plan.set_by_id_filter(filter.clone());
break;
}
}
}
Ok(())
}
fn plan_index_only_filter(
&self,
find_plan: &mut FindPlan,
index_scan_filters: &mut FilterVec,
index_descriptors: &[IndexDescriptor],
filters: &[Filter],
) -> NitriteResult<()> {
let mut io_filters = FilterVec::new();
for filter in filters {
if filter.is_index_only_filter() {
if self.can_be_grouped(filter, &io_filters)? {
io_filters.push(filter.clone());
} else {
log::error!("Cannot group index only filters");
return Err(NitriteError::new(
"Cannot group index only filters",
ErrorKind::FilterError,
));
}
}
}
if !io_filters.is_empty() {
let supported_type = {
let first_filter = &io_filters[0];
first_filter.supported_index_type()?
};
for index_descriptor in index_descriptors {
if supported_type == index_descriptor.index_type() {
find_plan.set_index_descriptor(index_descriptor.clone());
index_scan_filters.append(&mut io_filters);
break;
}
}
if find_plan.index_descriptor().is_none() {
log::error!("No index found for index only filter");
return Err(NitriteError::new(
"No index found for index only filter",
ErrorKind::FilterError,
));
}
}
Ok(())
}
fn can_be_grouped(&self, filter: &Filter, filters: &[Filter]) -> NitriteResult<bool> {
if filters.is_empty() {
return Ok(true);
}
let first_filter = &filters[0];
filter.can_be_grouped(first_filter.clone())
}
fn plan_index_scan_filter(
&self,
find_plan: &mut FindPlan,
index_scan_filters: &mut FilterVec,
index_descriptors: &[IndexDescriptor],
filters: &[Filter],
) -> NitriteResult<()> {
let mut index_filter_map = BTreeMap::new();
for index_descriptor in index_descriptors {
let fields_names = index_descriptor.index_fields().field_names();
let last_field_idx = fields_names.len().saturating_sub(1);
let mut index_filters = FilterVec::new();
for (field_idx, field_name) in fields_names.iter().enumerate() {
let is_terminal = field_idx == last_field_idx;
let mut matched = false;
for filter in filters {
if !filter.has_field() {
continue;
}
let name = filter.get_field_name()?;
if field_name == &name {
index_filters.push(filter.clone());
matched = true;
if !is_terminal {
break;
}
}
}
if !matched {
break;
}
}
if !index_filters.is_empty() {
index_filter_map.insert(index_descriptor.clone(), index_filters);
}
}
if let Some((best_descriptor, best_filters)) = index_filter_map
.into_iter()
.max_by_key(|(_, filters)| filters.len())
{
index_scan_filters.extend(best_filters);
find_plan.set_index_descriptor(best_descriptor);
}
Ok(())
}
fn plan_full_scan_filter(
&self,
find_plan: &mut FindPlan,
index_scan_filters: &[Filter],
full_scan_filters: &mut FilterVec,
filters: &[Filter],
) -> NitriteResult<()> {
for filter in filters {
if is_all_filter(filter) {
continue;
}
if !self.contains_filter(filter, index_scan_filters)? {
let mut eligible = false;
if let Some(by_id_filter) = &find_plan.by_id_filter() {
if !self.same_filter(filter, by_id_filter)? {
eligible = true;
}
} else {
eligible = true;
}
if eligible {
full_scan_filters.push(filter.clone());
}
}
}
if index_scan_filters.is_empty() {
for filter in full_scan_filters.iter() {
if filter.is_index_only_filter() {
log::error!("Index only filter {} cannot be used in full scan", filter);
return Err(NitriteError::new(
"Index only filter cannot be used in full scan",
ErrorKind::FilterError,
));
} else if is_text_filter(filter) {
log::error!("{} is not full text indexed", filter.get_field_name()?);
return Err(NitriteError::new(
&format!("{} is not full text indexed", filter.get_field_name()?),
ErrorKind::FilterError,
));
}
}
}
Ok(())
}
fn contains_filter(&self, filter: &Filter, filters: &[Filter]) -> NitriteResult<bool> {
for f in filters {
if self.same_filter(filter, f)? {
return Ok(true);
}
}
Ok(false)
}
fn same_filter(&self, filter1: &Filter, filter2: &Filter) -> NitriteResult<bool> {
let matcher1 = FilterMatcher::from_filter(filter1)?;
let matcher2 = FilterMatcher::from_filter(filter2)?;
Ok(matcher1.matches(&matcher2))
}
fn create_or_plan(
&self,
index_descriptors: &[IndexDescriptor],
filters: FilterVec,
) -> NitriteResult<FindPlan> {
let mut find_plan = FindPlan::new();
let mut flattened_filters = FilterVec::new();
for filter in &filters {
if is_or_filter(filter) {
let or_filter = filter.as_any().downcast_ref::<OrFilter>()
.ok_or_else(|| NitriteError::new(
"Expected OrFilter but got different filter type",
ErrorKind::FilterError,
))?;
let sub_filters = or_filter.logical_filters()?;
flattened_filters.extend(sub_filters);
} else {
flattened_filters.push(filter.clone());
}
}
for filter in flattened_filters {
let sub_plan = self.create_find_plan_internal(index_descriptors, &filter)?;
find_plan.add_sub_plan(sub_plan);
}
let mut clear = false;
for plan in find_plan.sub_plans().unwrap() {
if plan.index_descriptor().is_none() {
clear = true;
break;
}
}
if clear {
if let Some(_sub_plans) = find_plan.sub_plans() {
drop(_sub_plans);
}
find_plan.set_full_scan_filter(Filter::new(OrFilter::new(filters.to_vec())));
}
Ok(find_plan)
}
fn read_sort_options(
&self,
find_options: &FindOptions,
find_plan: &mut FindPlan,
) -> NitriteResult<()> {
if let Some(sort_by) = &find_options.sort_by {
find_plan.set_blocking_sort_order(sort_by.sorting_order());
}
Ok(())
}
fn read_limit_options(
&self,
find_options: &FindOptions,
find_plan: &mut FindPlan,
) -> NitriteResult<()> {
if let Some(skip) = find_options.skip {
find_plan.set_skip(skip);
}
if let Some(limit) = find_options.limit {
find_plan.set_limit(limit);
}
Ok(())
}
}
struct FilterMatcher {
field_name: Option<String>,
filter_type_id: std::any::TypeId,
filter_value: Option<Value>,
}
impl FilterMatcher {
fn from_filter(filter: &Filter) -> NitriteResult<Self> {
let field_name = if filter.has_field() {
Some(filter.get_field_name()?)
} else {
None
};
let filter_value = filter.get_field_value().ok().flatten();
Ok(Self {
field_name,
filter_type_id: filter.as_any().type_id(),
filter_value,
})
}
fn matches(&self, other: &FilterMatcher) -> bool {
if self.filter_type_id != other.filter_type_id {
return false;
}
match (&self.field_name, &other.field_name) {
(Some(name1), Some(name2)) if name1 != name2 => return false,
(Some(_), None) | (None, Some(_)) => return false,
_ => {}
}
match (&self.filter_value, &other.filter_value) {
(Some(val1), Some(val2)) if val1 != val2 => return false,
_ => {}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collection::{FindOptions, FindPlan};
use crate::common::{Fields, UNIQUE_INDEX};
use crate::filter::{and, field, or, Filter};
use crate::index::IndexDescriptor;
fn setup_find_optimizer() -> FindOptimizer {
FindOptimizer::new()
}
fn create_index_descriptor() -> IndexDescriptor {
IndexDescriptor::new(
UNIQUE_INDEX,
Fields::with_names(vec!["field"]).unwrap(),
"test_collection",
)
}
fn create_filter() -> Filter {
field("field").eq("value")
}
#[test]
fn test_create_find_plan() {
let optimizer = setup_find_optimizer();
let filter = create_filter();
let find_options = FindOptions::default();
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.create_find_plan(&filter, &find_options, &index_descriptors);
assert!(result.is_ok());
}
#[test]
fn test_create_find_plan_internal() {
let optimizer = setup_find_optimizer();
let filter = create_filter();
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_find_plan_internal(&index_descriptors, &filter);
assert!(result.is_ok());
}
#[test]
fn test_flatten_and_filter() {
let optimizer = setup_find_optimizer();
let filter = and(vec![create_filter(), create_filter()]);
let result = optimizer.inner.flatten_and_filter(&filter);
assert!(result.is_ok());
assert_eq!(result.unwrap().len(), 2);
}
#[test]
fn test_create_and_plan() {
let optimizer = setup_find_optimizer();
let filters = FilterVec::from_vec(vec![create_filter()]);
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_and_plan(&index_descriptors, filters);
assert!(result.is_ok());
}
#[test]
fn test_plan_id_filter() {
let optimizer = setup_find_optimizer();
let mut find_plan = FindPlan::new();
let filters = FilterVec::from_vec(vec![create_filter()]);
let result = optimizer.inner.plan_id_filter(&mut find_plan, &filters);
assert!(result.is_ok());
}
#[test]
fn test_plan_index_only_filter() {
let optimizer = setup_find_optimizer();
let mut find_plan = FindPlan::new();
let mut index_scan_filters = FilterVec::new();
let index_descriptors = vec![create_index_descriptor()];
let filters = FilterVec::from_vec(vec![create_filter()]);
let result = optimizer.inner.plan_index_only_filter(
&mut find_plan,
&mut index_scan_filters,
&index_descriptors,
&filters,
);
assert!(result.is_ok());
}
#[test]
fn test_can_be_grouped() {
let optimizer = setup_find_optimizer();
let filter = create_filter();
let filters = vec![create_filter()];
let result = optimizer.inner.can_be_grouped(&filter, &filters);
assert!(result.is_ok());
assert!(!result.unwrap());
}
#[test]
fn test_plan_index_scan_filter() {
let optimizer = setup_find_optimizer();
let mut find_plan = FindPlan::new();
let mut index_scan_filters = FilterVec::new();
let index_descriptors = vec![create_index_descriptor()];
let filters = FilterVec::from_vec(vec![create_filter()]);
let result = optimizer.inner.plan_index_scan_filter(
&mut find_plan,
&mut index_scan_filters,
&index_descriptors,
&filters,
);
assert!(result.is_ok());
}
#[test]
fn test_plan_full_scan_filter() {
let optimizer = setup_find_optimizer();
let mut find_plan = FindPlan::new();
let index_scan_filters = FilterVec::new();
let mut full_scan_filters = FilterVec::new();
let filters = FilterVec::from_vec(vec![create_filter()]);
let result = optimizer.inner.plan_full_scan_filter(
&mut find_plan,
&index_scan_filters,
&mut full_scan_filters,
&filters,
);
assert!(result.is_ok());
}
#[test]
fn test_contains_filter() {
let optimizer = setup_find_optimizer();
let filter = create_filter();
let filters = vec![create_filter()];
let result = optimizer.inner.contains_filter(&filter, &filters);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_same_filter() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let result = optimizer.inner.same_filter(&filter1, &filter2);
assert!(result.is_ok());
assert!(result.unwrap());
}
#[test]
fn test_create_or_plan() {
let optimizer = setup_find_optimizer();
let filters = FilterVec::from_vec(vec![create_filter()]);
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_or_plan(&index_descriptors, filters);
assert!(result.is_ok());
}
#[test]
fn test_read_sort_options() {
let optimizer = setup_find_optimizer();
let find_options = FindOptions::default();
let mut find_plan = FindPlan::new();
let result = optimizer.inner.read_sort_options(&find_options, &mut find_plan);
assert!(result.is_ok());
}
#[test]
fn test_read_limit_options() {
let optimizer = setup_find_optimizer();
let find_options = FindOptions::default();
let mut find_plan = FindPlan::new();
let result = optimizer.inner.read_limit_options(&find_options, &mut find_plan);
assert!(result.is_ok());
}
#[test]
fn test_flatten_and_filter_with_valid_and_filter() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let and_filter = and(vec![filter1, filter2]);
let result = optimizer.inner.flatten_and_filter(&and_filter);
assert!(result.is_ok());
let flattened = result.unwrap();
assert_eq!(flattened.len(), 2);
}
#[test]
fn test_flatten_and_filter_with_nested_and_filters() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let inner_and = and(vec![filter1, filter2]);
let filter3 = create_filter();
let outer_and = and(vec![inner_and, filter3]);
let result = optimizer.inner.flatten_and_filter(&outer_and);
assert!(result.is_ok());
let flattened = result.unwrap();
assert_eq!(flattened.len(), 3);
}
#[test]
fn test_flatten_and_filter_with_non_and_filter() {
let optimizer = setup_find_optimizer();
let regular_filter = create_filter();
let result = optimizer.inner.flatten_and_filter(®ular_filter);
assert!(result.is_err());
let error_msg = format!("{}", result.err().unwrap());
assert!(error_msg.contains("Expected AndFilter"));
}
#[test]
fn test_flatten_and_filter_preserves_non_and_sub_filters() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let and_filter = and(vec![filter1, filter2]);
let result = optimizer.inner.flatten_and_filter(&and_filter);
assert!(result.is_ok());
let flattened = result.unwrap();
assert!(flattened.iter().all(|f| !is_and_filter(f)));
}
#[test]
fn test_create_or_plan_with_valid_or_filter() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let or_filter = or(vec![filter1, filter2]);
let filters = FilterVec::from_vec(vec![or_filter]);
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_or_plan(&index_descriptors, filters);
assert!(result.is_ok());
}
#[test]
fn test_create_or_plan_with_multiple_or_filters() {
let optimizer = setup_find_optimizer();
let or_filter1 = or(vec![create_filter(), create_filter()]);
let or_filter2 = or(vec![create_filter(), create_filter()]);
let filters = FilterVec::from_vec(vec![or_filter1, or_filter2]);
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_or_plan(&index_descriptors, filters);
assert!(result.is_ok());
}
#[test]
fn test_create_or_plan_with_mixed_filters() {
let optimizer = setup_find_optimizer();
let or_filter = or(vec![create_filter(), create_filter()]);
let regular_filter = create_filter();
let filters = FilterVec::from_vec(vec![or_filter, regular_filter]);
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_or_plan(&index_descriptors, filters);
assert!(result.is_ok());
}
#[test]
fn test_create_or_plan_flattens_or_filters() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let or_filter = or(vec![filter1, filter2]);
let filters = FilterVec::from_vec(vec![or_filter]);
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_or_plan(&index_descriptors, filters);
assert!(result.is_ok());
let find_plan = result.unwrap();
assert!(find_plan.sub_plans().is_some());
}
#[test]
fn test_create_or_plan_with_no_or_filters() {
let optimizer = setup_find_optimizer();
let regular_filter = create_filter();
let filters = FilterVec::from_vec(vec![regular_filter]);
let index_descriptors = vec![create_index_descriptor()];
let result = optimizer.inner.create_or_plan(&index_descriptors, filters);
assert!(result.is_ok());
}
#[test]
fn test_create_or_plan_or_filter_type_safety() {
let optimizer = setup_find_optimizer();
let filter = create_filter();
if is_or_filter(&filter) {
let result = optimizer.inner.create_or_plan(
&[create_index_descriptor()],
FilterVec::from_vec(vec![filter]),
);
assert!(result.is_ok());
}
}
#[test]
fn test_flatten_and_filter_type_consistency() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let and_filter = and(vec![filter1, filter2]);
assert!(is_and_filter(&and_filter));
let result = optimizer.inner.flatten_and_filter(&and_filter);
assert!(result.is_ok());
}
#[test]
fn test_create_or_plan_type_consistency() {
let optimizer = setup_find_optimizer();
let filter1 = create_filter();
let filter2 = create_filter();
let or_filter = or(vec![filter1, filter2]);
assert!(is_or_filter(&or_filter));
let result = optimizer.inner.create_or_plan(
&[create_index_descriptor()],
FilterVec::from_vec(vec![or_filter]),
);
assert!(result.is_ok());
}
#[test]
fn test_sorting_aware_filter_creates_full_scan_filter() {
let optimizer = setup_find_optimizer();
let filter = field("emp_id").gt(5i64);
let find_options = FindOptions::default();
let index_descriptors: Vec<IndexDescriptor> = vec![];
let result = optimizer.create_find_plan(&filter, &find_options, &index_descriptors);
assert!(result.is_ok());
let find_plan = result.unwrap();
assert!(find_plan.full_scan_filter().is_some(), "full_scan_filter should be set for SortingAwareFilter");
assert!(find_plan.index_descriptor().is_none(), "No index should be used");
assert!(find_plan.index_scan_filter().is_none(), "No index scan filter should be used");
}
#[test]
fn test_equals_filter_creates_full_scan_filter_without_index() {
let optimizer = setup_find_optimizer();
let filter = field("emp_id").eq(5i64);
let find_options = FindOptions::default();
let index_descriptors: Vec<IndexDescriptor> = vec![];
let result = optimizer.create_find_plan(&filter, &find_options, &index_descriptors);
assert!(result.is_ok());
let find_plan = result.unwrap();
assert!(find_plan.full_scan_filter().is_some(), "full_scan_filter should be set for EqualsFilter without index");
}
}