Skip to content

Commit

Permalink
Added support for minimum_should_match in quickwit es API
Browse files Browse the repository at this point in the history
Closes #4828
  • Loading branch information
fulmicoton committed Oct 11, 2024
1 parent 1579235 commit b7a4984
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 2 deletions.
29 changes: 29 additions & 0 deletions quickwit/quickwit-query/src/elastic_query_dsl/bool_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct BoolQuery {
filter: Vec<ElasticQueryDslInner>,
#[serde(default)]
pub boost: Option<NotNaNf32>,
#[serde(default)]
pub minimum_should_match: Option<usize>,
}

impl BoolQuery {
Expand All @@ -57,6 +59,7 @@ impl BoolQuery {
should: children,
filter: Vec::new(),
boost: None,
minimum_should_match: None,
}
}
}
Expand All @@ -75,6 +78,7 @@ impl ConvertibleToQueryAst for BoolQuery {
must_not: convert_vec(self.must_not)?,
should: convert_vec(self.should)?,
filter: convert_vec(self.filter)?,
minimum_should_match: self.minimum_should_match,
};
Ok(bool_query_ast.into())
}
Expand All @@ -90,6 +94,8 @@ impl From<BoolQuery> for ElasticQueryDslInner {
mod tests {
use crate::elastic_query_dsl::bool_query::BoolQuery;
use crate::elastic_query_dsl::term_query::term_query_from_field_value;
use crate::elastic_query_dsl::ConvertibleToQueryAst;
use crate::query_ast::QueryAst;

#[test]
fn test_dsl_bool_query_deserialize_simple() {
Expand All @@ -111,6 +117,7 @@ mod tests {
should: Vec::new(),
filter: Vec::new(),
boost: None,
minimum_should_match: None
}
);
}
Expand All @@ -130,6 +137,7 @@ mod tests {
should: Vec::new(),
filter: vec![term_query_from_field_value("product_id", "2").into(),],
boost: None,
minimum_should_match: None,
}
);
}
Expand All @@ -152,7 +160,28 @@ mod tests {
should: Vec::new(),
filter: Vec::new(),
boost: None,
minimum_should_match: None,
}
);
}

#[test]
fn test_dsl_query_with_minimum_should_match() {
let bool_query_json = r#"{
"should": [
{ "term": {"product_id": {"value": "1" }} },
{ "term": {"product_id": {"value": "2" }} },
{ "term": {"product_id": {"value": "3" }} }
],
"minimum_should_match": 2
}"#;
let bool_query: BoolQuery = serde_json::from_str(bool_query_json).unwrap();
assert_eq!(bool_query.should.len(), 3);
assert_eq!(bool_query.minimum_should_match, Some(2));
let QueryAst::Bool(bool_query_ast) = bool_query.convert_to_query_ast().unwrap() else {
panic!();
};
assert_eq!(bool_query_ast.should.len(), 3);
assert_eq!(bool_query_ast.minimum_should_match, Some(2));
}
}
3 changes: 3 additions & 0 deletions quickwit/quickwit-query/src/query_ast/bool_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub struct BoolQuery {
pub should: Vec<QueryAst>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub filter: Vec<QueryAst>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub minimum_should_match: Option<usize>,
}

impl From<BoolQuery> for QueryAst {
Expand All @@ -65,6 +67,7 @@ impl BuildTantivyAst for BoolQuery {
with_validation: bool,
) -> Result<TantivyQueryAst, InvalidQuery> {
let mut boolean_query = super::tantivy_query_ast::TantivyBoolQuery::default();
boolean_query.minimum_should_match = self.minimum_should_match;
for must in &self.must {
let must_leaf = must.build_tantivy_ast_call(
schema,
Expand Down
2 changes: 2 additions & 0 deletions quickwit/quickwit-query/src/query_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ impl QueryAst {
must_not,
should,
filter,
minimum_should_match,
}) => {
let must = parse_user_query_in_asts(must, default_search_fields)?;
let must_not = parse_user_query_in_asts(must_not, default_search_fields)?;
Expand All @@ -92,6 +93,7 @@ impl QueryAst {
must_not,
should,
filter,
minimum_should_match,
}
.into())
}
Expand Down
12 changes: 10 additions & 2 deletions quickwit/quickwit-query/src/query_ast/tantivy_query_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.

use tantivy::query::{
AllQuery as TantivyAllQuery, ConstScoreQuery as TantivyConstScoreQuery,
AllQuery as TantivyAllQuery, BooleanQuery, ConstScoreQuery as TantivyConstScoreQuery,
EmptyQuery as TantivyEmptyQuery,
};
use tantivy::query_grammar::Occur;
Expand Down Expand Up @@ -160,6 +160,7 @@ pub(crate) struct TantivyBoolQuery {
pub must_not: Vec<TantivyQueryAst>,
pub should: Vec<TantivyQueryAst>,
pub filter: Vec<TantivyQueryAst>,
pub minimum_should_match: Option<usize>,
}

fn simplify_asts(asts: Vec<TantivyQueryAst>) -> Vec<TantivyQueryAst> {
Expand Down Expand Up @@ -360,7 +361,13 @@ impl From<TantivyBoolQuery> for Box<dyn TantivyQuery> {
Box::new(TantivyConstScoreQuery::new(filter_query, 0.0f32)),
));
}
Box::new(tantivy::query::BooleanQuery::from(clause))
let tantivy_bool_query = if let Some(minimum_should_match) = bool_query.minimum_should_match
{
BooleanQuery::with_minimum_required_clauses(clause, minimum_should_match)
} else {
BooleanQuery::from(clause)
};
Box::new(tantivy::query::BooleanQuery::from(tantivy_bool_query))
}
}

Expand Down Expand Up @@ -887,6 +894,7 @@ mod tests {
filter,
should,
must_not,
minimum_should_match: None,
})
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ fn build_request_for_es_api(
must_not: Vec::new(),
should: Vec::new(),
filter: queries,
minimum_should_match: None,
});
}

Expand Down

0 comments on commit b7a4984

Please sign in to comment.