elasticsearch_dsl/search/sort/
geo_distance_sort.rs

1use super::{SortMode, SortOrder};
2use crate::util::{KeyValuePair, ShouldSkip};
3use crate::{DistanceUnit, GeoDistanceType, GeoLocation};
4use serde::Serialize;
5
6/// Sorts search hits by other field values
7///
8/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html#sort-search-results>
9#[derive(Debug, Clone, PartialEq, Serialize)]
10#[serde(remote = "Self")]
11pub struct GeoDistanceSort {
12    #[serde(skip)]
13    field: String,
14
15    #[serde(skip)]
16    points: Vec<GeoLocation>,
17
18    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
19    order: Option<SortOrder>,
20
21    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
22    unit: Option<DistanceUnit>,
23
24    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
25    mode: Option<SortMode>,
26
27    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
28    distance_type: Option<GeoDistanceType>,
29
30    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
31    ignore_unmapped: Option<bool>,
32}
33
34impl GeoDistanceSort {
35    /// Creates an instance of [GeoDistanceSort]
36    pub fn new<T, U>(field: T, points: U) -> Self
37    where
38        T: ToString,
39        U: IntoIterator,
40        U::Item: Into<GeoLocation>,
41    {
42        Self {
43            field: field.to_string(),
44            points: points.into_iter().map(Into::into).collect(),
45            order: None,
46            unit: None,
47            mode: None,
48            distance_type: None,
49            ignore_unmapped: None,
50        }
51    }
52
53    /// Creates an instance of [GeoDistanceSort] by ascending order
54    pub fn ascending<T, U>(field: T, points: U) -> Self
55    where
56        T: ToString,
57        U: IntoIterator,
58        U::Item: Into<GeoLocation>,
59    {
60        Self::new(field, points).order(SortOrder::Asc)
61    }
62
63    /// Creates an instance of [GeoDistanceSort] by descending order
64    pub fn descending<T, U>(field: T, points: U) -> Self
65    where
66        T: ToString,
67        U: IntoIterator,
68        U::Item: Into<GeoLocation>,
69    {
70        Self::new(field, points).order(SortOrder::Desc)
71    }
72
73    /// Explicit order
74    ///
75    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html#_sort_order>
76    pub fn order(mut self, order: SortOrder) -> Self {
77        self.order = Some(order);
78        self
79    }
80
81    /// The unit to use when computing sort values
82    ///
83    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html#geo-sorting>
84    pub fn unit(mut self, unit: DistanceUnit) -> Self {
85        self.unit = Some(unit);
86        self
87    }
88
89    /// Sort mode for numeric fields
90    ///
91    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html#_sort_mode_option>
92    pub fn mode(mut self, mode: SortMode) -> Self {
93        self.mode = Some(mode);
94        self
95    }
96
97    /// How to compute the distance. Can either be arc (default), or plane (faster, but inaccurate on long distances and close to the poles).
98    ///
99    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html#_sort_mode_option>
100    pub fn distance_type(mut self, distance_type: GeoDistanceType) -> Self {
101        self.distance_type = Some(distance_type);
102        self
103    }
104
105    /// Indicates if the unmapped field should be treated as a missing value. Setting it to `true`
106    /// is equivalent to specifying an `unmapped_type` in the field sort. The default is `false`
107    /// (unmapped field cause the search to fail).
108    ///
109    /// <https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html#_sort_mode_option>
110    pub fn ignore_unmapped(mut self, ignore_unmapped: bool) -> Self {
111        self.ignore_unmapped = Some(ignore_unmapped);
112        self
113    }
114}
115
116impl IntoIterator for GeoDistanceSort {
117    type Item = Self;
118
119    type IntoIter = std::option::IntoIter<Self::Item>;
120
121    fn into_iter(self) -> Self::IntoIter {
122        Some(self).into_iter()
123    }
124}
125
126serialize_with_root_key_value_pair!("_geo_distance": GeoDistanceSort, field, points);
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use crate::util::assert_serialize;
132
133    #[test]
134    fn serialization() {
135        assert_serialize(
136            GeoDistanceSort::new("test", GeoLocation::new(1.2, 3.3)),
137            json!({
138                "_geo_distance": {
139                    "test": [ [3.3, 1.2] ]
140                }
141            }),
142        );
143
144        assert_serialize(
145            GeoDistanceSort::ascending("test", GeoLocation::new(1.2, 3.3)),
146            json!({
147                "_geo_distance": {
148                    "test": [ [3.3, 1.2] ],
149                    "order": "asc",
150                }
151            }),
152        );
153
154        assert_serialize(
155            GeoDistanceSort::descending("test", GeoLocation::new(1.2, 3.3))
156                .order(SortOrder::Asc)
157                .unit(DistanceUnit::Inches)
158                .mode(SortMode::Max)
159                .distance_type(GeoDistanceType::Arc)
160                .ignore_unmapped(true),
161            json!({
162                "_geo_distance": {
163                    "test": [ [3.3, 1.2] ],
164                    "unit": "in",
165                    "order": "asc",
166                    "mode": "max",
167                    "distance_type": "arc",
168                    "ignore_unmapped": true,
169                }
170            }),
171        );
172    }
173}