elasticsearch_dsl/search/queries/term_level/
range_query.rs

1use crate::search::*;
2use crate::util::*;
3use serde::Serialize;
4
5/// Returns documents that contain terms within a provided range.
6///
7/// To create a range query with numeric values:
8/// ```
9/// # use elasticsearch_dsl::queries::*;
10/// # use elasticsearch_dsl::queries::params::*;
11/// # let query =
12/// Query::range("numeric_field")
13///     .gt(1)
14///     .lt(3)
15///     .boost(2)
16///     .name("range_query");
17/// ```
18/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html>
19#[derive(Debug, Clone, PartialEq, Serialize)]
20#[serde(remote = "Self")]
21pub struct RangeQuery {
22    #[serde(skip)]
23    field: String,
24
25    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
26    gt: Option<Term>,
27
28    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
29    gte: Option<Term>,
30
31    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
32    lt: Option<Term>,
33
34    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
35    lte: Option<Term>,
36
37    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
38    format: Option<String>,
39
40    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
41    relation: Option<RangeRelation>,
42
43    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
44    time_zone: Option<String>,
45
46    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
47    boost: Option<f32>,
48
49    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
50    _name: Option<String>,
51}
52
53impl Query {
54    /// Creates an instance of [`RangeQuery`]
55    ///
56    /// - `field` - Field you wish to search.
57    pub fn range<T>(field: T) -> RangeQuery
58    where
59        T: ToString,
60    {
61        RangeQuery {
62            field: field.to_string(),
63            gt: Default::default(),
64            gte: Default::default(),
65            lt: Default::default(),
66            lte: Default::default(),
67            format: None,
68            relation: None,
69            time_zone: None,
70            boost: None,
71            _name: None,
72        }
73    }
74}
75
76impl RangeQuery {
77    /// Greater than.
78    pub fn gt<T>(mut self, gt: T) -> Self
79    where
80        T: Serialize,
81    {
82        self.gt = Term::new(gt);
83        self
84    }
85
86    /// Greater than or equal to.
87    pub fn gte<T>(mut self, gte: T) -> Self
88    where
89        T: Serialize,
90    {
91        self.gte = Term::new(gte);
92        self
93    }
94
95    /// Less than.
96    pub fn lt<T>(mut self, lt: T) -> Self
97    where
98        T: Serialize,
99    {
100        self.lt = Term::new(lt);
101        self
102    }
103
104    /// Less than or equal to.
105    pub fn lte<T>(mut self, lte: T) -> Self
106    where
107        T: Serialize,
108    {
109        self.lte = Term::new(lte);
110        self
111    }
112
113    /// Date format used to convert `date` values in the query.
114    ///
115    /// By default, Elasticsearch uses the
116    /// [date `format`](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html)
117    /// provided in the `<field>`'s mapping. This value overrides that mapping format.
118    ///
119    /// For valid syntax, see
120    /// [`format`](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html).
121    ///
122    /// >If a format or date value is incomplete, the range query replaces
123    /// > any missing components with default values. See
124    /// > [Missing date components](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html#missing-date-components).
125    pub fn format<T>(mut self, format: T) -> Self
126    where
127        T: ToString,
128    {
129        self.format = Some(format.to_string());
130        self
131    }
132
133    /// Indicates how the range query matches values for range fields.
134    pub fn relation(mut self, relation: RangeRelation) -> Self {
135        self.relation = Some(relation);
136        self
137    }
138
139    /// [Coordinated Universal Time (UTC) offset](https://en.wikipedia.org/wiki/List_of_UTC_time_offsets)
140    /// or [IANA time zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
141    /// used to convert `date` values in the query to UTC.
142    ///
143    /// Valid values are ISO 8601 UTC offsets, such as `+01:00` or `-08:00`, and IANA time zone IDs,
144    /// such as `America/Los_Angeles`.
145    ///
146    /// For an example query using the `time_zone` parameter, see
147    /// [Time zone in `range` queries](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html#range-query-time-zone).
148    pub fn time_zone<T>(mut self, time_zone: T) -> Self
149    where
150        T: ToString,
151    {
152        self.time_zone = Some(time_zone.to_string());
153        self
154    }
155
156    add_boost_and_name!();
157}
158
159impl ShouldSkip for RangeQuery {
160    fn should_skip(&self) -> bool {
161        self.gt.should_skip()
162            && self.gte.should_skip()
163            && self.lt.should_skip()
164            && self.lte.should_skip()
165    }
166}
167
168serialize_with_root_keyed!("range": RangeQuery);
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use chrono::prelude::*;
174
175    #[test]
176    fn serialization() {
177        assert_serialize_query(
178            Query::range("test_field"),
179            json!({
180                "range": {
181                    "test_field": {}
182                }
183            }),
184        );
185
186        assert_serialize_query(
187            Query::range("test_field")
188                .gt(None::<i32>)
189                .lt(None::<i32>)
190                .gte(None::<i32>)
191                .lte(None::<i32>),
192            json!({
193                "range": {
194                    "test_field": {}
195                }
196            }),
197        );
198
199        assert_serialize_query(
200            Query::range("test_numeric_field")
201                .gt(1)
202                .gte(2)
203                .lt(3)
204                .lte(4)
205                .relation(RangeRelation::Within)
206                .boost(2)
207                .name("range_query_test"),
208            json!({
209                "range": {
210                    "test_numeric_field": {
211                        "gt": 1,
212                        "gte": 2,
213                        "lt": 3,
214                        "lte": 4,
215                        "relation": "WITHIN",
216                        "boost": 2.0,
217                        "_name": "range_query_test"
218                    }
219                }
220            }),
221        );
222
223        assert_serialize_query(
224            Query::range("test_date_field")
225                .gt(Utc
226                    .with_ymd_and_hms(2014, 11, 28, 12, 0, 1)
227                    .single()
228                    .unwrap())
229                .gte(
230                    Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 2)
231                        .single()
232                        .unwrap(),
233                )
234                .lt(Utc
235                    .with_ymd_and_hms(2014, 11, 28, 12, 0, 3)
236                    .single()
237                    .unwrap())
238                .lte(
239                    Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 4)
240                        .single()
241                        .unwrap(),
242                )
243                .relation(RangeRelation::Contains)
244                .format("yyyy-MM-dd")
245                .time_zone("UTC")
246                .boost(2)
247                .name("range_query_test"),
248            json!({
249                "range": {
250                    "test_date_field": {
251                        "gt": "2014-11-28T12:00:01Z",
252                        "gte": "2014-11-28T12:00:02Z",
253                        "lt": "2014-11-28T12:00:03Z",
254                        "lte": "2014-11-28T12:00:04Z",
255                        "format": "yyyy-MM-dd",
256                        "time_zone": "UTC",
257                        "relation": "CONTAINS",
258                        "boost": 2.0,
259                        "_name": "range_query_test"
260                    }
261                }
262            }),
263        );
264    }
265}