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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::util::*;
use crate::{Query, SpanQuery};
use serde::Serialize;

/// Removes matches which overlap with another span query or which are within x tokens before
/// (controlled by the parameter `pre`) or y tokens after (controlled by the parameter `post`)
/// another SpanQuery. The span not query maps to Lucene `SpanNotQuery`.
///
/// The `include` and `exclude` clauses can be any span type query. The `include` clause is the
/// span query whose matches are filtered, and the `exclude` clause is the span query whose matches
/// must not overlap those returned.
///
/// <https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-span-not-query.html>
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(remote = "Self")]
pub struct SpanNotQuery {
    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
    dist: Option<i32>,

    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
    exclude: Vec<SpanQuery>,

    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
    include: Vec<SpanQuery>,

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

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

impl Query {
    /// Creates an instance of [`SpanNotQuery`]
    pub fn span_not<T, U>(exclude: T, include: U) -> SpanNotQuery
    where
        T: IntoIterator,
        T::Item: Into<SpanQuery>,
        U: IntoIterator,
        U::Item: Into<SpanQuery>,
    {
        SpanNotQuery {
            exclude: exclude.into_iter().map(Into::into).collect(),
            include: include.into_iter().map(Into::into).collect(),
            dist: None,
            post: None,
            pre: None,
        }
    }
}

impl SpanNotQuery {
    /// If set the amount of tokens from within the include span can’t have overlap with the
    /// exclude span.
    ///
    /// Equivalent of setting both `pre` and `post`.
    pub fn dist(mut self, dist: i32) -> Self {
        self.dist = Some(dist);
        self
    }

    /// If set the amount of tokens after the include span can’t have overlap with the exclude span.
    ///
    /// Defaults to 0.
    pub fn post(mut self, post: i32) -> Self {
        self.post = Some(post);
        self
    }

    /// If set the amount of tokens before the include span can’t have overlap with the exclude
    /// span.
    ///
    /// Defaults to 0.
    pub fn pre(mut self, pre: i32) -> Self {
        self.pre = Some(pre);
        self
    }
}

impl ShouldSkip for SpanNotQuery {}

serialize_with_root!("span_not": SpanNotQuery);

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

    #[test]
    fn serialization() {
        assert_serialize_query(
            Query::span_not(
                [Query::span_term("foo", 1234)],
                [Query::span_term("bar", 4321)],
            )
            .dist(1234)
            .post(4321)
            .pre(5678),
            json!({
                "span_not": {
                    "dist": 1234,
                    "exclude": [
                        {
                            "span_term": {
                                "foo": {
                                    "value": 1234
                                }
                            }
                        }
                    ],
                    "include": [
                        {
                            "span_term": {
                                "bar": {
                                    "value": 4321
                                }
                            }
                        }
                    ],
                    "post": 4321,
                    "pre": 5678
                }
            }),
        );

        assert_serialize_query(
            Query::span_not(
                [Query::span_term("foo", 1234)],
                [Query::span_term("bar", 4321)],
            )
            .dist(1234)
            .post(4321)
            .pre(5678),
            json!({
                "span_not": {
                    "dist": 1234,
                    "exclude": [
                        {
                            "span_term": {
                                "foo": {
                                    "value": 1234
                                }
                            }
                        }
                    ],
                    "include": [
                        {
                            "span_term": {
                                "bar": {
                                    "value": 4321
                                }
                            }
                        }
                    ],
                    "post": 4321,
                    "pre": 5678
                }
            }),
        );
    }
}