elasticsearch_dsl/search/
request.rs

1//! Allows you to execute a search query and get back search hits that match the query.
2use crate::search::*;
3use crate::util::*;
4use crate::Map;
5use crate::Set;
6
7/// Returns search hits that match the query defined in the request.
8///
9/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html>
10#[derive(Debug, Default, Clone, Serialize, PartialEq)]
11pub struct Search {
12    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
13    runtime_mappings: Map<String, RuntimeMapping>,
14
15    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
16    indices_boost: Vec<KeyValuePair<String, f32>>,
17
18    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
19    min_score: Option<f32>,
20
21    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
22    _source: Option<SourceFilter>,
23
24    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
25    stats: Vec<String>,
26
27    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
28    from: Option<u64>,
29
30    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
31    size: Option<u64>,
32
33    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
34    query: Option<Query>,
35
36    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
37    sort: SortCollection,
38
39    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
40    aggs: Aggregations,
41
42    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
43    track_total_hits: Option<TrackTotalHits>,
44
45    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
46    highlight: Option<Highlight>,
47
48    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
49    rescore: RescoreCollection,
50
51    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
52    suggest: Map<String, Suggester>,
53
54    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
55    stored_fields: StoredFields,
56
57    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
58    docvalue_fields: Set<String>,
59
60    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
61    script_fields: Map<String, ScriptField>,
62
63    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
64    post_filter: Option<Query>,
65
66    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
67    pit: Option<PointInTime>,
68
69    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
70    search_after: Terms,
71
72    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
73    timeout: Option<Time>,
74
75    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
76    knn: Vec<Knn>,
77
78    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
79    collapse: Option<Collapse>,
80
81    #[serde(flatten)]
82    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
83    extra: Map<String, serde_json::Value>,
84
85    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
86    track_scores: Option<bool>,
87}
88
89impl Search {
90    /// Creates a default search instance
91    pub fn new() -> Self {
92        Self::default()
93    }
94
95    /// Add runtime mapping to the search request
96    pub fn runtime_mapping<S>(mut self, name: S, mapping: RuntimeMapping) -> Self
97    where
98        S: ToString,
99    {
100        let _ = self.runtime_mappings.insert(name.to_string(), mapping);
101        self
102    }
103
104    /// Add script fields to the search request
105    pub fn script_fields<S, T>(mut self, name: S, script: T) -> Self
106    where
107        S: ToString,
108        T: Into<ScriptField>,
109    {
110        let _ = self.script_fields.insert(name.to_string(), script.into());
111        self
112    }
113
114    /// Allows to configure different boost level per index when searching
115    /// across more than one indices. This is very handy when hits coming from
116    /// one index matter more than hits coming from another index (think social
117    /// graph where each user has an index).
118    pub fn indices_boost<T, U>(mut self, field: T, boost: U) -> Self
119    where
120        T: ToString,
121        U: num_traits::AsPrimitive<f32>,
122    {
123        self.indices_boost
124            .push(KeyValuePair::new(field.to_string(), boost.as_()));
125        self
126    }
127
128    /// Exclude documents which have a `_score` less than the minimum specified
129    /// in `min_score`
130    ///
131    /// Note, most times, this does not make much sense, but is provided for
132    /// advanced use cases
133    pub fn min_score<F>(mut self, min_score: F) -> Self
134    where
135        F: Into<f32>,
136    {
137        self.min_score = Some(min_score.into());
138        self
139    }
140
141    /// Indicates which source fields are returned for matching documents
142    pub fn source<S>(mut self, source: S) -> Self
143    where
144        S: Into<SourceFilter>,
145    {
146        self._source = Some(source.into());
147        self
148    }
149
150    /// Specific `tag` of the request for logging and statistical purposes.
151    pub fn stats<S>(mut self, stats: S) -> Self
152    where
153        S: ToString,
154    {
155        self.stats.push(stats.to_string());
156        self
157    }
158
159    /// Starting document offset.
160    ///
161    /// Defaults to `0`.
162    pub fn from(mut self, from: u64) -> Self {
163        self.from = Some(from);
164        self
165    }
166
167    /// The number of hits to return.
168    ///
169    /// Defaults to `10`.
170    pub fn size(mut self, size: u64) -> Self {
171        self.size = Some(size);
172        self
173    }
174
175    /// Defines the search definition using the
176    /// [Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html).
177    pub fn query<Q>(mut self, query: Q) -> Self
178    where
179        Q: Into<Query>,
180    {
181        self.query = Some(query.into());
182        self
183    }
184
185    /// When you use the `post_filter` parameter to filter search results, the search hits are filtered after the
186    /// aggregations are calculated. A post filter has no impact on the aggregation results.
187    pub fn post_filter<Q>(mut self, post_filter: Q) -> Self
188    where
189        Q: Into<Query>,
190    {
191        self.post_filter = Some(post_filter.into());
192        self
193    }
194
195    /// A collection of sorting fields
196    pub fn sort<T>(mut self, sort: T) -> Self
197    where
198        T: IntoIterator,
199        T::Item: Into<Sort>,
200    {
201        self.sort.extend(sort);
202        self
203    }
204
205    /// Track total hits
206    pub fn track_total_hits<T>(mut self, track_total_hits: T) -> Self
207    where
208        T: Into<TrackTotalHits>,
209    {
210        self.track_total_hits = Some(track_total_hits.into());
211        self
212    }
213
214    /// If true, calculate and return document scores, even if the scores are not used for sorting.
215    pub fn track_scores(mut self, enabled: bool) -> Self {
216        self.track_scores = Some(enabled);
217        self
218    }
219
220    /// Highlight
221    pub fn highlight<H>(mut self, highlight: H) -> Self
222    where
223        H: Into<Highlight>,
224    {
225        self.highlight = Some(highlight.into());
226        self
227    }
228
229    /// Rescore
230    pub fn rescore<T>(mut self, rescore: T) -> Self
231    where
232        T: IntoIterator,
233        T::Item: Into<Rescore>,
234    {
235        self.rescore.extend(rescore);
236        self
237    }
238
239    /// Suggest
240    pub fn suggest<T, U>(mut self, name: T, suggester: U) -> Self
241    where
242        T: ToString,
243        U: Into<Suggester>,
244    {
245        let _ = self.suggest.insert(name.to_string(), suggester.into());
246        self
247    }
248
249    /// A collection of stored fields
250    pub fn stored_fields<T>(mut self, stored_fields: T) -> Self
251    where
252        T: Into<StoredFields>,
253    {
254        self.stored_fields = stored_fields.into();
255        self
256    }
257
258    /// A collection of docvalue fields
259    pub fn docvalue_fields<T>(mut self, docvalue_fields: T) -> Self
260    where
261        T: IntoIterator,
262        T::Item: ToString,
263    {
264        self.docvalue_fields
265            .extend(docvalue_fields.into_iter().map(|x| x.to_string()));
266        self
267    }
268
269    /// Point in time
270    pub fn pit(mut self, pit: PointInTime) -> Self {
271        self.pit = Some(pit);
272        self
273    }
274
275    /// Search after a set of sort values.
276    pub fn search_after<T>(mut self, sort_values: T) -> Self
277    where
278        T: Into<Terms>,
279    {
280        self.search_after = sort_values.into();
281        self
282    }
283
284    /// parameter to specify a duration you’d like to wait on each shard to complete.
285    ///
286    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-your-data.html#search-timeout>
287    pub fn timeout<T>(mut self, timeout: T) -> Self
288    where
289        T: Into<Time>,
290    {
291        self.timeout = Some(timeout.into());
292        self
293    }
294
295    /// Defines the kNN query to run.
296    ///
297    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html#search-api-knn>
298    pub fn knn(mut self, knn: Knn) -> Self {
299        self.knn.push(knn);
300        self
301    }
302
303    /// Parameter to specify collapsing results on some field
304    ///
305    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/collapse-search-results.html>
306    pub fn collapse<C>(mut self, collapse: C) -> Self
307    where
308        C: Into<Collapse>,
309    {
310        self.collapse = Some(collapse.into());
311        self
312    }
313
314    /// Extra fields for something not yet supported.
315    ///
316    /// ```
317    /// # use elasticsearch_dsl::Search;
318    /// # use serde_json::json;
319    /// # let search =
320    /// Search::new()
321    ///     .size(10)
322    ///     .extra([
323    ///         (
324    ///             "knn".to_owned(),
325    ///             json!({ "field": "abc" }),
326    ///         ),
327    ///         (
328    ///             "terminate_after".to_owned(),
329    ///             json!(42)
330    ///         ),
331    ///     ].into());
332    /// ```
333    pub fn extra(mut self, extra: Map<String, serde_json::Value>) -> Self {
334        self.extra = extra;
335        self
336    }
337
338    add_aggregate!();
339}
340
341#[cfg(test)]
342mod tests {
343    use super::*;
344
345    #[test]
346    fn serializes_to_empty_object_by_default() {
347        assert_serialize(Search::new(), json!({}));
348        assert_serialize(Search::default(), json!({}));
349    }
350
351    #[test]
352    fn serializes_extra_fields() {
353        assert_serialize(
354            Search::new().size(10).track_scores(false).extra(
355                [
356                    ("knn".to_owned(), json!({ "field": "abc" })),
357                    ("terminate_after".to_owned(), json!(42)),
358                ]
359                .into(),
360            ),
361            json!({
362                "size": 10,
363                "track_scores": false,
364                "knn": { "field": "abc" },
365                "terminate_after": 42,
366            }),
367        );
368    }
369}