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