logql/
expr.rs

1use crate::prelude::*;
2use crate::BinOp;
3use crate::BoolModifier;
4use crate::CardType;
5use crate::Labels;
6use crate::LogExpr;
7use crate::MetricExpr;
8use crate::OnOrIgnoringModifier;
9use crate::Separator;
10
11#[derive(Debug, Clone, PartialEq)]
12pub enum Expr {
13    Metric(MetricExpr),
14    Log(LogExpr),
15    BinOp {
16        op: BinOp,
17        options: BinOpOptions,
18        lhs: Box<Expr>,
19        rhs: Box<Expr>,
20    },
21}
22
23impl Parser for Expr {
24    fn parse(input: &str) -> IResult<&str, Self> {
25        map(
26            tuple((
27                alt((
28                    map(MetricExpr::parse, Expr::Metric),
29                    map(LogExpr::parse, Expr::Log),
30                )),
31                opt(map(
32                    tuple((
33                        opt(Separator::parse),
34                        BinOp::parse,
35                        opt(Separator::parse),
36                        BinOpOptions::parse,
37                        opt(Separator::parse),
38                        Expr::parse,
39                    )),
40                    |(_, op, _, options, _, expr)| (op, options, expr),
41                )),
42            )),
43            |(lhs, right)| match right {
44                Some((op, options, rhs)) => Self::BinOp {
45                    op,
46                    options,
47                    lhs: Box::new(lhs),
48                    rhs: Box::new(rhs),
49                },
50                None => lhs,
51            },
52        )(input)
53    }
54}
55
56#[derive(Eq, PartialEq, Debug, Clone)]
57pub struct BinOpOptions {
58    pub return_bool: bool,
59    pub card: CardType,
60    pub matching_labels: Option<Labels>,
61    pub on: bool,
62    pub include: Option<Labels>,
63}
64
65impl Parser for BinOpOptions {
66    fn parse(input: &str) -> IResult<&str, Self> {
67        println!("BinOpOptions {}", input);
68        alt((
69            map(
70                tuple((
71                    OnOrIgnoringModifier::parse,
72                    opt(Separator::parse),
73                    opt(alt((
74                        map(tag("group_left"), |_| CardType::ManyToOne),
75                        map(tag("group_right"), |_| CardType::OneToMany),
76                    ))),
77                    opt(Separator::parse),
78                    opt(map(
79                        tuple((
80                            cchar('('),
81                            opt(Separator::parse),
82                            opt(Labels::parse),
83                            opt(Separator::parse),
84                            cchar(')'),
85                        )),
86                        |(_, _, labels, _, _)| labels,
87                    )),
88                )),
89                |(on_or_ignoring, _, card_type, _, include)| {
90                    let card_type = match card_type {
91                        Some(card_type) => card_type,
92                        None => on_or_ignoring.return_bool.0,
93                    };
94                    let include = match include {
95                        Some(include) => include,
96                        None => None,
97                    };
98                    Self {
99                        return_bool: on_or_ignoring.return_bool.1,
100                        card: card_type,
101                        matching_labels: on_or_ignoring.labels,
102                        on: on_or_ignoring.on,
103                        include,
104                    }
105                },
106            ),
107            map(BoolModifier::parse, |i| Self {
108                return_bool: i.1,
109                card: i.0,
110                matching_labels: None,
111                on: false,
112                include: None,
113            }),
114        ))(input)
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use crate::{
121        basic::Literal, Duration, DurationUnit, Grouping, Identifier, JsonExpressionParser,
122        LogRangeExpr, MatchOp, Matcher, Matchers, PipelineExpr, PipelineStage,
123        RangeAggregationExpr, RangeOp, Selector, UnwrapExpr, Value, VectorAggregationExpr,
124        VectorOp,
125    };
126
127    use super::*;
128
129    #[test]
130    fn test_expr() {
131        assert_eq!(
132            Expr::parse("1.234").unwrap().1,
133            Expr::Metric(MetricExpr::Literal(Literal::from(1.234)))
134        );
135
136        assert_eq!(
137            Expr::parse("(((1.234)))").unwrap().1,
138            Expr::Metric(MetricExpr::Literal(Literal::from(1.234)))
139        );
140
141        assert_eq!(
142            Expr::parse(r#"topk(10, sum(rate({region="us-east1"} [5m]))) by (name)"#)
143                .unwrap()
144                .1,
145            Expr::Metric(MetricExpr::VectorAggregation(VectorAggregationExpr {
146                left: Box::new(MetricExpr::VectorAggregation(VectorAggregationExpr {
147                    left: Box::new(MetricExpr::RangeAggregation(RangeAggregationExpr {
148                        op: RangeOp::Rate,
149                        log_range_expr: LogRangeExpr {
150                            log_expr: LogExpr(
151                                Selector::from(Some(Matchers::from(vec![Matcher {
152                                    op: MatchOp::EQ,
153                                    key: Identifier::from("region".to_string()),
154                                    val: Value::from("us-east1".to_string())
155                                }]))),
156                                None
157                            ),
158                            offset_expr: None,
159                            unwrap_expr: None,
160                            range: Duration(Literal::from(5.0), DurationUnit::M)
161                        },
162                        grouping: None,
163                    })),
164                    op: VectorOp::SUM,
165                    grouping: None,
166                    param: None,
167                })),
168                op: VectorOp::TOPK,
169                grouping: Some(Grouping {
170                    without: false,
171                    groups: Some(Labels::from(vec![Identifier::from("name".to_string())]))
172                }),
173                param: Some(Literal::from(10.0))
174            }))
175        );
176
177        assert_eq!(
178            Expr::parse(r#"count_over_time({job="mysql"} [5m])"#)
179                .unwrap()
180                .1,
181            Expr::Metric(MetricExpr::RangeAggregation(RangeAggregationExpr {
182                op: RangeOp::Count,
183                log_range_expr: LogRangeExpr {
184                    log_expr: LogExpr(
185                        Selector::from(Some(Matchers::from(vec![Matcher {
186                            op: MatchOp::EQ,
187                            key: Identifier::from("job".to_string()),
188                            val: Value::from("mysql".to_string()),
189                        }]))),
190                        None
191                    ),
192                    offset_expr: None,
193                    unwrap_expr: None,
194                    range: Duration(Literal::from(5.0), DurationUnit::M),
195                },
196                grouping: None,
197            }))
198        );
199    }
200
201    #[test]
202    fn test_quantile_expr() {
203        assert_eq!(
204            Expr::parse(
205                r#"quantile_over_time(
206                0.99,
207                {container="ingress-nginx",service="hosted-grafana"}
208                | json
209                | unwrap response_latency_seconds [1m]
210                ) by (cluster)"#
211            )
212            .unwrap()
213            .1,
214            Expr::Metric(MetricExpr::RangeAggregation(RangeAggregationExpr {
215                op: RangeOp::Quantile(Literal(0.99)),
216                log_range_expr: LogRangeExpr {
217                    log_expr: LogExpr(
218                        Selector(Some(Matchers(vec![
219                            Matcher {
220                                op: MatchOp::EQ,
221                                key: Identifier::from("container".to_owned()),
222                                val: Value::from("ingress-nginx".to_owned()),
223                            },
224                            Matcher {
225                                op: MatchOp::EQ,
226                                key: Identifier::from("service".to_owned()),
227                                val: Value::from("hosted-grafana".to_owned()),
228                            },
229                        ]))),
230                        Some(PipelineExpr(vec![PipelineStage::JsonExpressionParser(
231                            JsonExpressionParser(None),
232                        )])),
233                    ),
234                    offset_expr: None,
235                    unwrap_expr: Some(UnwrapExpr {
236                        id: Identifier::from("response_latency_seconds".to_owned()),
237                        operation: None,
238                        post_filters: None,
239                    }),
240                    range: Duration(Literal(1.0), DurationUnit::M),
241                },
242                grouping: Some(Grouping {
243                    without: false,
244                    groups: Some(Labels(vec![Identifier::from("cluster".to_owned())])),
245                }),
246            }))
247        );
248    }
249
250    #[test]
251    fn test_bin_op() {
252        assert_eq!(
253            Expr::parse(r#"sum(count_over_time({app="foo"} [1m])) by (app, machine) > bool on(app) group_left(pool) sum(count_over_time({app="foo"} [1m])) by (app, pool)"#).unwrap().1,
254            Expr::BinOp {
255                op: BinOp::GT,
256                options: BinOpOptions {
257                    return_bool: true,
258                    card: CardType::ManyToOne,
259                    matching_labels: Some(Labels::from(vec![Identifier::from("app".to_string())])),
260                    on: true,
261                    include: Some(Labels::from(vec![Identifier::from("pool".to_string())])),
262                },
263                lhs: Box::new(Expr::Metric(MetricExpr::VectorAggregation(
264                    VectorAggregationExpr {
265                        left: Box::new(MetricExpr::RangeAggregation(RangeAggregationExpr {
266                            op: RangeOp::Count,
267                            log_range_expr: LogRangeExpr {
268                                log_expr: LogExpr(Selector::from(Some(
269                                    Matchers::from(vec![Matcher {
270                                        op: MatchOp::EQ,
271                                        key: Identifier::from("app".to_string()),
272                                        val: Value::from("foo".to_string()),
273                                    }])
274                                )), None),
275                                offset_expr: None,
276                                unwrap_expr: None,
277                                range: Duration(Literal::from(1.0), DurationUnit::M)
278                            },
279                            grouping: None
280                        })),
281                        op: VectorOp::SUM,
282                        grouping: Some(Grouping {
283                            without: false,
284                            groups: Some(Labels::from(vec![
285                                Identifier::from("app".to_string()),
286                                Identifier::from("machine".to_string())
287                            ]))
288                        }),
289                        param: None,
290                    }
291                ))),
292                rhs: Box::new(Expr::Metric(MetricExpr::VectorAggregation(
293                    VectorAggregationExpr {
294                        left: Box::new(MetricExpr::RangeAggregation(RangeAggregationExpr {
295                            op: RangeOp::Count,
296                            log_range_expr: LogRangeExpr {
297                                log_expr: LogExpr(Selector::from(Some(
298                                    Matchers::from(vec![Matcher {
299                                        op: MatchOp::EQ,
300                                        key: Identifier::from("app".to_string()),
301                                        val: Value::from("foo".to_string()),
302                                    }])
303                                )), None),
304                                offset_expr: None,
305                                unwrap_expr: None,
306                                range: Duration(Literal::from(1.0), DurationUnit::M)
307                            },
308                            grouping: None
309                        })),
310                        op: VectorOp::SUM,
311                        grouping: Some(Grouping {
312                            without: false,
313                            groups: Some(Labels::from(vec![
314                                Identifier::from("app".to_string()),
315                                Identifier::from("pool".to_string())
316                            ]))
317                        }),
318                        param: None,
319                    }
320                )))
321            }
322        );
323    }
324}