OptimizedFilter: Option<Vec<T>> -> Vec<T> (#9555)

This commit is contained in:
xzfc
2026-08-04 11:16:54 +02:00
committed by generall
parent ee152a7ec5
commit 4c87b9c4fc
2 changed files with 84 additions and 111 deletions
@@ -12,20 +12,16 @@ pub enum OptimizedCondition<'a> {
Filter(OptimizedFilter<'a>),
}
pub struct OptimizedMinShould<'a> {
pub conditions: Vec<OptimizedCondition<'a>>,
pub min_count: usize,
}
pub struct OptimizedFilter<'a> {
/// At least one of those conditions should match
pub should: Option<Vec<OptimizedCondition<'a>>>,
/// At least one of those conditions should match, if not empty.
pub should: Vec<OptimizedCondition<'a>>,
/// At least minimum amount of given conditions should match
pub min_should: Option<OptimizedMinShould<'a>>,
pub min_should: Vec<OptimizedCondition<'a>>,
pub min_should_count: usize,
/// All conditions must match
pub must: Option<Vec<OptimizedCondition<'a>>>,
pub must: Vec<OptimizedCondition<'a>>,
/// All conditions must NOT match
pub must_not: Option<Vec<OptimizedCondition<'a>>>,
pub must_not: Vec<OptimizedCondition<'a>>,
}
impl ConditionChecker for OptimizedFilter<'_> {
@@ -35,13 +31,14 @@ impl ConditionChecker for OptimizedFilter<'_> {
let OptimizedFilter {
should,
min_should,
min_should_count,
must,
must_not,
} = self;
// `should`: at least one matches.
if let Some(conditions) = should
&& !conditions
// `should`: at least one matches, if not empty.
if !should.is_empty()
&& !should
.iter()
.try_any(|condition| condition.check(point_id))?
{
@@ -49,41 +46,29 @@ impl ConditionChecker for OptimizedFilter<'_> {
}
// `min_should`: at least `min_count` match.
if let Some(min_should) = min_should {
let OptimizedMinShould {
conditions,
min_count,
} = min_should;
let mut matched = 0;
for condition in conditions {
if condition.check(point_id)? {
matched += 1;
if matched == *min_count {
break;
}
}
}
if matched < *min_count {
let mut remaining = *min_should_count;
let mut min_should_iter = min_should.iter();
while remaining > 0 {
let Some(condition) = min_should_iter.next() else {
// Not enough conditions to match `min_count`
return Ok(false);
};
if condition.check(point_id)? {
remaining -= 1;
}
}
// `must`: all match.
if let Some(conditions) = must {
for condition in conditions {
if !condition.check(point_id)? {
return Ok(false);
}
for condition in must {
if !condition.check(point_id)? {
return Ok(false);
}
}
// `must_not`: none match.
if let Some(conditions) = must_not {
for condition in conditions {
if condition.check(point_id)? {
return Ok(false);
}
for condition in must_not {
if condition.check(point_id)? {
return Ok(false);
}
}
@@ -12,9 +12,7 @@ use crate::index::query_estimator::{
combine_min_should_estimations, combine_must_estimations, combine_should_estimations,
invert_estimation,
};
use crate::index::query_optimization::optimized_filter::{
OptimizedCondition, OptimizedFilter, OptimizedMinShould,
};
use crate::index::query_optimization::optimized_filter::{OptimizedCondition, OptimizedFilter};
use crate::index::query_optimization::payload_provider::PayloadProvider;
use crate::payload_storage::PayloadStorageRead;
use crate::types::{Condition, Filter, MinShould};
@@ -55,78 +53,63 @@ where
hw_counter: &HardwareCounterCell,
) -> OperationResult<(OptimizedFilter<'b>, CardinalityEstimation)> {
let mut filter_estimations: Vec<CardinalityEstimation> = vec![];
let Filter {
should,
min_should,
must,
must_not,
} = filter;
let optimized_filter = OptimizedFilter {
should: if let Some(conditions) = filter.should.as_ref()
&& !conditions.is_empty()
{
let (optimized_conditions, estimation) = self.optimize_should(
conditions,
payload_provider.clone(),
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
Some(optimized_conditions)
} else {
None
},
// Keep an empty `min_should` when `min_count > 0`: it's
// unsatisfiable (match-none), and dropping it would match all
// (issue #9369). Only the no-op (empty, `min_count == 0`) is dropped.
min_should: if let Some(MinShould {
let (should, estimation) = self.optimize_should(
should.as_deref().unwrap_or(&[]),
payload_provider.clone(),
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
let (min_should, min_should_count) = match min_should.as_ref() {
Some(MinShould {
conditions,
min_count,
}) = filter.min_should.as_ref()
&& (!conditions.is_empty() || *min_count > 0)
{
let (optimized_conditions, estimation) = self.optimize_min_should(
conditions,
*min_count,
payload_provider.clone(),
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
Some(OptimizedMinShould {
conditions: optimized_conditions,
min_count: *min_count,
})
} else {
None
},
must: if let Some(conditions) = filter.must.as_ref()
&& !conditions.is_empty()
{
let (optimized_conditions, estimation) = self.optimize_must(
conditions,
payload_provider.clone(),
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
Some(optimized_conditions)
} else {
None
},
must_not: if let Some(conditions) = filter.must_not.as_ref()
&& !conditions.is_empty()
{
let (optimized_conditions, estimation) = self.optimize_must_not(
conditions,
payload_provider,
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
Some(optimized_conditions)
} else {
None
},
}) => (conditions.as_slice(), *min_count),
None => (&[][..], 0),
};
let (min_should, estimation) = self.optimize_min_should(
min_should,
min_should_count,
payload_provider.clone(),
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
let (must, estimation) = self.optimize_must(
must.as_deref().unwrap_or(&[]),
payload_provider.clone(),
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
let (must_not, estimation) = self.optimize_must_not(
must_not.as_deref().unwrap_or(&[]),
payload_provider,
total,
deferred_behavior,
hw_counter,
)?;
filter_estimations.push(estimation);
let optimized_filter = OptimizedFilter {
should,
min_should,
min_should_count,
must,
must_not,
};
Ok((
@@ -185,6 +168,11 @@ where
deferred_behavior: DeferredBehavior,
hw_counter: &HardwareCounterCell,
) -> OperationResult<(Vec<OptimizedCondition<'b>>, CardinalityEstimation)> {
if conditions.is_empty() {
// Empty `should` => match every point.
return Ok((Vec::new(), CardinalityEstimation::exact(total)));
}
let mut converted = self.convert_conditions(
conditions,
payload_provider,