1#![allow(unused_imports)]
2#![allow(async_fn_in_trait)]
3
4use crate::{DataServiceError, GraphNode, RuntimeError, UserContext};
5use std::collections::BTreeMap;
6use teaql_core::request::{
7 QueryOptions, QuerySelection, apply_runtime_metadata, merge_outer_filter_into_facet_aggregates,
8 runtime_relation_aggregates,
9};
10use teaql_core::{
11 CompactRow, Expr, RelationAggregate as RuntimeRelationAggregate, SelectQuery, SmartList,
12 TraceNode,
13};
14
15pub trait TeaqlQueryDataService {
16 type Error: std::error::Error + Send + Sync + 'static;
17
18 async fn fetch_all(
19 &self,
20 query: &PurposedSelectQuery,
21 ) -> Result<Vec<CompactRow>, DataServiceError<Self::Error>>;
22
23 async fn fetch_smart_list(
24 &self,
25 query: &PurposedSelectQuery,
26 ) -> Result<SmartList<CompactRow>, DataServiceError<Self::Error>>;
27
28 async fn fetch_smart_list_with_relation_aggregates(
29 &self,
30 query: &PurposedSelectQuery,
31 relation_aggregates: &[RuntimeRelationAggregate],
32 ) -> Result<SmartList<CompactRow>, DataServiceError<Self::Error>>;
33
34 async fn fetch_stream(
35 &self,
36 query: &PurposedSelectQuery,
37 ) -> Result<
38 std::pin::Pin<
39 Box<
40 dyn futures_core::Stream<
41 Item = Result<
42 teaql_data_service::StreamChunk,
43 DataServiceError<Self::Error>,
44 >,
45 > + '_,
46 >,
47 >,
48 DataServiceError<Self::Error>,
49 >;
50}
51
52pub trait TeaqlEntityDataService: TeaqlQueryDataService {
53 async fn fetch_enhanced_entities<T>(
54 &self,
55 query: &PurposedSelectQuery,
56 ) -> Result<SmartList<T>, DataServiceError<Self::Error>>
57 where
58 T: teaql_core::Entity;
59
60 async fn fetch_enhanced_entities_with_relation_aggregates<T>(
61 &self,
62 query: &PurposedSelectQuery,
63 relation_aggregates: &[RuntimeRelationAggregate],
64 ) -> Result<SmartList<T>, DataServiceError<Self::Error>>
65 where
66 T: teaql_core::Entity;
67}
68
69impl<'a, E> TeaqlQueryDataService for crate::EntityDataService<'a, E>
70where
71 E: teaql_data_service::QueryExecutor
72 + teaql_data_service::MutationExecutor
73 + teaql_data_service::StreamQueryExecutor
74 + Send
75 + Sync
76 + 'static,
77{
78 type Error = E::Error;
79
80 async fn fetch_all(
81 &self,
82 query: &PurposedSelectQuery,
83 ) -> Result<Vec<CompactRow>, DataServiceError<Self::Error>> {
84 crate::EntityDataService::fetch_all(self, query).await
85 }
86
87 async fn fetch_smart_list(
88 &self,
89 query: &PurposedSelectQuery,
90 ) -> Result<SmartList<CompactRow>, DataServiceError<Self::Error>> {
91 crate::EntityDataService::fetch_smart_list(self, query).await
92 }
93
94 async fn fetch_smart_list_with_relation_aggregates(
95 &self,
96 query: &PurposedSelectQuery,
97 relation_aggregates: &[RuntimeRelationAggregate],
98 ) -> Result<SmartList<CompactRow>, DataServiceError<Self::Error>> {
99 crate::EntityDataService::fetch_smart_list_with_relation_aggregates(
100 self,
101 query,
102 relation_aggregates,
103 )
104 .await
105 }
106
107 async fn fetch_stream(
108 &self,
109 query: &PurposedSelectQuery,
110 ) -> Result<
111 std::pin::Pin<
112 Box<
113 dyn futures_core::Stream<
114 Item = Result<
115 teaql_data_service::StreamChunk,
116 DataServiceError<Self::Error>,
117 >,
118 > + '_,
119 >,
120 >,
121 DataServiceError<Self::Error>,
122 > {
123 crate::EntityDataService::fetch_stream(self, query).await
124 }
125}
126
127impl<'a, E> TeaqlEntityDataService for crate::EntityDataService<'a, E>
128where
129 E: teaql_data_service::QueryExecutor
130 + teaql_data_service::MutationExecutor
131 + teaql_data_service::StreamQueryExecutor
132 + Send
133 + Sync
134 + 'static,
135{
136 async fn fetch_enhanced_entities<T>(
137 &self,
138 query: &PurposedSelectQuery,
139 ) -> Result<SmartList<T>, DataServiceError<Self::Error>>
140 where
141 T: teaql_core::Entity,
142 {
143 crate::EntityDataService::fetch_enhanced_entities(self, query).await
144 }
145
146 async fn fetch_enhanced_entities_with_relation_aggregates<T>(
147 &self,
148 query: &PurposedSelectQuery,
149 relation_aggregates: &[RuntimeRelationAggregate],
150 ) -> Result<SmartList<T>, DataServiceError<Self::Error>>
151 where
152 T: teaql_core::Entity,
153 {
154 crate::EntityDataService::fetch_enhanced_entities_with_relation_aggregates(
155 self,
156 query,
157 relation_aggregates,
158 )
159 .await
160 }
161}
162
163pub type TeaqlDataServiceError<R> = DataServiceError<<R as TeaqlQueryDataService>::Error>;
164
165pub trait TeaqlRuntime {
166 fn user_context(&self) -> &UserContext;
167
168 fn fetch_facet_smart_list(
169 &self,
170 entity: &str,
171 query: &PurposedSelectQuery,
172 relation_aggregates: &[RuntimeRelationAggregate],
173 trace_context: Vec<TraceNode>,
174 ) -> impl std::future::Future<Output = Result<SmartList<CompactRow>, RuntimeError>> + Send;
175}
176
177#[doc(hidden)]
179pub trait AuditedSave<'a, C>
180where
181 C: TeaqlRuntime + ?Sized + 'a,
182{
183 type Error;
184 fn save(
185 self,
186 context: &'a C,
187 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<GraphNode, Self::Error>> + '_>>;
188}
189
190pub struct PurposedQuery<T> {
191 pub inner: T,
192 pub purpose: String,
193}
194
195impl<T> PurposedQuery<T> {
196 pub fn new(inner: T, purpose: impl Into<String>) -> Self {
197 Self {
198 inner,
199 purpose: purpose.into(),
200 }
201 }
202}
203
204#[derive(Debug, Clone)]
211pub struct PurposedSelectQuery {
212 query: SelectQuery,
213}
214
215impl PurposedSelectQuery {
216 pub fn new(mut query: SelectQuery, purpose: impl Into<String>) -> Self {
217 let purpose = purpose.into();
218 assert!(
219 !purpose.trim().is_empty(),
220 "query purpose must not be empty"
221 );
222 query.trace_chain.push(TraceNode {
223 entity_type: query.entity.clone(),
224 entity_id: None,
225 comment: purpose,
226 });
227 Self { query }
228 }
229
230 pub fn as_query(&self) -> &SelectQuery {
231 &self.query
232 }
233
234 pub fn into_query(self) -> SelectQuery {
235 self.query
236 }
237}
238
239pub async fn execute_facets<C>(
240 context: &C,
241 outer_query: &SelectQuery,
242 options: &QueryOptions,
243) -> Result<BTreeMap<String, SmartList<CompactRow>>, RuntimeError>
244where
245 C: TeaqlRuntime + ?Sized,
246{
247 let mut facets = BTreeMap::new();
248 for facet in &options.facets {
249 let mut selection = facet.query.clone();
250 merge_outer_filter_into_facet_aggregates(&mut selection, outer_query);
251 if !facet.include_all_facets {
252 selection = restrict_facet_to_outer_query(
253 context,
254 selection,
255 outer_query,
256 &facet.relation_name,
257 )?;
258 }
259 let relation_aggregates = runtime_relation_aggregates(&selection.query_options);
260 let query = apply_runtime_metadata(
261 selection.query,
262 &selection.query_options,
263 &selection.child_enhancements,
264 );
265 let entity = query.entity.clone();
266 let mut chain = outer_query.trace_chain.clone();
267 chain.push(TraceNode {
268 entity_type: query.entity.clone(),
269 entity_id: None,
270 comment: facet.facet_name.clone(),
271 });
272
273 let query =
274 PurposedSelectQuery::new(query, format!("Calculate facet {}", facet.facet_name));
275 let facet_rows = context
276 .fetch_facet_smart_list(&entity, &query, &relation_aggregates, chain)
277 .await?;
278 facets.insert(facet.facet_name.clone(), facet_rows);
279 }
280 Ok(facets)
281}
282
283pub fn restrict_facet_to_outer_query<C>(
284 context: &C,
285 mut selection: QuerySelection,
286 outer_query: &SelectQuery,
287 relation_name: &str,
288) -> Result<QuerySelection, RuntimeError>
289where
290 C: TeaqlRuntime + ?Sized,
291{
292 let descriptor = context
293 .user_context()
294 .entity(&outer_query.entity)
295 .cloned()
296 .ok_or_else(|| RuntimeError::Graph(format!("missing entity: {}", outer_query.entity)))?;
297 let relation = descriptor
298 .relation_by_name(relation_name)
299 .cloned()
300 .ok_or_else(|| RuntimeError::MissingRelation {
301 entity: outer_query.entity.clone(),
302 relation: relation_name.to_owned(),
303 })?;
304 let mut subquery = outer_query.clone();
305 subquery.projection.clear();
306 subquery.expr_projection.clear();
307 subquery.order_by.clear();
308 subquery.slice = None;
309 subquery.aggregates.clear();
310 subquery.group_by.clear();
311 subquery.relations.clear();
312 selection.query = selection.query.and_filter(Expr::in_subquery(
313 relation.foreign_key,
314 descriptor,
315 subquery,
316 relation.local_key,
317 ));
318 Ok(selection)
319}