1use crate::query::ast::{ScoringExpression, SpanExpression};
6use crate::query::boolean::BoolQuery;
7use crate::query::constant_score::ConstantScoreQuery;
8use crate::query::exists::ExistsQuery;
9use crate::query::match_query::MatchQuery;
10use crate::query::phrase::MatchPhraseQuery;
11use crate::query::prefix::PrefixQuery;
12use crate::query::range::RangeQuery;
13use crate::query::span::{SpanFirstQuery, SpanNearQuery, SpanNotQuery, SpanTermQuery};
14use crate::query::term::TermQuery;
15use crate::query::{Query, SpanQuery};
16
17#[allow(dead_code)] pub(crate) fn ast_to_query(ast: &ScoringExpression) -> Box<dyn Query> {
20 match ast {
21 ScoringExpression::Term { field, value } => Box::new(TermQuery {
22 field: field.clone(),
23 value: value.clone(),
24 }),
25
26 ScoringExpression::Terms { field, values } => {
27 let should: Vec<Box<dyn Query>> = values
29 .iter()
30 .map(|v| -> Box<dyn Query> {
31 Box::new(TermQuery {
32 field: field.clone(),
33 value: v.clone(),
34 })
35 })
36 .collect();
37 Box::new(BoolQuery {
38 must: vec![],
39 should,
40 must_not: vec![],
41 filter: vec![],
42 minimum_should_match: None,
43 })
44 }
45
46 ScoringExpression::Match {
47 field,
48 query,
49 analyzer,
50 } => Box::new(MatchQuery {
51 field: field.clone(),
52 query_text: query.clone(),
53 analyzer: analyzer.clone(),
54 }),
55
56 ScoringExpression::MatchPhrase {
57 field,
58 query,
59 analyzer,
60 } => Box::new(MatchPhraseQuery {
61 field: field.clone(),
62 query_text: query.clone(),
63 analyzer: analyzer.clone(),
64 }),
65
66 ScoringExpression::Bool {
67 must,
68 should,
69 must_not,
70 filter,
71 minimum_should_match,
72 } => Box::new(BoolQuery {
73 must: must.iter().map(ast_to_query).collect(),
74 should: should.iter().map(ast_to_query).collect(),
75 must_not: must_not.iter().map(ast_to_query).collect(),
76 filter: filter.iter().map(ast_to_query).collect(),
77 minimum_should_match: *minimum_should_match,
78 }),
79
80 ScoringExpression::MatchBoolPrefix {
81 field,
82 query,
83 analyzer,
84 } => {
85 Box::new(crate::query::match_query::MatchBoolPrefixQuery {
90 field: field.clone(),
91 query_text: query.clone(),
92 analyzer: analyzer.clone(),
93 })
94 }
95
96 ScoringExpression::DisMax {
97 queries,
98 tie_breaker,
99 } => Box::new(crate::query::dis_max::DisMaxQuery {
100 queries: queries.iter().map(ast_to_query).collect(),
101 tie_breaker: *tie_breaker,
102 }),
103
104 ScoringExpression::Exists { field } => Box::new(ExistsQuery {
105 field: field.clone(),
106 }),
107
108 ScoringExpression::Prefix { field, value } => Box::new(PrefixQuery {
109 field: field.clone(),
110 value: value.clone(),
111 }),
112
113 ScoringExpression::ConstantScore { query, boost } => Box::new(ConstantScoreQuery {
114 inner: ast_to_query(query),
115 boost: *boost,
116 }),
117
118 ScoringExpression::Nested {
119 path,
120 query,
121 inner_hits,
122 } => Box::new(crate::query::nested::NestedQuery {
123 path: path.clone(),
124 inner: ast_to_query(query),
125 inner_hits: inner_hits.clone(),
126 }),
127
128 ScoringExpression::GeoDistance {
129 field,
130 lat,
131 lon,
132 distance,
133 } => {
134 let distance_km = crate::query::parser::parse_distance_km(distance);
135 Box::new(crate::spatial::query::GeoDistanceQuery {
136 field: field.clone(),
137 center: crate::spatial::geo::GeoPoint::new(*lat, *lon),
138 distance_km,
139 })
140 }
141
142 ScoringExpression::GeoBoundingBox {
143 field,
144 top_left_lat,
145 top_left_lon,
146 bottom_right_lat,
147 bottom_right_lon,
148 } => Box::new(crate::spatial::query::GeoBoundingBoxQuery {
149 field: field.clone(),
150 top_left: crate::spatial::geo::GeoPoint::new(*top_left_lat, *top_left_lon),
151 bottom_right: crate::spatial::geo::GeoPoint::new(*bottom_right_lat, *bottom_right_lon),
152 }),
153
154 ScoringExpression::GeoShape {
155 field,
156 shape,
157 relation,
158 } => {
159 let query_geom = crate::spatial::shape::parse_geojson(&shape.json)
160 .unwrap_or(::geo::Geometry::Point(::geo::Point::new(0.0, 0.0)));
161 let query_bbox =
162 crate::spatial::shape::compute_bbox(&query_geom).unwrap_or((0.0, 0.0, 0.0, 0.0));
163 Box::new(crate::spatial::query::GeoShapeQuery {
164 field: field.clone(),
165 query_shape: query_geom,
166 query_bbox,
167 relation: relation.clone(),
168 })
169 }
170
171 ScoringExpression::Range {
172 field,
173 gte,
174 gt,
175 lte,
176 lt,
177 } => Box::new(RangeQuery {
178 field: field.clone(),
179 gte: *gte,
180 gt: *gt,
181 lte: *lte,
182 lt: *lt,
183 }),
184
185 ScoringExpression::Boost { query, boost } => Box::new(crate::query::boost::BoostQuery {
186 inner: ast_to_query(query),
187 boost: *boost,
188 }),
189
190 ScoringExpression::ScriptScore {
191 query,
192 script,
193 params,
194 } => Box::new(crate::query::script_score::ScriptScoreQuery {
195 query: ast_to_query(query),
196 script: script.clone(),
197 params: params.clone(),
198 }),
199
200 ScoringExpression::FunctionScore {
201 query,
202 functions,
203 score_mode,
204 boost_mode,
205 } => Box::new(crate::query::function_score::FunctionScoreQuery {
206 query: ast_to_query(query),
207 functions: functions.clone(),
208 score_mode: score_mode.clone(),
209 boost_mode: boost_mode.clone(),
210 }),
211
212 ScoringExpression::Boosting {
213 positive,
214 negative,
215 negative_boost,
216 } => Box::new(crate::query::boosting::BoostingQuery {
217 positive: ast_to_query(positive),
218 negative: ast_to_query(negative),
219 negative_boost: *negative_boost,
220 }),
221
222 ScoringExpression::Fuzzy {
223 field,
224 value,
225 fuzziness,
226 } => Box::new(crate::query::fuzzy::FuzzyQuery {
227 field: field.clone(),
228 value: value.clone(),
229 fuzziness: *fuzziness,
230 }),
231
232 ScoringExpression::Regexp { field, value } => Box::new(crate::query::regexp::RegexpQuery {
233 field: field.clone(),
234 pattern: value.clone(),
235 }),
236
237 ScoringExpression::Wildcard { field, value } => {
238 Box::new(crate::query::wildcard::WildcardQuery {
239 field: field.clone(),
240 pattern: value.clone(),
241 })
242 }
243
244 ScoringExpression::MultiMatch {
245 fields,
246 query,
247 analyzer,
248 tie_breaker,
249 } => {
250 let queries: Vec<Box<dyn Query>> = fields
251 .iter()
252 .map(|f| -> Box<dyn Query> {
253 Box::new(MatchQuery {
254 field: f.clone(),
255 query_text: query.clone(),
256 analyzer: analyzer.clone(),
257 })
258 })
259 .collect();
260 Box::new(crate::query::dis_max::DisMaxQuery {
261 queries,
262 tie_breaker: *tie_breaker,
263 })
264 }
265
266 ScoringExpression::Span(span_ast) => ast_to_span_query(span_ast),
272
273 ScoringExpression::Knn {
274 field,
275 query_vector,
276 k,
277 num_candidates,
278 threshold,
279 } => Box::new(crate::vector::query::KnnQuery {
280 field: field.clone(),
281 query_vector: query_vector.clone(),
282 k: *k,
283 num_candidates: *num_candidates,
284 threshold: *threshold,
285 }),
286
287 ScoringExpression::MatchAll => Box::new(MatchAllQuery),
288
289 ScoringExpression::MatchNone => Box::new(MatchNoneQuery),
290 }
291}
292
293pub(crate) fn ast_to_span_query(ast: &SpanExpression) -> Box<dyn SpanQuery> {
300 match ast {
301 SpanExpression::SpanTerm { field, value } => Box::new(SpanTermQuery {
302 field: field.clone(),
303 value: value.clone(),
304 }),
305 SpanExpression::SpanNear {
306 field,
307 terms,
308 slop,
309 in_order,
310 } => Box::new(SpanNearQuery {
311 field: field.clone(),
312 terms: terms.clone(),
313 slop: *slop,
314 in_order: *in_order,
315 }),
316 SpanExpression::SpanNot { include, exclude } => Box::new(SpanNotQuery {
317 include: ast_to_span_query(include),
318 exclude: ast_to_span_query(exclude),
319 }),
320 SpanExpression::SpanFirst { query, end } => Box::new(SpanFirstQuery {
321 inner: ast_to_span_query(query),
322 end: *end,
323 }),
324 }
325}
326
327pub struct MatchAllQuery;
329
330impl Query for MatchAllQuery {
331 fn bind(
332 &self,
333 _searcher: &crate::search::searcher::Searcher,
334 _score_mode: crate::core::ScoreMode,
335 ) -> crate::core::Result<Box<dyn crate::query::BoundQuery>> {
336 Ok(Box::new(BoundMatchAllQuery))
337 }
338}
339
340struct BoundMatchAllQuery;
341
342impl crate::query::BoundQuery for BoundMatchAllQuery {
343 fn is_match_all(&self) -> bool {
344 true
345 }
346
347 fn scorer_supplier(
348 &self,
349 reader: &crate::segment::reader::SegmentReader,
350 ) -> crate::core::Result<Option<Box<dyn crate::query::ScorerSupplier>>> {
351 let doc_count = reader.doc_count();
352 if doc_count == 0 {
353 return Ok(None);
354 }
355 Ok(Some(Box::new(MatchAllScorerSupplier { doc_count })))
356 }
357}
358
359struct MatchAllScorerSupplier {
360 doc_count: u32,
361}
362
363impl crate::query::ScorerSupplier for MatchAllScorerSupplier {
364 fn cost(&self) -> u64 {
365 self.doc_count as u64
366 }
367
368 fn scorer(self: Box<Self>) -> crate::core::Result<Box<dyn crate::core::Scorer>> {
369 Ok(Box::new(MatchAllScorer {
370 current: 0,
371 doc_count: self.doc_count,
372 }))
373 }
374}
375
376struct MatchAllScorer {
377 current: u32,
378 doc_count: u32,
379}
380
381impl crate::core::Scorer for MatchAllScorer {
382 fn doc_id(&self) -> crate::core::DocId {
383 if self.current < self.doc_count {
384 crate::core::DocId::new(self.current)
385 } else {
386 crate::core::NO_MORE_DOCS
387 }
388 }
389 fn next(&mut self) -> crate::core::DocId {
390 self.current += 1;
391 self.doc_id()
392 }
393 fn advance(&mut self, target: crate::core::DocId) -> crate::core::DocId {
394 self.current = target.as_u32();
395 self.doc_id()
396 }
397 fn score(&mut self) -> f32 {
398 1.0
399 }
400 fn two_phase(&mut self) -> Option<&mut dyn crate::core::TwoPhaseIterator> {
401 None
402 }
403}
404
405pub struct MatchNoneQuery;
406
407impl Query for MatchNoneQuery {
408 fn bind(
409 &self,
410 _searcher: &crate::search::searcher::Searcher,
411 _score_mode: crate::core::ScoreMode,
412 ) -> crate::core::Result<Box<dyn crate::query::BoundQuery>> {
413 Ok(Box::new(BoundMatchNoneQuery))
414 }
415}
416
417struct BoundMatchNoneQuery;
418
419impl crate::query::BoundQuery for BoundMatchNoneQuery {
420 fn scorer_supplier(
421 &self,
422 _reader: &crate::segment::reader::SegmentReader,
423 ) -> crate::core::Result<Option<Box<dyn crate::query::ScorerSupplier>>> {
424 Ok(None)
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431 use crate::query::ast::ScoringExpression;
432
433 #[test]
434 fn convert_term() {
435 let ast = ScoringExpression::Term {
436 field: "f".into(),
437 value: "v".into(),
438 };
439 let _query = ast_to_query(&ast); }
441
442 #[test]
443 fn convert_bool_nested() {
444 let ast = ScoringExpression::Bool {
445 must: vec![ScoringExpression::Term {
446 field: "a".into(),
447 value: "1".into(),
448 }],
449 should: vec![ScoringExpression::Match {
450 field: "b".into(),
451 query: "hello".into(),
452 analyzer: None,
453 }],
454 must_not: vec![],
455 filter: vec![ScoringExpression::Exists { field: "c".into() }],
456 minimum_should_match: None,
457 };
458 let _query = ast_to_query(&ast);
459 }
460
461 #[test]
462 fn convert_all_types() {
463 let types = vec![
464 ScoringExpression::Term {
465 field: "f".into(),
466 value: "v".into(),
467 },
468 ScoringExpression::Terms {
469 field: "f".into(),
470 values: vec!["a".into(), "b".into()],
471 },
472 ScoringExpression::Match {
473 field: "f".into(),
474 query: "q".into(),
475 analyzer: None,
476 },
477 ScoringExpression::MatchPhrase {
478 field: "f".into(),
479 query: "q".into(),
480 analyzer: None,
481 },
482 ScoringExpression::Exists { field: "f".into() },
483 ScoringExpression::Prefix {
484 field: "f".into(),
485 value: "p".into(),
486 },
487 ScoringExpression::ConstantScore {
488 query: Box::new(ScoringExpression::MatchAll),
489 boost: 1.0,
490 },
491 ScoringExpression::MatchAll,
492 ScoringExpression::MatchNone,
493 ];
494 for ast in &types {
495 let _q = ast_to_query(ast);
496 }
497 }
498}