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