flusso_query/handles/
geo.rs1use std::marker::PhantomData;
5
6use serde_json::{Map, Value};
7
8use super::{Common, Sort, SortOrder, common_opts, exists_q, wrap};
9use crate::query::{AsQuery, Query, Root};
10
11#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
13pub struct GeoPoint {
14 pub lat: f64,
16 pub lon: f64,
18}
19
20impl GeoPoint {
21 pub fn new(lat: f64, lon: f64) -> Self {
23 Self { lat, lon }
24 }
25
26 fn to_value(self) -> Value {
28 let mut point = Map::new();
29 point.insert("lat".to_string(), Value::from(self.lat));
30 point.insert("lon".to_string(), Value::from(self.lon));
31 Value::Object(point)
32 }
33}
34
35#[derive(Debug, Clone)]
38pub struct Geo<S = Root> {
39 path: String,
40 _scope: PhantomData<fn() -> S>,
41}
42
43impl<S> Geo<S> {
44 pub fn at(path: impl Into<String>) -> Self {
45 Self {
46 path: path.into(),
47 _scope: PhantomData,
48 }
49 }
50
51 pub fn within(&self, distance: impl Into<String>, center: GeoPoint) -> GeoDistanceQuery<S> {
55 GeoDistanceQuery {
56 path: self.path.clone(),
57 distance: distance.into(),
58 center,
59 opts: Map::new(),
60 common: Common::default(),
61 _scope: PhantomData,
62 }
63 }
64
65 pub fn in_bounding_box(&self, top_left: GeoPoint, bottom_right: GeoPoint) -> Query<S> {
67 let mut corners = Map::new();
68 corners.insert("top_left".to_string(), top_left.to_value());
69 corners.insert("bottom_right".to_string(), bottom_right.to_value());
70 let mut body = Map::new();
71 body.insert(self.path.clone(), Value::Object(corners));
72 wrap_object("geo_bounding_box", body)
73 }
74
75 pub fn in_polygon(&self, points: impl IntoIterator<Item = GeoPoint>) -> Query<S> {
77 let vertices = points.into_iter().map(GeoPoint::to_value).collect();
78 let mut inner = Map::new();
79 inner.insert("points".to_string(), Value::Array(vertices));
80 let mut body = Map::new();
81 body.insert(self.path.clone(), Value::Object(inner));
82 wrap_object("geo_polygon", body)
83 }
84
85 pub fn exists(&self) -> Query<S> {
87 exists_q(&self.path)
88 }
89
90 pub fn distance_sort(
92 &self,
93 center: GeoPoint,
94 order: SortOrder,
95 unit: impl Into<String>,
96 ) -> Sort {
97 let mut body = Map::new();
98 body.insert(self.path.clone(), center.to_value());
99 body.insert(
100 "order".to_string(),
101 Value::String(order.as_str().to_string()),
102 );
103 body.insert("unit".to_string(), Value::String(unit.into()));
104 Sort::from_parts("_geo_distance".to_string(), body)
105 }
106}
107
108fn wrap_object<S>(name: &str, body: Map<String, Value>) -> Query<S> {
110 wrap(name, body)
111}
112
113#[derive(Debug, Clone)]
116pub struct GeoDistanceQuery<S = Root> {
117 path: String,
118 distance: String,
119 center: GeoPoint,
120 opts: Map<String, Value>,
121 common: Common,
122 _scope: PhantomData<fn() -> S>,
123}
124
125impl<S> GeoDistanceQuery<S> {
126 #[must_use]
129 pub fn distance_type(mut self, distance_type: impl Into<String>) -> Self {
130 self.opts.insert(
131 "distance_type".to_string(),
132 Value::String(distance_type.into()),
133 );
134 self
135 }
136
137 #[must_use]
140 pub fn validation_method(mut self, validation_method: impl Into<String>) -> Self {
141 self.opts.insert(
142 "validation_method".to_string(),
143 Value::String(validation_method.into()),
144 );
145 self
146 }
147
148 common_opts!(common);
149}
150
151impl<S> AsQuery<S> for GeoDistanceQuery<S> {
152 fn into_query(self) -> Option<Query<S>> {
153 let mut body = self.opts;
154 body.insert("distance".to_string(), Value::String(self.distance));
155 body.insert(self.path, self.center.to_value());
156 self.common.write(&mut body);
157 Some(wrap("geo_distance", body))
158 }
159}