1use async_graphql::dynamic::{ObjectAccessor, ValueAccessor};
2
3use crate::compiler::ir::{CompareOp, FilterNode, SqlValue};
4use crate::cube::definition::{DimType, DimensionNode};
5
6pub fn parse_where(
7 accessor: &ObjectAccessor,
8 dimensions: &[DimensionNode],
9) -> Result<FilterNode, async_graphql::Error> {
10 let mut conditions = Vec::new();
11
12 if let Ok(any_val) = accessor.try_get("any") {
13 if let Ok(list) = any_val.list() {
14 let mut or_children = Vec::new();
15 for item in list.iter() {
16 if let Ok(obj) = item.object() {
17 let child = parse_where(&obj, dimensions)?;
18 if !child.is_empty() {
19 or_children.push(child);
20 }
21 }
22 }
23 if !or_children.is_empty() {
24 conditions.push(FilterNode::Or(or_children));
25 }
26 }
27 }
28
29 for node in dimensions {
30 match node {
31 DimensionNode::Leaf(dim) => {
32 if let Ok(filter_val) = accessor.try_get(&dim.graphql_name) {
33 if let Ok(filter_obj) = filter_val.object() {
34 let leaf_conditions =
35 parse_leaf_filter(&filter_obj, &dim.column, &dim.dim_type)?;
36 conditions.extend(leaf_conditions);
37 }
38 }
39 }
40 DimensionNode::Group { graphql_name, children } => {
41 if let Ok(group_val) = accessor.try_get(graphql_name) {
42 if let Ok(group_obj) = group_val.object() {
43 let child_filter = parse_where(&group_obj, children)?;
44 if !child_filter.is_empty() {
45 conditions.push(child_filter);
46 }
47 }
48 }
49 }
50 }
51 }
52
53 Ok(match conditions.len() {
54 0 => FilterNode::Empty,
55 1 => conditions.into_iter().next().unwrap(),
56 _ => FilterNode::And(conditions),
57 })
58}
59
60pub fn parse_leaf_filter_for_selector(
63 obj: &ObjectAccessor,
64 column: &str,
65 dim_type: &DimType,
66) -> Result<Vec<FilterNode>, async_graphql::Error> {
67 parse_leaf_filter(obj, column, dim_type)
68}
69
70fn parse_leaf_filter(
71 obj: &ObjectAccessor,
72 column: &str,
73 dim_type: &DimType,
74) -> Result<Vec<FilterNode>, async_graphql::Error> {
75 let mut conditions = Vec::new();
76
77 let ops: &[(&str, CompareOp)] = match dim_type {
78 DimType::Int | DimType::Float => &[
79 ("eq", CompareOp::Eq), ("ne", CompareOp::Ne),
80 ("gt", CompareOp::Gt), ("ge", CompareOp::Ge),
81 ("lt", CompareOp::Lt), ("le", CompareOp::Le),
82 ],
83 DimType::String => &[
84 ("is", CompareOp::Eq), ("not", CompareOp::Ne),
85 ("like", CompareOp::Like), ("includes", CompareOp::Includes),
86 ],
87 DimType::DateTime => &[
88 ("is", CompareOp::Eq), ("not", CompareOp::Ne),
89 ("after", CompareOp::Gt), ("since", CompareOp::Ge),
90 ("before", CompareOp::Lt), ("till", CompareOp::Le),
91 ],
92 DimType::Bool => &[("eq", CompareOp::Eq)],
93 };
94
95 for (key, op) in ops {
96 if let Ok(val) = obj.try_get(key) {
97 let sql_val = accessor_to_sql(&val, dim_type)?;
98 conditions.push(FilterNode::Condition {
99 column: column.to_string(),
100 op: op.clone(),
101 value: sql_val,
102 });
103 }
104 }
105
106 if let Ok(val) = obj.try_get("isNull") {
108 if let Ok(b) = val.boolean() {
109 conditions.push(FilterNode::Condition {
110 column: column.to_string(),
111 op: if b { CompareOp::IsNull } else { CompareOp::IsNotNull },
112 value: SqlValue::Bool(b),
113 });
114 }
115 }
116
117 if matches!(dim_type, DimType::String) {
118 for (key, op) in &[("in", CompareOp::In), ("notIn", CompareOp::NotIn)] {
119 if let Ok(val) = obj.try_get(key) {
120 if let Ok(list) = val.list() {
121 let mut values = Vec::new();
122 for item in list.iter() {
123 if let Ok(s) = item.string() {
124 values.push(s.to_string());
125 }
126 }
127 if !values.is_empty() {
128 conditions.push(FilterNode::Condition {
129 column: column.to_string(),
130 op: op.clone(),
131 value: SqlValue::String(values.join(",")),
132 });
133 }
134 }
135 }
136 }
137 }
138
139 Ok(conditions)
140}
141
142fn accessor_to_sql(
143 val: &ValueAccessor,
144 dim_type: &DimType,
145) -> Result<SqlValue, async_graphql::Error> {
146 match dim_type {
147 DimType::Int => Ok(SqlValue::Int(val.i64()?)),
148 DimType::Float => Ok(SqlValue::Float(val.f64()?)),
149 DimType::Bool => Ok(SqlValue::Bool(val.boolean()?)),
150 DimType::String | DimType::DateTime => Ok(SqlValue::String(val.string()?.to_string())),
151 }
152}
153