diff --git a/filter-parser/src/lib.rs b/filter-parser/src/lib.rs index 8da3da35f..49eba1c61 100644 --- a/filter-parser/src/lib.rs +++ b/filter-parser/src/lib.rs @@ -112,6 +112,7 @@ impl<'a> From> for Token<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum FilterCondition<'a> { + Not(Box), Condition { fid: Token<'a>, op: Condition<'a> }, Or(Vec), And(Vec), @@ -148,24 +149,6 @@ impl<'a> FilterCondition<'a> { } } - pub fn negate(self) -> FilterCondition<'a> { - use FilterCondition::*; - - match self { - Condition { fid, op } => match op.negate() { - (op, None) => Condition { fid, op }, - (a, Some(b)) => Or(vec![ - Condition { fid: fid.clone(), op: a }.into(), - Condition { fid, op: b }.into(), - ]), - }, - Or(subfilters) => And(subfilters.into_iter().map(|x| x.negate().into()).collect()), - And(subfilters) => Or(subfilters.into_iter().map(|x| x.negate().into()).collect()), - GeoLowerThan { point, radius } => GeoGreaterThan { point, radius }, - GeoGreaterThan { point, radius } => GeoLowerThan { point, radius }, - } - } - pub fn parse(input: &'a str) -> Result, Error> { if input.trim().is_empty() { return Ok(None); @@ -219,7 +202,9 @@ fn parse_and(input: Span) -> IResult { /// If we parse a `NOT` we MUST parse something behind. fn parse_not(input: Span) -> IResult { alt(( - map(preceded(ws(tuple((tag("NOT"), multispace1))), cut(parse_not)), |e| e.negate()), + map(preceded(ws(tuple((tag("NOT"), multispace1))), cut(parse_not)), |e| { + FilterCondition::Not(Box::new(e)) + }), parse_primary, ))(input) } diff --git a/milli/src/search/facet/filter.rs b/milli/src/search/facet/filter.rs index 371cf975e..23917e4aa 100644 --- a/milli/src/search/facet/filter.rs +++ b/milli/src/search/facet/filter.rs @@ -360,6 +360,16 @@ impl<'a> Filter<'a> { filterable_fields: &HashSet, ) -> Result { match &self.condition { + FilterCondition::Not(f) => { + let all_ids = index.documents_ids(rtxn)?; + let selected = Self::inner_evaluate( + &(f.as_ref().clone()).into(), + rtxn, + index, + filterable_fields, + )?; + return Ok(all_ids - selected); + } FilterCondition::Condition { fid, op } => { if crate::is_faceted(fid.value(), filterable_fields) { let field_ids_map = index.fields_ids_map(rtxn)?;