logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::search::*;
use crate::util::*;
use serde::Serialize;

/// Queries documents that contain fields indexed using the `shape` type.
///
/// Requires the [`shape` Mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/shape.html).
///
/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-shape-query.html>
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(remote = "Self")]
pub struct ShapeQuery {
    #[serde(skip)]
    field: String,

    #[serde(skip)]
    shape: InlineShape,

    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
    ignore_unmapped: Option<bool>,

    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
    boost: Option<f32>,

    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
    _name: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize)]
struct InlineShape {
    shape: Shape,

    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
    relation: Option<SpatialRelation>,
}

impl Query {
    /// Creates an instance of [`ShapeQuery`]
    ///
    /// - `field` - Field you wish to search
    /// - `shape` - Shape you with to search
    pub fn shape<S, T>(field: S, shape: T) -> ShapeQuery
    where
        S: ToString,
        T: Into<Shape>,
    {
        ShapeQuery {
            field: field.to_string(),
            shape: InlineShape {
                shape: shape.into(),
                relation: None,
            },
            ignore_unmapped: None,
            boost: None,
            _name: None,
        }
    }
}

impl ShapeQuery {
    /// The [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.shape.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.ignore_unmapped = Some(ignore_unmapped);
        self
    }

    add_boost_and_name!();
}

impl ShouldSkip for ShapeQuery {}

serialize_with_root_key_value_pair!("shape": ShapeQuery, field, shape);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_serialization() {
        assert_serialize_query(
            Query::shape("pin.location", Shape::point([2.2, 1.1])),
            json!({
                "shape": {
                    "pin.location": {
                        "shape": {
                            "type": "point",
                            "coordinates": [2.2, 1.1]
                        }
                    },
                }
            }),
        );

        assert_serialize_query(
            Query::shape("pin.location", Shape::point([2.2, 1.1]))
                .boost(2)
                .name("test")
                .ignore_unmapped(true)
                .relation(SpatialRelation::Within),
            json!({
                "shape": {
                    "_name": "test",
                    "boost": 2.0,
                    "ignore_unmapped": true,
                    "pin.location": {
                        "shape": {
                            "type": "point",
                            "coordinates": [2.2, 1.1]
                        },
                        "relation": "WITHIN"
                    },
                }
            }),
        );
    }
}