1use crate::{
22 db,
23 error::EntityHydrationError,
24 events::{EntityEvents, GenericEvent},
25 one_time_executor::IntoOneTimeExecutor,
26 traits::*,
27 tree_query::{TreeQuerySource, build_tree_query, partition_by_tag},
28};
29
30pub struct EsQuery<'q, Repo, Flavor, F, A>
35where
36 Repo: EsRepo,
37{
38 inner: sqlx::query::Map<'q, db::Db, F, A>,
39 source: TreeQuerySource<<<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId>,
40 _repo: std::marker::PhantomData<Repo>,
41 _flavor: std::marker::PhantomData<Flavor>,
42}
43
44pub struct EsQueryFlavorFlat;
46
47pub struct EsQueryFlavorNested;
49
50impl<'q, Repo, Flavor, F, A> EsQuery<'q, Repo, Flavor, F, A>
51where
52 Repo: EsRepo,
53 <<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId: Unpin,
54 F: FnMut(
55 db::Row,
56 ) -> Result<
57 GenericEvent<<<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId>,
58 sqlx::Error,
59 > + Send,
60 A: 'q + Send + sqlx::IntoArguments<'q, db::Db>,
61{
62 pub fn new(
63 query: sqlx::query::Map<'q, db::Db, F, A>,
64 source: TreeQuerySource<
65 <<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId,
66 >,
67 ) -> Self {
68 Self {
69 inner: query,
70 source,
71 _repo: std::marker::PhantomData,
72 _flavor: std::marker::PhantomData,
73 }
74 }
75
76 async fn fetch_optional_inner<E: From<sqlx::Error> + From<EntityHydrationError>>(
77 self,
78 op: impl IntoOneTimeExecutor<'_>,
79 ) -> Result<Option<<Repo as EsRepo>::Entity>, E> {
80 let executor = op.into_executor();
81 let rows = executor.fetch_all(self.inner).await?;
82 if rows.is_empty() {
83 return Ok(None);
84 }
85
86 Ok(EntityEvents::load_first(rows.into_iter())?)
87 }
88
89 async fn fetch_n_inner<E: From<sqlx::Error> + From<EntityHydrationError>>(
90 self,
91 op: impl IntoOneTimeExecutor<'_>,
92 first: usize,
93 ) -> Result<(Vec<<Repo as EsRepo>::Entity>, bool), E> {
94 let executor = op.into_executor();
95 let rows = executor.fetch_all(self.inner).await?;
96 Ok(EntityEvents::load_n(rows.into_iter(), first)?)
97 }
98
99 async fn fetch_tree_rows<E: From<sqlx::Error>>(
100 self,
101 op: impl IntoOneTimeExecutor<'_>,
102 include_deleted: bool,
103 ) -> Result<
104 (
105 Vec<GenericEvent<<<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId>>,
106 std::collections::HashMap<i32, Vec<db::Row>>,
107 ),
108 E,
109 > {
110 let executor = op.into_executor();
111 let spec = <Repo as EsRepo>::nested_tree_spec();
112 let sql = build_tree_query(
113 self.source.user_sql,
114 self.source.order_by_cols,
115 &spec,
116 include_deleted,
117 self.source.n_user_args + 1,
118 );
119
120 let mut inner = self.inner;
121 let args = sqlx::Execute::take_arguments(&mut inner)
122 .map_err(sqlx::Error::Encode)?
123 .unwrap_or_default();
124
125 let rows: Vec<db::Row> = sqlx::query_with::<db::Db, _>(&sql, args)
126 .fetch_all(executor)
127 .await?;
128 let mut by_tag = partition_by_tag(rows)?;
129 let root_rows = by_tag.remove(&0).unwrap_or_default();
130 let root = root_rows
131 .iter()
132 .map(self.source.decode)
133 .collect::<Result<Vec<_>, sqlx::Error>>()?;
134 Ok((root, by_tag))
135 }
136}
137
138impl<'q, Repo, F, A> EsQuery<'q, Repo, EsQueryFlavorFlat, F, A>
139where
140 Repo: EsRepo,
141 <<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId: Unpin,
142 F: FnMut(
143 db::Row,
144 ) -> Result<
145 GenericEvent<<<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId>,
146 sqlx::Error,
147 > + Send,
148 A: 'q + Send + sqlx::IntoArguments<'q, db::Db>,
149{
150 pub async fn fetch_optional(
154 self,
155 op: impl IntoOneTimeExecutor<'_>,
156 ) -> Result<Option<<Repo as EsRepo>::Entity>, <Repo as EsRepo>::QueryError> {
157 self.fetch_optional_inner(op).await
158 }
159
160 pub async fn fetch_n(
165 self,
166 op: impl IntoOneTimeExecutor<'_>,
167 first: usize,
168 ) -> Result<(Vec<<Repo as EsRepo>::Entity>, bool), <Repo as EsRepo>::QueryError> {
169 self.fetch_n_inner(op, first).await
170 }
171}
172
173impl<'q, Repo, F, A> EsQuery<'q, Repo, EsQueryFlavorNested, F, A>
174where
175 Repo: EsRepo,
176 <<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId: Unpin,
177 F: FnMut(
178 db::Row,
179 ) -> Result<
180 GenericEvent<<<<Repo as EsRepo>::Entity as EsEntity>::Event as EsEvent>::EntityId>,
181 sqlx::Error,
182 > + Send,
183 A: 'q + Send + sqlx::IntoArguments<'q, db::Db>,
184{
185 pub async fn fetch_optional(
190 self,
191 op: impl IntoOneTimeExecutor<'_>,
192 ) -> Result<Option<<Repo as EsRepo>::Entity>, <Repo as EsRepo>::QueryError> {
193 self.fetch_optional_tree(op, false).await
194 }
195
196 pub async fn fetch_n(
201 self,
202 op: impl IntoOneTimeExecutor<'_>,
203 first: usize,
204 ) -> Result<(Vec<<Repo as EsRepo>::Entity>, bool), <Repo as EsRepo>::QueryError> {
205 self.fetch_n_tree(op, first, false).await
206 }
207
208 pub async fn fetch_optional_include_deleted(
211 self,
212 op: impl IntoOneTimeExecutor<'_>,
213 ) -> Result<Option<<Repo as EsRepo>::Entity>, <Repo as EsRepo>::QueryError> {
214 self.fetch_optional_tree(op, true).await
215 }
216
217 pub async fn fetch_n_include_deleted(
220 self,
221 op: impl IntoOneTimeExecutor<'_>,
222 first: usize,
223 ) -> Result<(Vec<<Repo as EsRepo>::Entity>, bool), <Repo as EsRepo>::QueryError> {
224 self.fetch_n_tree(op, first, true).await
225 }
226
227 async fn fetch_optional_tree(
228 self,
229 op: impl IntoOneTimeExecutor<'_>,
230 include_deleted: bool,
231 ) -> Result<Option<<Repo as EsRepo>::Entity>, <Repo as EsRepo>::QueryError> {
232 let (root, mut by_tag) = self
233 .fetch_tree_rows::<<Repo as EsRepo>::QueryError>(op, include_deleted)
234 .await?;
235 let Some(entity) = EntityEvents::load_first::<<Repo as EsRepo>::Entity>(root)? else {
236 return Ok(None);
237 };
238 let mut entities = [entity];
239 let mut cursor = 1i32;
240 <Repo as EsRepo>::hydrate_nested_from_rows::<<Repo as EsRepo>::QueryError>(
241 &mut by_tag,
242 &mut cursor,
243 &mut entities,
244 )?;
245 let [entity] = entities;
246 Ok(Some(entity))
247 }
248
249 async fn fetch_n_tree(
250 self,
251 op: impl IntoOneTimeExecutor<'_>,
252 first: usize,
253 include_deleted: bool,
254 ) -> Result<(Vec<<Repo as EsRepo>::Entity>, bool), <Repo as EsRepo>::QueryError> {
255 let (root, mut by_tag) = self
256 .fetch_tree_rows::<<Repo as EsRepo>::QueryError>(op, include_deleted)
257 .await?;
258 let (mut entities, more) = EntityEvents::load_n::<<Repo as EsRepo>::Entity>(root, first)?;
259 let mut cursor = 1i32;
260 <Repo as EsRepo>::hydrate_nested_from_rows::<<Repo as EsRepo>::QueryError>(
261 &mut by_tag,
262 &mut cursor,
263 &mut entities,
264 )?;
265 Ok((entities, more))
266 }
267}