subgraph/data_sources/sql/create_query/create_nested_query_recursive/
mod.rs

1use bson::{doc, Bson};
2use log::{debug, trace};
3
4use crate::{
5    configuration::subgraph::{
6        data_sources::sql::DialectEnum, entities::ServiceEntityConfig, SubGraphConfig,
7    },
8    data_sources::sql::SqlDataSource,
9    filter_operator::FilterOperator,
10    resolver_type::ResolverType,
11    sql_value::SqlValue,
12};
13
14use super::JoinClauses;
15
16impl SqlDataSource {
17    pub fn create_nested_query_recursive(
18        inputs: &Vec<Bson>,
19        entity: &ServiceEntityConfig,
20        dialect: &DialectEnum,
21        filter_by_operator: FilterOperator,
22        has_more: bool,
23        pg_param_offset: Option<i32>,
24        subgraph_config: &SubGraphConfig,
25        join_clauses: Option<JoinClauses>,
26        disable_eager_loading: bool,
27    ) -> Result<
28        (
29            Option<String>,
30            Vec<SqlValue>,
31            JoinClauses,
32            Vec<String>,
33            Option<i32>,
34        ),
35        async_graphql::Error,
36    > {
37        debug!("Creating Recursive Nested Query");
38        trace!("Initial Inputs: {:?}", inputs);
39        trace!("Initial Join Clauses: {:?}", join_clauses);
40        trace!("Pg Param Offset: {:?}", pg_param_offset);
41        let mut nested_query = String::new();
42        let mut combined_where_values = vec![];
43        let mut combined_where_keys = vec![];
44        let mut combined_join_clauses = join_clauses.unwrap_or(JoinClauses(Vec::new()));
45
46        nested_query.push_str(" (");
47
48        let mut pg_param_offset = Some(pg_param_offset.unwrap_or(0));
49        trace!("Pg Param Offset Init: {:?}", pg_param_offset);
50
51        for (i, filter) in inputs.iter().enumerate() {
52            //get the filters to handle recursively
53            let mut recursive_filters = vec![];
54            for filter_operator in FilterOperator::list() {
55                let filter = filter.as_document().unwrap().get(filter_operator.as_str());
56                recursive_filters.push((filter_operator, filter));
57            }
58
59            let mut initial_input = filter.clone().as_document().unwrap().clone();
60            let mut is_nested = false;
61
62            // Remove the and/or/like filters from the initial_input.
63            // These are handled recursively.
64            for filter_operator in FilterOperator::list() {
65                if initial_input.contains_key(filter_operator.as_str()) {
66                    initial_input.remove(filter_operator.as_str());
67                    is_nested = true;
68                }
69            }
70
71            // Nest inside a "query" property for recursive calls.
72            let query_input = doc! { "query": initial_input };
73
74            // Handle the initial filter.
75            let (where_keys, where_values, _value_keys, _values, join_clauses) =
76                SqlDataSource::get_key_data(
77                    &query_input,
78                    entity,
79                    &ResolverType::FindOne,
80                    &dialect,
81                    &subgraph_config,
82                    disable_eager_loading,
83                )?;
84            combined_join_clauses.0.extend(join_clauses.0);
85            combined_where_values.extend(where_values.clone());
86            combined_where_keys.extend(where_keys.clone());
87
88            let (parameterized_query, offset) = SqlDataSource::create_where_clause(
89                &where_keys,
90                dialect,
91                pg_param_offset,
92                &where_values,
93                filter_by_operator.clone(),
94            )?;
95
96            pg_param_offset = Some(offset);
97
98            nested_query.push_str(&parameterized_query);
99
100            if is_nested && i == 0 && !has_more && nested_query != " (" {
101                nested_query.push_str(" AND ");
102            }
103
104            for (i, recursive_filter) in recursive_filters.iter().enumerate() {
105                if recursive_filter.1.is_none() {
106                    continue;
107                }
108
109                let filters = match recursive_filter.0 {
110                    FilterOperator::And | FilterOperator::Or => recursive_filter
111                        .1
112                        .clone()
113                        .unwrap()
114                        .as_array()
115                        .unwrap()
116                        .clone(),
117                    _ => {
118                        let doc = recursive_filter.1.clone().unwrap().as_document().unwrap();
119                        let mut array = vec![];
120                        array.push(Bson::Document(doc.clone()));
121                        array
122                    }
123                };
124                let is_last = i == recursive_filters.len() - 1;
125                let has_more = if !is_last && recursive_filters[i + 1].1.is_some() {
126                    true
127                } else {
128                    false
129                };
130                let (
131                    recursive_query,
132                    recursive_where_values,
133                    recursive_join_clauses,
134                    recursive_where_keys,
135                    offset,
136                ) = SqlDataSource::create_nested_query_recursive(
137                    &filters,
138                    entity,
139                    dialect,
140                    recursive_filter.0.clone(),
141                    has_more,
142                    pg_param_offset,
143                    subgraph_config,
144                    Some(combined_join_clauses.clone()),
145                    disable_eager_loading,
146                )?;
147
148                combined_where_values.extend(recursive_where_values);
149                combined_where_keys.extend(recursive_where_keys);
150                combined_join_clauses.0.extend(recursive_join_clauses.0);
151                if offset.is_some() {
152                    pg_param_offset = offset;
153                }
154
155                if recursive_query.is_some() {
156                    nested_query.push_str(&recursive_query.unwrap());
157                }
158            }
159
160            if i != inputs.len() - 1 {
161                match filter_by_operator {
162                    FilterOperator::And => nested_query.push_str(" AND "),
163                    FilterOperator::Or => nested_query.push_str(" OR "),
164                    _ => (),
165                }
166            }
167        }
168
169        nested_query.push_str(")");
170
171        if has_more {
172            nested_query.push_str(" AND ");
173        }
174
175        let is_empty = nested_query.contains("()");
176        let is_empty_and =
177            nested_query.contains(FilterOperator::And.as_str()) && nested_query.contains("()");
178        if is_empty || nested_query.is_empty() || is_empty_and {
179            return Ok((
180                None,
181                combined_where_values,
182                combined_join_clauses,
183                combined_where_keys,
184                pg_param_offset,
185            ));
186        }
187
188        // Filter duplicates from the join clauses.
189        combined_join_clauses.0.sort();
190        combined_join_clauses.0.dedup();
191
192        trace!("Nested query: {}", nested_query);
193        trace!("Combined Where Values: {:?}", combined_where_values);
194        trace!("Combined Join Clauses: {:?}", combined_join_clauses);
195        trace!("Combined Where Keys: {:?}", combined_where_keys);
196
197        Ok((
198            Some(nested_query),
199            combined_where_values,
200            combined_join_clauses,
201            combined_where_keys,
202            pg_param_offset,
203        ))
204    }
205}