1use crate::route::route_for;
43use crate::{repo, Entity, Executor, Result, ShardRoute};
44use core::future::Future;
45use core::marker::PhantomData;
46use sea_query::{
47 Alias, Asterisk, ColumnRef, Cond, Expr, Func, IntoColumnRef, JoinType, Order, Query as SqQuery,
48 SimpleExpr, Value,
49};
50
51pub struct Query<E> {
54 cond: Cond,
55 joins: Vec<(JoinType, String, SimpleExpr)>,
56 orders: Vec<(ColumnRef, Order)>,
57 limit: Option<u64>,
58 offset: Option<u64>,
59 route: ShardRoute,
60 _entity: PhantomData<E>,
61}
62
63impl<E: Entity> Default for Query<E> {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl<E: Entity> Query<E> {
70 pub fn new() -> Self {
72 Self {
73 cond: Cond::all(),
74 joins: Vec::new(),
75 orders: Vec::new(),
76 limit: None,
77 offset: None,
78 route: ShardRoute::Global,
79 _entity: PhantomData,
80 }
81 }
82
83 fn add(mut self, expr: SimpleExpr) -> Self {
84 let cond = core::mem::replace(&mut self.cond, Cond::all());
85 self.cond = cond.add(expr);
86 self
87 }
88
89 pub fn where_eq(mut self, column: &str, value: impl Into<Value>) -> Self {
96 let val: Value = value.into();
97 if matches!(self.route, ShardRoute::Global) {
98 self.route = route_for::<E>(column, &val);
99 }
100 self.add(Expr::col(col_ref::<E>(column)).eq(val))
101 }
102
103 pub fn where_ne(self, column: &str, value: impl Into<Value>) -> Self {
105 self.add(Expr::col(col_ref::<E>(column)).ne(value.into()))
106 }
107
108 pub fn where_gt(self, column: &str, value: impl Into<Value>) -> Self {
110 self.add(Expr::col(col_ref::<E>(column)).gt(value.into()))
111 }
112
113 pub fn where_gte(self, column: &str, value: impl Into<Value>) -> Self {
115 self.add(Expr::col(col_ref::<E>(column)).gte(value.into()))
116 }
117
118 pub fn where_lt(self, column: &str, value: impl Into<Value>) -> Self {
120 self.add(Expr::col(col_ref::<E>(column)).lt(value.into()))
121 }
122
123 pub fn where_lte(self, column: &str, value: impl Into<Value>) -> Self {
125 self.add(Expr::col(col_ref::<E>(column)).lte(value.into()))
126 }
127
128 pub fn where_like(self, column: &str, pattern: &str) -> Self {
130 self.add(Expr::col(col_ref::<E>(column)).like(pattern))
131 }
132
133 pub fn where_not_like(self, column: &str, pattern: &str) -> Self {
135 self.add(Expr::col(col_ref::<E>(column)).not_like(pattern))
136 }
137
138 pub fn where_in<V: Into<Value>>(
140 self,
141 column: &str,
142 values: impl IntoIterator<Item = V>,
143 ) -> Self {
144 let vals: Vec<Value> = values.into_iter().map(Into::into).collect();
145 self.add(Expr::col(col_ref::<E>(column)).is_in(vals))
146 }
147
148 pub fn where_not_in<V: Into<Value>>(
150 self,
151 column: &str,
152 values: impl IntoIterator<Item = V>,
153 ) -> Self {
154 let vals: Vec<Value> = values.into_iter().map(Into::into).collect();
155 self.add(Expr::col(col_ref::<E>(column)).is_not_in(vals))
156 }
157
158 pub fn where_null(self, column: &str) -> Self {
160 self.add(Expr::col(col_ref::<E>(column)).is_null())
161 }
162
163 pub fn where_not_null(self, column: &str) -> Self {
165 self.add(Expr::col(col_ref::<E>(column)).is_not_null())
166 }
167
168 pub fn filter(mut self, cond: Cond) -> Self {
172 let existing = core::mem::replace(&mut self.cond, Cond::all());
173 self.cond = existing.add(cond);
174 self
175 }
176
177 pub fn join(self, foreign_table: &str, local_col: &str, foreign_col: &str) -> Self {
182 self.join_with(JoinType::InnerJoin, foreign_table, local_col, foreign_col)
183 }
184
185 pub fn left_join(self, foreign_table: &str, local_col: &str, foreign_col: &str) -> Self {
187 self.join_with(JoinType::LeftJoin, foreign_table, local_col, foreign_col)
188 }
189
190 fn join_with(
191 mut self,
192 kind: JoinType,
193 foreign_table: &str,
194 local_col: &str,
195 foreign_col: &str,
196 ) -> Self {
197 let on = Expr::col((Alias::new(E::table()), Alias::new(local_col)))
198 .equals((Alias::new(foreign_table.to_owned()), Alias::new(foreign_col.to_owned())));
199 self.joins.push((kind, foreign_table.to_owned(), on));
200 self
201 }
202
203 pub fn order_by_asc(mut self, column: &str) -> Self {
207 self.orders.push((col_ref::<E>(column), Order::Asc));
208 self
209 }
210
211 pub fn order_by_desc(mut self, column: &str) -> Self {
213 self.orders.push((col_ref::<E>(column), Order::Desc));
214 self
215 }
216
217 pub fn limit(mut self, n: u64) -> Self {
219 self.limit = Some(n);
220 self
221 }
222
223 pub fn offset(mut self, n: u64) -> Self {
225 self.offset = Some(n);
226 self
227 }
228
229 fn render_select(self, dialect: crate::Dialect) -> (ShardRoute, String, Vec<Value>) {
243 let mut stmt = SqQuery::select();
244 stmt.from(Alias::new(E::table()));
245 for c in E::columns() {
246 stmt.column((Alias::new(E::table()), Alias::new(c.name)));
247 }
248 for (kind, table, on) in &self.joins {
249 stmt.join(*kind, Alias::new(table.clone()), on.clone());
250 }
251 stmt.cond_where(self.cond.clone());
252 for (col, ord) in &self.orders {
253 stmt.order_by(col.clone(), ord.clone());
254 }
255 if let Some(l) = self.limit {
256 stmt.limit(l);
257 }
258 if let Some(o) = self.offset {
259 stmt.offset(o);
260 }
261
262 let (sql, params) = dialect.build_query(&stmt);
263 (self.route, sql, params.0)
264 }
265
266 fn render_count(self, dialect: crate::Dialect) -> (ShardRoute, String, Vec<Value>) {
268 let mut stmt = SqQuery::select();
269 stmt.from(Alias::new(E::table()));
270 for (kind, table, on) in &self.joins {
271 stmt.join(*kind, Alias::new(table.clone()), on.clone());
272 }
273 stmt.cond_where(self.cond.clone());
274 stmt.expr_as(Func::count(Expr::col(Asterisk)), Alias::new("cnt"));
275
276 let (sql, params) = dialect.build_query(&stmt);
277 (self.route, sql, params.0)
278 }
279
280 fn render_update(
282 self,
283 dialect: crate::Dialect,
284 set: Vec<(&str, Value)>,
285 ) -> (ShardRoute, String, Vec<Value>) {
286 let mut stmt = SqQuery::update();
287 stmt.table(Alias::new(E::table()));
288 for (c, v) in set {
289 stmt.value(Alias::new(c.to_owned()), v);
290 }
291 stmt.cond_where(self.cond.clone());
292
293 let (sql, params) = dialect.build_query(&stmt);
294 (self.route, sql, params.0)
295 }
296
297 fn render_increment(
299 self,
300 dialect: crate::Dialect,
301 column: &str,
302 by: i64,
303 ) -> (ShardRoute, String, Vec<Value>) {
304 let mut stmt = SqQuery::update();
305 stmt.table(Alias::new(E::table()));
306 stmt.value(Alias::new(column.to_owned()), Expr::col(Alias::new(column.to_owned())).add(by));
307 stmt.cond_where(self.cond.clone());
308
309 let (sql, params) = dialect.build_query(&stmt);
310 (self.route, sql, params.0)
311 }
312
313 fn render_delete(self, dialect: crate::Dialect) -> (ShardRoute, String, Vec<Value>) {
315 let mut stmt = SqQuery::delete();
316 stmt.from_table(Alias::new(E::table()));
317 stmt.cond_where(self.cond.clone());
318
319 let (sql, params) = dialect.build_query(&stmt);
320 (self.route, sql, params.0)
321 }
322
323 pub fn all<'a, X: Executor>(self, exec: &'a X) -> impl Future<Output = Result<Vec<E>>> + 'a
341 where
342 E: 'a,
343 {
344 let (route, sql, params) = self.render_select(exec.dialect());
345 async move {
346 let rows = exec.fetch_all_routed(route, &sql, params).await?;
347 rows.iter().map(|r| E::from_row(r.as_ref())).collect()
348 }
349 }
350
351 pub fn first<'a, X: Executor>(self, exec: &'a X) -> impl Future<Output = Result<Option<E>>> + 'a
353 where
354 E: 'a,
355 {
356 let rows = self.limit(1).all(exec);
357 async move { Ok(rows.await?.into_iter().next()) }
358 }
359
360 pub fn count<'a, X: Executor>(self, exec: &'a X) -> impl Future<Output = Result<u64>> + 'a
362 where
363 E: 'a,
364 {
365 let (route, sql, params) = self.render_count(exec.dialect());
366 async move {
367 let rows = exec.fetch_all_routed(route, &sql, params).await?;
368 let n = match rows.first() {
369 Some(r) => r.get_i64("cnt")?.unwrap_or(0),
370 None => 0,
371 };
372 Ok(n.max(0) as u64)
373 }
374 }
375
376 pub fn update<'a, X: Executor>(
387 self,
388 exec: &'a X,
389 set: Vec<(&str, Value)>,
390 ) -> impl Future<Output = Result<u64>> + 'a {
391 let (route, sql, params) = self.render_update(exec.dialect(), set);
392 async move { Ok(exec.execute_routed(route, &sql, params).await?.rows_affected) }
393 }
394
395 pub fn increment<'a, X: Executor>(
399 self,
400 exec: &'a X,
401 column: &str,
402 by: i64,
403 ) -> impl Future<Output = Result<u64>> + 'a {
404 let (route, sql, params) = self.render_increment(exec.dialect(), column, by);
405 async move { Ok(exec.execute_routed(route, &sql, params).await?.rows_affected) }
406 }
407
408 pub fn delete<'a, X: Executor>(self, exec: &'a X) -> impl Future<Output = Result<u64>> + 'a {
412 let (route, sql, params) = self.render_delete(exec.dialect());
413 async move { Ok(exec.execute_routed(route, &sql, params).await?.rows_affected) }
414 }
415
416 pub fn first_or_create<'a, X: Executor>(
420 self,
421 exec: &'a X,
422 default: E,
423 ) -> impl Future<Output = Result<E>> + 'a
424 where
425 E: 'a,
426 {
427 let existing = self.first(exec);
430
431 async move {
432 if let Some(found) = existing.await? {
433 return Ok(found);
434 }
435 let id = repo::insert(exec, &default).await?;
436 if id > 0 {
437 if let Some(created) = repo::find_by_pk(exec, id).await? {
438 return Ok(created);
439 }
440 }
441 Ok(default)
442 }
443 }
444}
445
446fn col_ref<E: Entity>(spec: &str) -> ColumnRef {
448 if let Some((t, c)) = spec.split_once('.') {
449 (Alias::new(t.to_owned()), Alias::new(c.to_owned())).into_column_ref()
450 } else {
451 (Alias::new(E::table()), Alias::new(spec.to_owned())).into_column_ref()
452 }
453}