elasticsearch_dsl/search/rescoring/
rescore_.rs

1use crate::util::ShouldSkip;
2use crate::{Query, ScoreMode};
3
4/// Rescoring can help to improve precision by reordering just the top (eg 100 - 500)
5/// documents returned by the [query](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html#request-body-search-query)
6/// and [post_filter](https://www.elastic.co/guide/en/elasticsearch/reference/current/filter-search-results.html#post-filter)
7/// phases, using a secondary (usually more costly) algorithm, instead of applying the costly algorithm to all documents in the index.
8///
9/// A `rescore` request is executed on each shard before it returns its results to be sorted by the node handling the overall search request.
10///
11/// Currently the rescore API has only one implementation: the query rescorer, which uses a query to tweak the scoring.
12/// In the future, alternative rescorers may be made available, for example, a pair-wise rescorer.
13///
14/// To create a `rescore` query with simple `term` query:
15/// ```
16/// # use elasticsearch_dsl::rescoring::*;
17/// # use elasticsearch_dsl::queries::*;
18/// # use elasticsearch_dsl::queries::params::*;
19/// # let rescore =
20/// Rescore::new(Query::term("title", "test"));
21/// ```
22/// To create a `rescore` query with simple `term` query and optional fields:
23/// ```
24/// # use elasticsearch_dsl::rescoring::*;
25/// # use elasticsearch_dsl::queries::*;
26/// # use elasticsearch_dsl::queries::params::*;
27/// # let query =
28/// Rescore::new(Query::term("title", "test"))
29///     .rescore_query_weight(0.2)
30///     .window_size(100);
31/// ```
32/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/filter-search-results.html#rescore>
33#[derive(Debug, Clone, PartialEq, Serialize)]
34pub struct Rescore {
35    query: RescoreQuery,
36
37    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
38    window_size: Option<u64>,
39}
40
41#[derive(Debug, Clone, PartialEq, Serialize)]
42struct RescoreQuery {
43    rescore_query: Option<Query>,
44
45    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
46    rescore_query_weight: Option<f32>,
47
48    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
49    query_weight: Option<f32>,
50
51    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
52    score_mode: Option<ScoreMode>,
53}
54
55impl Rescore {
56    /// Creates a new instance of [Rescore]
57    ///
58    /// - `query` - Second query which will be execute on top-k results returned by original query.
59    pub fn new<T>(query: T) -> Self
60    where
61        T: Into<Option<Query>>,
62    {
63        Self {
64            query: RescoreQuery {
65                rescore_query: query.into(),
66                rescore_query_weight: None,
67                query_weight: None,
68                score_mode: None,
69            },
70            window_size: None,
71        }
72    }
73
74    /// The number of docs which will be examined on each shard can be controlled by the `window_size` parameter, which defaults to 10.
75    pub fn window_size(mut self, window_size: u64) -> Self {
76        self.window_size = Some(window_size);
77        self
78    }
79
80    /// The relative importance of the rescore query can be controlled with the `rescore_query_weight` respectively. Both default to 1.
81    pub fn rescore_query_weight<T>(mut self, rescore_query_weight: T) -> Self
82    where
83        T: num_traits::AsPrimitive<f32>,
84    {
85        self.query.rescore_query_weight = Some(rescore_query_weight.as_());
86        self
87    }
88
89    /// The relative importance of the original query can be controlled with the `query_weight` respectively. Both default to 1.
90    pub fn query_weight<T>(mut self, query_weight: T) -> Self
91    where
92        T: num_traits::AsPrimitive<f32>,
93    {
94        self.query.query_weight = Some(query_weight.as_());
95        self
96    }
97
98    /// The way the scores are combined can be controlled with the
99    pub fn score_mode(mut self, score_mode: ScoreMode) -> Self {
100        self.query.score_mode = Some(score_mode);
101        self
102    }
103}
104
105impl ShouldSkip for Rescore {
106    fn should_skip(&self) -> bool {
107        self.query
108            .rescore_query
109            .as_ref()
110            .is_none_or(ShouldSkip::should_skip)
111    }
112}
113
114impl IntoIterator for Rescore {
115    type Item = Self;
116
117    type IntoIter = std::option::IntoIter<Self::Item>;
118
119    fn into_iter(self) -> Self::IntoIter {
120        Some(self).into_iter()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::util::assert_serialize_rescore;
128
129    #[test]
130    fn should_skip() {
131        assert!(Rescore::new(Query::range("field")).should_skip());
132        assert!(!Rescore::new(Query::range("field").gte(1)).should_skip());
133    }
134
135    #[test]
136    fn serialization() {
137        assert_serialize_rescore(
138            Rescore::new(Query::term("title", "test")),
139            json!({
140                "query": {
141                    "rescore_query": {
142                        "term": {
143                            "title": {
144                                "value": "test"
145                            }
146                        }
147                    }
148                }
149            }),
150        );
151
152        assert_serialize_rescore(
153            Rescore::new(Query::term("title", "test"))
154                .rescore_query_weight(0.2)
155                .query_weight(0.5)
156                .window_size(100)
157                .score_mode(ScoreMode::Max),
158            json!({
159                "window_size": 100,
160                "query": {
161                    "query_weight": 0.5,
162                    "rescore_query_weight": 0.2,
163                    "score_mode": "max",
164                    "rescore_query": {
165                        "term": {
166                            "title": {
167                                "value": "test"
168                            }
169                        }
170                    }
171                }
172            }),
173        );
174    }
175}