Skip to main content

partiql/planner/
filter.rs

1use crate::sql::re_from_str;
2use crate::sql::Env;
3use crate::sql::Expr;
4use crate::sql::Selector;
5use crate::sql::WhereCond;
6use crate::value::PqlValue;
7
8#[derive(Debug, Default, Clone)]
9pub struct Filter(pub Option<Box<WhereCond>>);
10
11impl Filter {
12    pub fn execute(self, value: PqlValue, env: &Env) -> PqlValue {
13        match &self.0 {
14            None => value,
15            Some(box WhereCond::Eq { expr, right }) => match expr {
16                Expr::Selector(selector) => {
17                    let selector = selector.expand_fullpath2(&env);
18                    let cond = WhereCond::Eq {
19                        expr: expr.to_owned(),
20                        right: right.to_owned(),
21                    };
22                    value
23                        .restrict(&selector, &Some(cond))
24                        .expect("restricted value")
25                }
26                _ => {
27                    todo!();
28                }
29            },
30            Some(box WhereCond::Like { expr, right }) => match expr {
31                Expr::Selector(selector) => {
32                    let selector = selector.expand_fullpath2(&env);
33                    let cond = WhereCond::Like {
34                        expr: expr.to_owned(),
35                        right: right.to_owned(),
36                    };
37                    value
38                        .restrict(&selector, &Some(cond))
39                        .expect("restricted value")
40                }
41                _ => {
42                    todo!();
43                }
44            },
45            _ => {
46                dbg!(&self);
47                todo!()
48            }
49        }
50    }
51}
52
53pub fn restrict(
54    value: Option<PqlValue>,
55    path: &Selector,
56    cond: &Option<WhereCond>,
57) -> Option<PqlValue> {
58    match value {
59        None => None,
60        Some(PqlValue::Boolean(boolean)) if boolean => Some(PqlValue::Boolean(boolean)),
61        Some(PqlValue::Boolean(_)) => None,
62        Some(PqlValue::Null) => None,
63        Some(PqlValue::Str(string)) => {
64            let is_match = match cond {
65                Some(WhereCond::Eq { expr: _, right }) => {
66                    PqlValue::Str(string.clone()) == right.to_owned()
67                }
68                Some(WhereCond::Like { expr: _, right }) => re_from_str(&right).is_match(&string),
69
70                _ => unreachable!(),
71            };
72            if is_match {
73                Some(PqlValue::Str(string.to_owned()))
74            } else {
75                None
76            }
77        }
78        Some(PqlValue::Float(float)) => Some(PqlValue::Float(float)),
79        Some(PqlValue::Array(array)) => {
80            let arr = array
81                .into_iter()
82                .filter_map(|v| {
83                    let vv = restrict(Some(v), path, cond);
84                    vv
85                })
86                .collect::<Vec<_>>();
87
88            if arr.is_empty() {
89                None
90            } else {
91                Some(PqlValue::Array(arr))
92            }
93        }
94        Some(PqlValue::Object(mut object)) => {
95            if let Some((head, tail)) = &path.split_first() {
96                if let Some(value) = object.get(&head.to_string()) {
97                    match restrict(Some(value.to_owned()), &tail, cond) {
98                        Some(v) if tail.to_vec().len() > 0 => {
99                            let it = object.get_mut(&head.to_string()).unwrap();
100                            *it = v.to_owned();
101                            Some(PqlValue::Object(object))
102                        }
103                        Some(_v) => Some(PqlValue::Object(object.to_owned())),
104                        _ => None,
105                    }
106                } else {
107                    None
108                }
109            } else {
110                unreachable!()
111            }
112        }
113        _ => {
114            todo!();
115        }
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use std::str::FromStr;
122
123    use super::restrict;
124    use crate::pqlir_parser;
125    use crate::sql::Expr;
126    use crate::sql::Selector;
127    use crate::sql::WhereCond;
128    use crate::value::PqlValue;
129
130    #[test]
131    fn boolean() -> anyhow::Result<()> {
132        let value = PqlValue::from_str(
133            "
134    <<true, false, null>>
135   ",
136        )?;
137
138        let res = value.restrict(&Selector::default(), &None);
139        assert_eq!(res, Some(PqlValue::from_str(r#"<<true>>"#)?));
140        Ok(())
141    }
142
143    #[test]
144    fn missing() -> anyhow::Result<()> {
145        let value = PqlValue::from_str(
146            "
147{
148    'top': <<
149        {'a': 1, 'b': true, 'c': 'alpha'},
150        {'a': 2, 'b': null, 'c': 'beta'},
151        {'a': 3, 'c': 'gamma'}
152    >>
153}
154   ",
155        )?;
156        let res = value.restrict(&Selector::from("top.b"), &None);
157        let expected = pqlir_parser::pql_value(
158            "
159{
160    'top': <<
161        {'a': 1, 'b': true, 'c': 'alpha'}
162    >>
163}
164   ",
165        )?;
166        assert_eq!(res, Some(expected));
167
168        Ok(())
169    }
170
171    #[test]
172    fn pattern_string() -> anyhow::Result<()> {
173        let value = PqlValue::from_str(
174            "
175{
176    'hr': {
177        'employeesNest': <<
178            {
179            'id': 3,
180            'name': 'Bob Smith',
181            'title': null,
182            'projects': [ { 'name': 'AWS Redshift Spectrum querying' },
183                            { 'name': 'AWS Redshift security' },
184                            { 'name': 'AWS Aurora security' }
185                        ]
186            },
187            {
188                'id': 4,
189                'name': 'Susan Smith',
190                'title': 'Dev Mgr',
191                'projects': []
192            },
193            {
194                'id': 6,
195                'name': 'Jane Smith',
196                'title': 'Software Eng 2',
197                'projects': [ { 'name': 'AWS Redshift security' } ]
198            }
199        >>
200    }
201}
202   ",
203        )?;
204        let selector = Selector::from("hr.employeesNest.projects.name");
205        let cond = WhereCond::Like {
206            expr: Expr::default(),
207            right: "%security%".to_owned(),
208        };
209        let res = value.restrict(&selector, &Some(cond));
210        let expected = pqlir_parser::pql_value(
211            "
212{
213    'hr': {
214        'employeesNest': <<
215            {
216                'id': 3,
217                'name': 'Bob Smith',
218                'title': null,
219                'projects': [
220                    { 'name': 'AWS Redshift security' },
221                    { 'name': 'AWS Aurora security' }
222                ]
223            },
224            {
225                'id': 6,
226                'name': 'Jane Smith',
227                'title': 'Software Eng 2',
228                'projects': [ { 'name': 'AWS Redshift security' } ]
229            }
230        >>
231    }
232}
233   ",
234        )?;
235        assert_eq!(res, Some(expected));
236
237        Ok(())
238    }
239}