use crate::query::ast::{ScoringExpression, SpanExpression};
use crate::query::boolean::BoolQuery;
use crate::query::constant_score::ConstantScoreQuery;
use crate::query::exists::ExistsQuery;
use crate::query::match_query::MatchQuery;
use crate::query::phrase::MatchPhraseQuery;
use crate::query::prefix::PrefixQuery;
use crate::query::range::RangeQuery;
use crate::query::span::{SpanFirstQuery, SpanNearQuery, SpanNotQuery, SpanTermQuery};
use crate::query::term::TermQuery;
use crate::query::{Query, SpanQuery};
#[allow(dead_code)] pub(crate) fn ast_to_query(ast: &ScoringExpression) -> Box<dyn Query> {
match ast {
ScoringExpression::Term { field, value } => Box::new(TermQuery {
field: field.clone(),
value: value.clone(),
}),
ScoringExpression::Terms { field, values } => {
let should: Vec<Box<dyn Query>> = values
.iter()
.map(|v| -> Box<dyn Query> {
Box::new(TermQuery {
field: field.clone(),
value: v.clone(),
})
})
.collect();
Box::new(BoolQuery {
must: vec![],
should,
must_not: vec![],
filter: vec![],
minimum_should_match: None,
})
}
ScoringExpression::Match {
field,
query,
analyzer,
} => Box::new(MatchQuery {
field: field.clone(),
query_text: query.clone(),
analyzer: analyzer.clone(),
}),
ScoringExpression::MatchPhrase {
field,
query,
analyzer,
} => Box::new(MatchPhraseQuery {
field: field.clone(),
query_text: query.clone(),
analyzer: analyzer.clone(),
}),
ScoringExpression::Bool {
must,
should,
must_not,
filter,
minimum_should_match,
} => Box::new(BoolQuery {
must: must.iter().map(ast_to_query).collect(),
should: should.iter().map(ast_to_query).collect(),
must_not: must_not.iter().map(ast_to_query).collect(),
filter: filter.iter().map(ast_to_query).collect(),
minimum_should_match: *minimum_should_match,
}),
ScoringExpression::MatchBoolPrefix {
field,
query,
analyzer,
} => {
Box::new(crate::query::match_query::MatchBoolPrefixQuery {
field: field.clone(),
query_text: query.clone(),
analyzer: analyzer.clone(),
})
}
ScoringExpression::DisMax {
queries,
tie_breaker,
} => Box::new(crate::query::dis_max::DisMaxQuery {
queries: queries.iter().map(ast_to_query).collect(),
tie_breaker: *tie_breaker,
}),
ScoringExpression::Exists { field } => Box::new(ExistsQuery {
field: field.clone(),
}),
ScoringExpression::Prefix { field, value } => Box::new(PrefixQuery {
field: field.clone(),
value: value.clone(),
}),
ScoringExpression::ConstantScore { query, boost } => Box::new(ConstantScoreQuery {
inner: ast_to_query(query),
boost: *boost,
}),
ScoringExpression::Nested {
path,
query,
inner_hits,
} => Box::new(crate::query::nested::NestedQuery {
path: path.clone(),
inner: ast_to_query(query),
inner_hits: inner_hits.clone(),
}),
ScoringExpression::GeoDistance {
field,
lat,
lon,
distance,
} => {
let distance_km = crate::query::parser::parse_distance_km(distance);
Box::new(crate::spatial::query::GeoDistanceQuery {
field: field.clone(),
center: crate::spatial::geo::GeoPoint::new(*lat, *lon),
distance_km,
})
}
ScoringExpression::GeoBoundingBox {
field,
top_left_lat,
top_left_lon,
bottom_right_lat,
bottom_right_lon,
} => Box::new(crate::spatial::query::GeoBoundingBoxQuery {
field: field.clone(),
top_left: crate::spatial::geo::GeoPoint::new(*top_left_lat, *top_left_lon),
bottom_right: crate::spatial::geo::GeoPoint::new(*bottom_right_lat, *bottom_right_lon),
}),
ScoringExpression::GeoShape {
field,
shape,
relation,
} => {
let query_geom = crate::spatial::shape::parse_geojson(&shape.json)
.unwrap_or(::geo::Geometry::Point(::geo::Point::new(0.0, 0.0)));
let query_bbox =
crate::spatial::shape::compute_bbox(&query_geom).unwrap_or((0.0, 0.0, 0.0, 0.0));
Box::new(crate::spatial::query::GeoShapeQuery {
field: field.clone(),
query_shape: query_geom,
query_bbox,
relation: relation.clone(),
})
}
ScoringExpression::Range {
field,
gte,
gt,
lte,
lt,
} => Box::new(RangeQuery {
field: field.clone(),
gte: *gte,
gt: *gt,
lte: *lte,
lt: *lt,
}),
ScoringExpression::Boost { query, boost } => Box::new(crate::query::boost::BoostQuery {
inner: ast_to_query(query),
boost: *boost,
}),
ScoringExpression::ScriptScore {
query,
script,
params,
} => Box::new(crate::query::script_score::ScriptScoreQuery {
query: ast_to_query(query),
script: script.clone(),
params: params.clone(),
}),
ScoringExpression::FunctionScore {
query,
functions,
score_mode,
boost_mode,
} => Box::new(crate::query::function_score::FunctionScoreQuery {
query: ast_to_query(query),
functions: functions.clone(),
score_mode: score_mode.clone(),
boost_mode: boost_mode.clone(),
}),
ScoringExpression::Boosting {
positive,
negative,
negative_boost,
} => Box::new(crate::query::boosting::BoostingQuery {
positive: ast_to_query(positive),
negative: ast_to_query(negative),
negative_boost: *negative_boost,
}),
ScoringExpression::Fuzzy {
field,
value,
fuzziness,
} => Box::new(crate::query::fuzzy::FuzzyQuery {
field: field.clone(),
value: value.clone(),
fuzziness: *fuzziness,
}),
ScoringExpression::Regexp { field, value } => Box::new(crate::query::regexp::RegexpQuery {
field: field.clone(),
pattern: value.clone(),
}),
ScoringExpression::Wildcard { field, value } => {
Box::new(crate::query::wildcard::WildcardQuery {
field: field.clone(),
pattern: value.clone(),
})
}
ScoringExpression::MultiMatch {
fields,
query,
analyzer,
tie_breaker,
} => {
let queries: Vec<Box<dyn Query>> = fields
.iter()
.map(|f| -> Box<dyn Query> {
Box::new(MatchQuery {
field: f.clone(),
query_text: query.clone(),
analyzer: analyzer.clone(),
})
})
.collect();
Box::new(crate::query::dis_max::DisMaxQuery {
queries,
tie_breaker: *tie_breaker,
})
}
ScoringExpression::Span(span_ast) => ast_to_span_query(span_ast),
ScoringExpression::Knn {
field,
query_vector,
k,
num_candidates,
threshold,
} => Box::new(crate::vector::query::KnnQuery {
field: field.clone(),
query_vector: query_vector.clone(),
k: *k,
num_candidates: *num_candidates,
threshold: *threshold,
}),
ScoringExpression::MatchAll => Box::new(MatchAllQuery),
ScoringExpression::MatchNone => Box::new(MatchNoneQuery),
}
}
pub(crate) fn ast_to_span_query(ast: &SpanExpression) -> Box<dyn SpanQuery> {
match ast {
SpanExpression::SpanTerm { field, value } => Box::new(SpanTermQuery {
field: field.clone(),
value: value.clone(),
}),
SpanExpression::SpanNear {
field,
terms,
slop,
in_order,
} => Box::new(SpanNearQuery {
field: field.clone(),
terms: terms.clone(),
slop: *slop,
in_order: *in_order,
}),
SpanExpression::SpanNot { include, exclude } => Box::new(SpanNotQuery {
include: ast_to_span_query(include),
exclude: ast_to_span_query(exclude),
}),
SpanExpression::SpanFirst { query, end } => Box::new(SpanFirstQuery {
inner: ast_to_span_query(query),
end: *end,
}),
}
}
pub struct MatchAllQuery;
impl Query for MatchAllQuery {
fn bind(
&self,
_searcher: &crate::search::searcher::Searcher,
_score_mode: crate::core::ScoreMode,
) -> crate::core::Result<Box<dyn crate::query::BoundQuery>> {
Ok(Box::new(BoundMatchAllQuery))
}
}
struct BoundMatchAllQuery;
impl crate::query::BoundQuery for BoundMatchAllQuery {
fn is_match_all(&self) -> bool {
true
}
fn scorer_supplier(
&self,
reader: &crate::segment::reader::SegmentReader,
) -> crate::core::Result<Option<Box<dyn crate::query::ScorerSupplier>>> {
let doc_count = reader.doc_count();
if doc_count == 0 {
return Ok(None);
}
Ok(Some(Box::new(MatchAllScorerSupplier { doc_count })))
}
}
struct MatchAllScorerSupplier {
doc_count: u32,
}
impl crate::query::ScorerSupplier for MatchAllScorerSupplier {
fn cost(&self) -> u64 {
self.doc_count as u64
}
fn scorer(self: Box<Self>) -> crate::core::Result<Box<dyn crate::core::Scorer>> {
Ok(Box::new(MatchAllScorer {
current: 0,
doc_count: self.doc_count,
}))
}
}
struct MatchAllScorer {
current: u32,
doc_count: u32,
}
impl crate::core::Scorer for MatchAllScorer {
fn doc_id(&self) -> crate::core::DocId {
if self.current < self.doc_count {
crate::core::DocId::new(self.current)
} else {
crate::core::NO_MORE_DOCS
}
}
fn next(&mut self) -> crate::core::DocId {
self.current += 1;
self.doc_id()
}
fn advance(&mut self, target: crate::core::DocId) -> crate::core::DocId {
self.current = target.as_u32();
self.doc_id()
}
fn score(&mut self) -> f32 {
1.0
}
fn two_phase(&mut self) -> Option<&mut dyn crate::core::TwoPhaseIterator> {
None
}
}
pub struct MatchNoneQuery;
impl Query for MatchNoneQuery {
fn bind(
&self,
_searcher: &crate::search::searcher::Searcher,
_score_mode: crate::core::ScoreMode,
) -> crate::core::Result<Box<dyn crate::query::BoundQuery>> {
Ok(Box::new(BoundMatchNoneQuery))
}
}
struct BoundMatchNoneQuery;
impl crate::query::BoundQuery for BoundMatchNoneQuery {
fn scorer_supplier(
&self,
_reader: &crate::segment::reader::SegmentReader,
) -> crate::core::Result<Option<Box<dyn crate::query::ScorerSupplier>>> {
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::query::ast::ScoringExpression;
#[test]
fn convert_term() {
let ast = ScoringExpression::Term {
field: "f".into(),
value: "v".into(),
};
let _query = ast_to_query(&ast); }
#[test]
fn convert_bool_nested() {
let ast = ScoringExpression::Bool {
must: vec![ScoringExpression::Term {
field: "a".into(),
value: "1".into(),
}],
should: vec![ScoringExpression::Match {
field: "b".into(),
query: "hello".into(),
analyzer: None,
}],
must_not: vec![],
filter: vec![ScoringExpression::Exists { field: "c".into() }],
minimum_should_match: None,
};
let _query = ast_to_query(&ast);
}
#[test]
fn convert_all_types() {
let types = vec![
ScoringExpression::Term {
field: "f".into(),
value: "v".into(),
},
ScoringExpression::Terms {
field: "f".into(),
values: vec!["a".into(), "b".into()],
},
ScoringExpression::Match {
field: "f".into(),
query: "q".into(),
analyzer: None,
},
ScoringExpression::MatchPhrase {
field: "f".into(),
query: "q".into(),
analyzer: None,
},
ScoringExpression::Exists { field: "f".into() },
ScoringExpression::Prefix {
field: "f".into(),
value: "p".into(),
},
ScoringExpression::ConstantScore {
query: Box::new(ScoringExpression::MatchAll),
boost: 1.0,
},
ScoringExpression::MatchAll,
ScoringExpression::MatchNone,
];
for ast in &types {
let _q = ast_to_query(ast);
}
}
}