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 kind: teaql_core::TraceKind::Purpose,
224 entity_type: query.entity.clone(),
225 entity_id: None,
226 comment: purpose,
227 });
228 Self { query }
229 }
230
231 pub fn as_query(&self) -> &SelectQuery {
232 &self.query
233 }
234
235 pub fn into_query(self) -> SelectQuery {
236 self.query
237 }
238}
239
240pub async fn execute_facets<C>(
241 context: &C,
242 outer_query: &SelectQuery,
243 options: &QueryOptions,
244) -> Result<BTreeMap<String, SmartList<CompactRow>>, RuntimeError>
245where
246 C: TeaqlRuntime + ?Sized,
247{
248 let mut facets = BTreeMap::new();
249 for facet in &options.facets {
250 let mut selection = facet.query.clone();
251 merge_outer_filter_into_facet_aggregates(&mut selection, outer_query);
252 if !facet.include_all_facets {
253 selection = restrict_facet_to_outer_query(
254 context,
255 selection,
256 outer_query,
257 &facet.relation_name,
258 )?;
259 }
260 let relation_aggregates = runtime_relation_aggregates(&selection.query_options);
261 let query = apply_runtime_metadata(
262 selection.query,
263 &selection.query_options,
264 &selection.child_enhancements,
265 );
266 let entity = query.entity.clone();
267 let mut chain = outer_query.trace_chain.clone();
268 chain.push(TraceNode {
269 kind: teaql_core::TraceKind::Relation,
270 entity_type: query.entity.clone(),
271 entity_id: None,
272 comment: facet.facet_name.clone(),
273 });
274
275 let query =
276 PurposedSelectQuery::new(query, format!("Calculate facet {}", facet.facet_name));
277 let facet_rows = context
278 .fetch_facet_smart_list(&entity, &query, &relation_aggregates, chain)
279 .await?;
280 facets.insert(facet.facet_name.clone(), facet_rows);
281 }
282 Ok(facets)
283}
284
285pub fn restrict_facet_to_outer_query<C>(
286 context: &C,
287 mut selection: QuerySelection,
288 outer_query: &SelectQuery,
289 relation_name: &str,
290) -> Result<QuerySelection, RuntimeError>
291where
292 C: TeaqlRuntime + ?Sized,
293{
294 let descriptor = context
295 .user_context()
296 .entity(&outer_query.entity)
297 .cloned()
298 .ok_or_else(|| RuntimeError::Graph(format!("missing entity: {}", outer_query.entity)))?;
299 let relation = descriptor
300 .relation_by_name(relation_name)
301 .cloned()
302 .ok_or_else(|| RuntimeError::MissingRelation {
303 entity: outer_query.entity.clone(),
304 relation: relation_name.to_owned(),
305 })?;
306 let mut subquery = outer_query.clone();
307 subquery.projection.clear();
308 subquery.expr_projection.clear();
309 subquery.order_by.clear();
310 subquery.slice = None;
311 subquery.aggregates.clear();
312 subquery.group_by.clear();
313 subquery.relations.clear();
314 selection.query = selection.query.and_filter(Expr::in_subquery(
315 relation.foreign_key,
316 descriptor,
317 subquery,
318 relation.local_key,
319 ));
320 Ok(selection)
321}