elasticsearch_dsl/search/aggregations/bucket/
bucket_selector_aggregation.rs

1use serde::Serialize;
2
3use crate::search::*;
4use crate::types::Map;
5use crate::util::*;
6
7/// Specifies the path to the buckets to filter in a bucket selector aggregation.
8///
9/// This can either be a single path, referencing a single metric, or multiple paths
10/// in case of more complex aggregations.
11#[derive(Debug, Clone, PartialEq, Serialize)]
12#[serde(untagged)]
13pub enum BucketsPath {
14    /// A single path referencing a metric.
15    Single(String),
16    /// Multiple paths in the form of key-value pairs.
17    /// Each key corresponds to an alias, and each value is a path to the metric.
18    Multi(Map<String, String>),
19}
20
21impl From<&str> for BucketsPath {
22    fn from(path: &str) -> Self {
23        BucketsPath::Single(path.to_string())
24    }
25}
26
27impl From<String> for BucketsPath {
28    fn from(path: String) -> Self {
29        BucketsPath::Single(path)
30    }
31}
32
33impl From<Vec<(&str, &str)>> for BucketsPath {
34    fn from(paths: Vec<(&str, &str)>) -> Self {
35        BucketsPath::Multi(
36            paths
37                .into_iter()
38                .map(|(k, v)| (k.to_string(), v.to_string()))
39                .collect(),
40        )
41    }
42}
43
44impl From<Vec<(String, String)>> for BucketsPath {
45    fn from(paths: Vec<(String, String)>) -> Self {
46        BucketsPath::Multi(paths.into_iter().collect())
47    }
48}
49
50#[derive(Debug, Clone, Serialize, PartialEq)]
51/// A parent pipeline aggregation which allows the user to specify a script to run
52/// for each bucket on the set of values returned by another aggregation.
53pub struct BucketSelectorAggregation {
54    bucket_selector: BucketSelectorAggregationInner,
55
56    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
57    aggs: Aggregations,
58}
59
60#[derive(Debug, Clone, Serialize, PartialEq)]
61struct BucketSelectorAggregationInner {
62    buckets_path: BucketsPath,
63
64    script: Script,
65
66    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
67    gap_policy: Option<GapPolicy>,
68
69    #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
70    format: Option<String>,
71}
72
73impl Aggregation {
74    /// Creates an instance of [`BucketSelectorAggregation`]
75    ///
76    /// - `buckets_path` - the path to the buckets to filter
77    /// - `script` - the script to execute for filtering
78    pub fn bucket_selector<B, S>(buckets_path: B, script: S) -> BucketSelectorAggregation
79    where
80        B: Into<BucketsPath>,
81        S: Into<Script>,
82    {
83        BucketSelectorAggregation {
84            bucket_selector: BucketSelectorAggregationInner {
85                buckets_path: buckets_path.into(),
86                script: script.into(),
87                gap_policy: None,
88                format: None,
89            },
90            aggs: Aggregations::new(),
91        }
92    }
93}
94
95impl BucketSelectorAggregation {
96    /// Sets the gap policy for the bucket selector aggregation.
97    ///
98    /// The gap policy determines how documents with missing values are treated.
99    /// The default policy is to skip gaps.
100    pub fn gap_policy(mut self, gap_policy: GapPolicy) -> Self {
101        self.bucket_selector.gap_policy = Some(gap_policy);
102        self
103    }
104
105    /// Sets the format for the bucket selector aggregation.
106    ///
107    /// The format parameter can be used to specify the format of the output values.
108    pub fn format<T>(mut self, format: T) -> Self
109    where
110        T: ToString,
111    {
112        self.bucket_selector.format = Some(format.to_string());
113        self
114    }
115
116    add_aggregate!();
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn serialization() {
125        assert_serialize_aggregation(
126            Aggregation::bucket_selector(
127                "the_sum",
128                Script::source("params.the_sum > 1000").lang("painless"),
129            ),
130            json!({
131                "bucket_selector": {
132                    "buckets_path": "the_sum",
133                    "script": {
134                        "lang": "painless",
135                         "source": "params.the_sum > 1000"
136                    }
137                }
138            }),
139        );
140
141        assert_serialize_aggregation(
142            Aggregation::bucket_selector(
143                "the_sum",
144                Script::source("params.the_sum > 1000").lang("painless"),
145            )
146            .gap_policy(GapPolicy::Skip)
147            .format("###.00"),
148            json!({
149                "bucket_selector": {
150                    "buckets_path": "the_sum",
151                   "script": {
152                        "lang": "painless",
153                        "source": "params.the_sum > 1000"
154                    },
155                    "gap_policy": "skip",
156                    "format": "###.00"
157                }
158            }),
159        );
160
161        assert_serialize_aggregation(
162            Aggregation::bucket_selector(
163                vec![("sum_value", "the_sum")],
164                Script::source("params.sum_value > 1000").lang("painless"),
165            ),
166            json!({
167                "bucket_selector": {
168                    "buckets_path": {
169                        "sum_value": "the_sum"
170                    },
171                    "script": {
172                        "lang": "painless",
173                        "source":"params.sum_value > 1000"
174                    }
175                }
176            }),
177        );
178    }
179}