1use std::marker::PhantomData;
6
7use serde_json::{Map, Value};
8
9use super::{Common, DistanceType, Sort, SortOrder, ValidationMethod, common_opts, exists_q, wrap};
10use crate::query::{AsQuery, Query, Root};
11
12#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
14pub struct GeoPoint {
15 pub lat: f64,
17 pub lon: f64,
19}
20
21impl GeoPoint {
22 pub fn new(lat: f64, lon: f64) -> Self {
24 Self { lat, lon }
25 }
26
27 fn to_value(self) -> Value {
29 let mut point = Map::new();
30 point.insert("lat".to_string(), Value::from(self.lat));
31 point.insert("lon".to_string(), Value::from(self.lon));
32 Value::Object(point)
33 }
34}
35
36#[derive(Debug, Clone, Copy)]
39pub enum DistanceUnit {
40 Kilometers,
42 Meters,
44 Centimeters,
46 Millimeters,
48 Miles,
50 Yards,
52 Feet,
54 NauticalMiles,
56}
57
58impl DistanceUnit {
59 pub(crate) fn as_str(self) -> &'static str {
60 match self {
61 DistanceUnit::Kilometers => "km",
62 DistanceUnit::Meters => "m",
63 DistanceUnit::Centimeters => "cm",
64 DistanceUnit::Millimeters => "mm",
65 DistanceUnit::Miles => "mi",
66 DistanceUnit::Yards => "yd",
67 DistanceUnit::Feet => "ft",
68 DistanceUnit::NauticalMiles => "nmi",
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy)]
77pub struct Distance {
78 value: f64,
79 unit: DistanceUnit,
80}
81
82impl Distance {
83 pub fn new(value: f64, unit: DistanceUnit) -> Self {
85 Self { value, unit }
86 }
87
88 pub fn km(value: f64) -> Self {
90 Self::new(value, DistanceUnit::Kilometers)
91 }
92
93 pub fn meters(value: f64) -> Self {
95 Self::new(value, DistanceUnit::Meters)
96 }
97
98 pub fn centimeters(value: f64) -> Self {
100 Self::new(value, DistanceUnit::Centimeters)
101 }
102
103 pub fn millimeters(value: f64) -> Self {
105 Self::new(value, DistanceUnit::Millimeters)
106 }
107
108 pub fn miles(value: f64) -> Self {
110 Self::new(value, DistanceUnit::Miles)
111 }
112
113 pub fn yards(value: f64) -> Self {
115 Self::new(value, DistanceUnit::Yards)
116 }
117
118 pub fn feet(value: f64) -> Self {
120 Self::new(value, DistanceUnit::Feet)
121 }
122
123 pub fn nautical_miles(value: f64) -> Self {
125 Self::new(value, DistanceUnit::NauticalMiles)
126 }
127
128 fn to_query_string(self) -> String {
129 format!("{}{}", self.value, self.unit.as_str())
130 }
131}
132
133#[derive(Debug, Clone)]
136pub struct Geo<S = Root> {
137 path: String,
138 _scope: PhantomData<fn() -> S>,
139}
140
141impl<S> Geo<S> {
142 pub fn at(path: impl Into<String>) -> Self {
143 Self {
144 path: path.into(),
145 _scope: PhantomData,
146 }
147 }
148
149 pub fn within(&self, distance: Distance, center: GeoPoint) -> GeoDistanceQuery<S> {
153 GeoDistanceQuery {
154 path: self.path.clone(),
155 distance: distance.to_query_string(),
156 center,
157 opts: Map::new(),
158 common: Common::default(),
159 _scope: PhantomData,
160 }
161 }
162
163 pub fn within_box(&self, top_left: GeoPoint, bottom_right: GeoPoint) -> Query<S> {
165 let mut corners = Map::new();
166 corners.insert("top_left".to_string(), top_left.to_value());
167 corners.insert("bottom_right".to_string(), bottom_right.to_value());
168 let mut body = Map::new();
169 body.insert(self.path.clone(), Value::Object(corners));
170 wrap_object("geo_bounding_box", body)
171 }
172
173 pub fn within_polygon(&self, points: impl IntoIterator<Item = GeoPoint>) -> Query<S> {
175 let vertices = points.into_iter().map(GeoPoint::to_value).collect();
176 let mut inner = Map::new();
177 inner.insert("points".to_string(), Value::Array(vertices));
178 let mut body = Map::new();
179 body.insert(self.path.clone(), Value::Object(inner));
180 wrap_object("geo_polygon", body)
181 }
182
183 pub fn exists(&self) -> Query<S> {
185 exists_q(&self.path)
186 }
187
188 pub fn distance_from(&self, center: GeoPoint) -> Sort {
193 let mut body = Map::new();
194 body.insert(self.path.clone(), center.to_value());
195 body.insert("order".to_string(), Value::String("asc".to_string()));
196 Sort::from_parts("_geo_distance".to_string(), body)
197 }
198
199 pub fn distance_sort(&self, center: GeoPoint, order: SortOrder, unit: DistanceUnit) -> Sort {
201 let mut body = Map::new();
202 body.insert(self.path.clone(), center.to_value());
203 body.insert(
204 "order".to_string(),
205 Value::String(order.as_str().to_string()),
206 );
207 body.insert("unit".to_string(), Value::String(unit.as_str().to_string()));
208 Sort::from_parts("_geo_distance".to_string(), body)
209 }
210}
211
212fn wrap_object<S>(name: &str, body: Map<String, Value>) -> Query<S> {
214 wrap(name, body)
215}
216
217#[derive(Debug, Clone)]
220pub struct GeoDistanceQuery<S = Root> {
221 path: String,
222 distance: String,
223 center: GeoPoint,
224 opts: Map<String, Value>,
225 common: Common,
226 _scope: PhantomData<fn() -> S>,
227}
228
229impl<S> GeoDistanceQuery<S> {
230 #[must_use]
232 pub fn distance_type(mut self, distance_type: DistanceType) -> Self {
233 self.opts.insert(
234 "distance_type".to_string(),
235 Value::String(distance_type.as_str().to_string()),
236 );
237 self
238 }
239
240 #[must_use]
243 pub fn validation_method(mut self, validation_method: ValidationMethod) -> Self {
244 self.opts.insert(
245 "validation_method".to_string(),
246 Value::String(validation_method.as_str().to_string()),
247 );
248 self
249 }
250
251 common_opts!(common);
252}
253
254impl<S> AsQuery<S> for GeoDistanceQuery<S> {
255 fn into_query(self) -> Option<Query<S>> {
256 let mut body = self.opts;
257 body.insert("distance".to_string(), Value::String(self.distance));
258 body.insert(self.path, self.center.to_value());
259 self.common.write(&mut body);
260 Some(wrap("geo_distance", body))
261 }
262}