1#![allow(unused_imports)]
2#![allow(async_fn_in_trait)]
3
4use std::collections::BTreeMap;
5use teaql_core::{
6 Expr, Record, RelationAggregate as RuntimeRelationAggregate,
7 SelectQuery, SmartList, TraceNode,
8};
9use teaql_core::request::{
10 QuerySelection, QueryOptions, apply_runtime_metadata,
11 runtime_relation_aggregates, merge_outer_filter_into_facet_aggregates,
12};
13use crate::{GraphNode, RepositoryError, RuntimeError, UserContext};
14
15pub trait TeaqlRecordRepository {
16 type Error: std::error::Error + Send + Sync + 'static;
17
18 async fn fetch_all(&self, query: &SelectQuery) -> Result<Vec<Record>, RepositoryError<Self::Error>>;
19
20 async fn fetch_smart_list(&self, query: &SelectQuery) -> Result<SmartList<Record>, RepositoryError<Self::Error>>;
21
22 async fn fetch_smart_list_with_relation_aggregates(
23 &self,
24 query: &SelectQuery,
25 relation_aggregates: &[RuntimeRelationAggregate],
26 ) -> Result<SmartList<Record>, RepositoryError<Self::Error>>;
27
28 async fn fetch_stream(&self, query: &SelectQuery) -> Result<Vec<teaql_data_service::StreamChunk>, RepositoryError<Self::Error>>;
29}
30
31pub trait TeaqlEntityRepository: TeaqlRecordRepository {
32 async fn fetch_enhanced_entities<T>(&self, query: &SelectQuery) -> Result<SmartList<T>, RepositoryError<Self::Error>>
33 where
34 T: teaql_core::Entity;
35
36 async fn fetch_enhanced_entities_with_relation_aggregates<T>(
37 &self,
38 query: &SelectQuery,
39 relation_aggregates: &[RuntimeRelationAggregate],
40 ) -> Result<SmartList<T>, RepositoryError<Self::Error>>
41 where
42 T: teaql_core::Entity;
43
44 async fn save_entity_graph<T>(&self, entity: T) -> Result<GraphNode, RepositoryError<Self::Error>>
45 where
46 T: teaql_core::Entity;
47}
48
49impl<'a, E> TeaqlRecordRepository for crate::ResolvedRepository<'a, E>
50where
51 E: teaql_data_service::QueryExecutor + teaql_data_service::MutationExecutor + teaql_data_service::StreamQueryExecutor + Send + Sync + 'static,
52{
53 type Error = E::Error;
54
55 async fn fetch_all(&self, query: &SelectQuery) -> Result<Vec<Record>, RepositoryError<Self::Error>> {
56 crate::ResolvedRepository::fetch_all(self, query).await
57 }
58
59 async fn fetch_smart_list(&self, query: &SelectQuery) -> Result<SmartList<Record>, RepositoryError<Self::Error>> {
60 crate::ResolvedRepository::fetch_smart_list(self, query).await
61 }
62
63 async fn fetch_smart_list_with_relation_aggregates(
64 &self,
65 query: &SelectQuery,
66 relation_aggregates: &[RuntimeRelationAggregate],
67 ) -> Result<SmartList<Record>, RepositoryError<Self::Error>> {
68 crate::ResolvedRepository::fetch_smart_list_with_relation_aggregates(
69 self,
70 query,
71 relation_aggregates,
72 ).await
73 }
74
75 async fn fetch_stream(&self, query: &SelectQuery) -> Result<Vec<teaql_data_service::StreamChunk>, RepositoryError<Self::Error>> {
76 crate::ResolvedRepository::fetch_stream(self, query).await
77 }
78}
79
80impl<'a, E> TeaqlEntityRepository for crate::ResolvedRepository<'a, E>
81where
82 E: teaql_data_service::QueryExecutor + teaql_data_service::MutationExecutor + teaql_data_service::StreamQueryExecutor + Send + Sync + 'static,
83{
84 async fn fetch_enhanced_entities<T>(&self, query: &SelectQuery) -> Result<SmartList<T>, RepositoryError<Self::Error>>
85 where
86 T: teaql_core::Entity,
87 {
88 crate::ResolvedRepository::fetch_enhanced_entities(self, query).await
89 }
90
91 async fn fetch_enhanced_entities_with_relation_aggregates<T>(
92 &self,
93 query: &SelectQuery,
94 relation_aggregates: &[RuntimeRelationAggregate],
95 ) -> Result<SmartList<T>, RepositoryError<Self::Error>>
96 where
97 T: teaql_core::Entity,
98 {
99 crate::ResolvedRepository::fetch_enhanced_entities_with_relation_aggregates(
100 self,
101 query,
102 relation_aggregates,
103 ).await
104 }
105
106 async fn save_entity_graph<T>(&self, entity: T) -> Result<GraphNode, RepositoryError<Self::Error>>
107 where
108 T: teaql_core::Entity,
109 {
110 crate::ResolvedRepository::save_entity_graph(self, entity).await
111 }
112}
113
114pub type TeaqlRepositoryError<R> = RepositoryError<<R as TeaqlRecordRepository>::Error>;
115
116pub trait TeaqlRuntime {
117 fn user_context(&self) -> &UserContext;
118
119 fn fetch_facet_smart_list(
120 &self,
121 entity: &str,
122 query: &SelectQuery,
123 relation_aggregates: &[RuntimeRelationAggregate],
124 trace_context: Vec<TraceNode>,
125 ) -> impl std::future::Future<Output = Result<SmartList<Record>, RuntimeError>> + Send;
126}
127
128#[doc(hidden)]
130pub trait AuditedSave<'a, C>
131where
132 C: TeaqlRuntime + ?Sized + 'a,
133{
134 type Error;
135 fn save(self, ctx: &'a C) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<GraphNode, Self::Error>> + '_>>;
136}
137
138pub struct PurposedQuery<T> {
139 pub inner: T,
140 pub purpose: String,
141}
142
143impl<T> PurposedQuery<T> {
144 pub fn new(inner: T, purpose: impl Into<String>) -> Self {
145 Self { inner, purpose: purpose.into() }
146 }
147}
148
149pub async fn execute_facets<C>(
150 ctx: &C,
151 outer_query: &SelectQuery,
152 options: &QueryOptions,
153) -> Result<BTreeMap<String, SmartList<Record>>, RuntimeError>
154where
155 C: TeaqlRuntime + ?Sized,
156{
157 let mut facets = BTreeMap::new();
158 for facet in &options.facets {
159 let mut selection = facet.query.clone();
160 merge_outer_filter_into_facet_aggregates(&mut selection, outer_query);
161 if !facet.include_all_facets {
162 selection = restrict_facet_to_outer_query(ctx, selection, outer_query, &facet.relation_name)?;
163 }
164 let relation_aggregates = runtime_relation_aggregates(&selection.query_options);
165 let query = apply_runtime_metadata(
166 selection.query,
167 &selection.query_options,
168 &selection.child_enhancements,
169 );
170 let mut chain = outer_query.trace_chain.clone();
171 chain.push(TraceNode {
172 entity_type: query.entity.clone(),
173 entity_id: None,
174 comment: facet.facet_name.clone(),
175 });
176
177 let facet_rows = ctx.fetch_facet_smart_list(&query.entity, &query, &relation_aggregates, chain).await?;
178 facets.insert(facet.facet_name.clone(), facet_rows);
179 }
180 Ok(facets)
181}
182
183pub fn restrict_facet_to_outer_query<C>(
184 ctx: &C,
185 mut selection: QuerySelection,
186 outer_query: &SelectQuery,
187 relation_name: &str,
188) -> Result<QuerySelection, RuntimeError>
189where
190 C: TeaqlRuntime + ?Sized,
191{
192 let descriptor = ctx
193 .user_context()
194 .entity(&outer_query.entity)
195 .cloned()
196 .ok_or_else(|| RuntimeError::Graph(format!("missing entity: {}", outer_query.entity)))?;
197 let relation = descriptor
198 .relation_by_name(relation_name)
199 .cloned()
200 .ok_or_else(|| RuntimeError::MissingRelation {
201 entity: outer_query.entity.clone(),
202 relation: relation_name.to_owned(),
203 })?;
204 let mut subquery = outer_query.clone();
205 subquery.projection.clear();
206 subquery.expr_projection.clear();
207 subquery.order_by.clear();
208 subquery.slice = None;
209 subquery.aggregates.clear();
210 subquery.group_by.clear();
211 subquery.relations.clear();
212 selection.query = selection.query.and_filter(Expr::in_subquery(
213 relation.foreign_key,
214 descriptor,
215 subquery,
216 relation.local_key,
217 ));
218 Ok(selection)
219}