diff --git a/src/search/params/geo_shapes.rs b/src/search/params/geo_shapes.rs index ec88565..55b74d2 100644 --- a/src/search/params/geo_shapes.rs +++ b/src/search/params/geo_shapes.rs @@ -5,13 +5,15 @@ use crate::search::*; /// Note: Elasticsearch uses WGS-84 coordinates only #[derive(Debug, Clone, Copy, PartialEq, Serialize)] pub struct PointGeoShape { - coordinates: GeoCoordinate, + /// Coordinates + pub coordinates: GeoCoordinate, } /// An arbitrary line given two or more points #[derive(Debug, Clone, PartialEq, Serialize)] pub struct LineStringGeoShape { - coordinates: Vec, + /// Coordinates + pub coordinates: Vec, } /// A closed polygon whose first and last point must match, thus requiring @@ -19,47 +21,56 @@ pub struct LineStringGeoShape { /// vertices #[derive(Debug, Clone, PartialEq, Serialize)] pub struct PolygonGeoShape { - coordinates: Vec>, + /// Coordinates + pub coordinates: Vec>, } /// An array of unconnected, but likely related points #[derive(Debug, Clone, PartialEq, Serialize)] pub struct MultiPointGeoShape { - coordinates: Vec, + /// Coordinates + pub coordinates: Vec, } /// An array of separate linestrings #[derive(Debug, Clone, PartialEq, Serialize)] pub struct MultiLineStringGeoShape { - coordinates: Vec>, + /// Coordinates + pub coordinates: Vec>, } /// An array of separate polygons #[derive(Debug, Clone, PartialEq, Serialize)] pub struct MultiPolygonGeoShape { - coordinates: Vec>>, + /// Coordinates + pub coordinates: Vec>>, } /// A bounding rectangle, or envelope, specified by specifying only /// the top left and bottom right points. #[derive(Debug, Clone, PartialEq, Serialize)] pub struct EnvelopeGeoShape { - coordinates: Vec, + /// Coordinates + pub coordinates: Vec, } /// A circle specified by a center point and radius with units, /// which default to `METERS` #[derive(Debug, Clone, Copy, PartialEq, Serialize)] pub struct CircleGeoShape { - coordinates: GeoCoordinate, - radius: Distance, + /// Coordinates + pub coordinates: GeoCoordinate, + + /// Circle radius + pub radius: Distance, } /// A GeoJSON shape similar to the `multi*` shapes except that multiple types /// can coexist (e.g., a Point and a LineString) #[derive(Debug, Clone, PartialEq, Serialize)] pub struct GeometryCollection { - geometries: Vec, + /// A collection of geo shapes + pub geometries: Vec, } /// The `geo_shape` data type facilitates the indexing of and searching with @@ -67,6 +78,7 @@ pub struct GeometryCollection { /// when either the data being indexed or the queries being executed contain /// shapes other than just points. #[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(tag = "type")] pub enum GeoShape { /// A single geographic coordinate /// diff --git a/src/search/queries/geo/geo_shape_query.rs b/src/search/queries/geo/geo_shape_query.rs new file mode 100644 index 0000000..eb6a24e --- /dev/null +++ b/src/search/queries/geo/geo_shape_query.rs @@ -0,0 +1,150 @@ +use crate::search::*; +use crate::util::*; +use serde::Serialize; + +/// Filter documents indexed using the `geo_shape` or `geo_point` type. +/// +/// Requires the +/// [`geo_shape` mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html) +/// or the +/// [`geo_point` mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html). +/// +/// The `geo_shape` query uses the same grid square representation as the +/// `geo_shape` mapping to find documents that have a shape that intersects +/// with the query shape. It will also use the same Prefix Tree configuration +/// as defined for the field mapping. +/// +/// +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct GeoShapeQuery { + #[serde(rename = "geo_shape")] + inner: Inner, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +struct Inner { + #[serde(flatten)] + pair: KeyValuePair, + + #[serde(skip_serializing_if = "ShouldSkip::should_skip")] + ignore_unmapped: Option, + + #[serde(skip_serializing_if = "ShouldSkip::should_skip")] + boost: Option, + + #[serde(skip_serializing_if = "ShouldSkip::should_skip")] + _name: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +struct Shape { + shape: GeoShape, + + #[serde(skip_serializing_if = "ShouldSkip::should_skip")] + relation: Option, +} + +impl Query { + /// Creates an instance of [`GeoShapeQuery`] + /// + /// - `field` - Field you wish to search + /// - `shape` - SHape you with to search + pub fn geo_shape(field: S, shape: T) -> GeoShapeQuery + where + S: ToString, + T: Into, + { + GeoShapeQuery { + inner: Inner { + pair: KeyValuePair::new( + field.to_string(), + Shape { + shape: shape.into(), + relation: None, + }, + ), + ignore_unmapped: None, + boost: None, + _name: None, + }, + } + } +} + +impl GeoShapeQuery { + /// The [geo_shape strategy](https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html#spatial-strategy) + /// mapping parameter determines which spatial relation operators may be + /// used at search time. + pub fn relation(mut self, relation: SpatialRelation) -> Self { + self.inner.pair.value.relation = Some(relation); + self + } + + /// When set to true the `ignore_unmapped` option will ignore an unmapped + /// field and will not match any documents for this query. This can be + /// useful when querying multiple indexes which might have different + /// mappings. When set to `false` (the default value) the query will throw + /// an exception if the field is not mapped. + pub fn ignore_unmapped(mut self, ignore_unmapped: bool) -> Self { + self.inner.ignore_unmapped = Some(ignore_unmapped); + self + } + + add_boost_and_name!(); +} + +impl ShouldSkip for GeoShapeQuery {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialization() { + assert_serialize( + Query::geo_shape( + "pin.location", + GeoShape::Point(PointGeoShape { + coordinates: GeoCoordinate::from([2.2, 1.1]), + }), + ), + json!({ + "geo_shape": { + "pin.location": { + "shape": { + "type": "point", + "coordinates": [2.2, 1.1] + } + }, + } + }), + ); + + assert_serialize( + Query::geo_shape( + "pin.location", + GeoShape::Point(PointGeoShape { + coordinates: GeoCoordinate::from([2.2, 1.1]), + }), + ) + .boost(2) + .name("test") + .ignore_unmapped(true) + .relation(SpatialRelation::Within), + json!({ + "geo_shape": { + "_name": "test", + "boost": 2, + "ignore_unmapped": true, + "pin.location": { + "shape": { + "type": "point", + "coordinates": [2.2, 1.1] + }, + "relation": "WITHIN" + }, + } + }), + ); + } +} diff --git a/src/search/queries/geo/mod.rs b/src/search/queries/geo/mod.rs index 05fdbf7..276cdcc 100644 --- a/src/search/queries/geo/mod.rs +++ b/src/search/queries/geo/mod.rs @@ -4,7 +4,9 @@ mod geo_bounding_box_query; mod geo_distance_query; mod geo_shape_lookup_query; +mod geo_shape_query; pub use self::geo_bounding_box_query::*; pub use self::geo_distance_query::*; pub use self::geo_shape_lookup_query::*; +pub use self::geo_shape_query::*; diff --git a/src/search/queries/mod.rs b/src/search/queries/mod.rs index 1f3cede..ee9b7f0 100644 --- a/src/search/queries/mod.rs +++ b/src/search/queries/mod.rs @@ -135,6 +135,7 @@ query!( GeoDistance(GeoDistanceQuery), GeoBoundingBox(GeoBoundingBoxQuery), GeoShapeLookup(GeoShapeLookupQuery), + GeoShape(GeoShapeQuery), Json(JsonQuery), Wrapper(WrapperQuery), );