1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use super::PhraseWeight;
use crate::core::searcher::Searcher;
use crate::error::TantivyError;
use crate::query::bm25::BM25Weight;
use crate::query::Query;
use crate::query::Weight;
use crate::schema::IndexRecordOption;
use crate::schema::{Field, Term};
use crate::Result;
use std::collections::BTreeSet;
#[derive(Clone, Debug)]
pub struct PhraseQuery {
field: Field,
phrase_terms: Vec<(usize, Term)>,
}
impl PhraseQuery {
pub fn new(terms: Vec<Term>) -> PhraseQuery {
let terms_with_offset = terms.into_iter().enumerate().collect();
PhraseQuery::new_with_offset(terms_with_offset)
}
pub fn new_with_offset(mut terms: Vec<(usize, Term)>) -> PhraseQuery {
assert!(
terms.len() > 1,
"A phrase query is required to have strictly more than one term."
);
terms.sort_by_key(|&(offset, _)| offset);
let field = terms[0].1.field();
assert!(
terms[1..].iter().all(|term| term.1.field() == field),
"All terms from a phrase query must belong to the same field"
);
PhraseQuery {
field,
phrase_terms: terms,
}
}
pub fn field(&self) -> Field {
self.field
}
pub fn phrase_terms(&self) -> Vec<Term> {
self.phrase_terms
.iter()
.map(|(_, term)| term.clone())
.collect::<Vec<Term>>()
}
pub(crate) fn phrase_weight(
&self,
searcher: &Searcher,
scoring_enabled: bool,
) -> Result<PhraseWeight> {
let schema = searcher.schema();
let field_entry = schema.get_field_entry(self.field);
let has_positions = field_entry
.field_type()
.get_index_record_option()
.map(IndexRecordOption::has_positions)
.unwrap_or(false);
if !has_positions {
let field_name = field_entry.name();
return Err(TantivyError::SchemaError(format!(
"Applied phrase query on field {:?}, which does not have positions indexed",
field_name
)));
}
let terms = self.phrase_terms();
let bm25_weight = BM25Weight::for_terms(searcher, &terms);
Ok(PhraseWeight::new(
self.phrase_terms.clone(),
bm25_weight,
scoring_enabled,
))
}
}
impl Query for PhraseQuery {
fn weight(&self, searcher: &Searcher, scoring_enabled: bool) -> Result<Box<dyn Weight>> {
let phrase_weight = self.phrase_weight(searcher, scoring_enabled)?;
Ok(Box::new(phrase_weight))
}
fn query_terms(&self, term_set: &mut BTreeSet<Term>) {
for (_, query_term) in &self.phrase_terms {
term_set.insert(query_term.clone());
}
}
}