pub struct QueryBuilder<'a, T: Model> { /* private fields */ }model-mysql or model-postgres or model-sqlite only.Expand description
Fluent async query builder for SELECT, COUNT, DELETE, and UPDATE.
For a model with a soft-delete column (#[soft_delete]) every operation
sees only rows not deleted, unless the chain has
with_deleted or
only_deleted; delete sets the
column and force_delete removes rows. With
#[timestamps] / #[version], update also sets
updated_at and increments the version (18.30.0).
use rust_web_server::model::{DbPool, Order, QueryBuilder};
let pool = DbPool::memory().await?;
// pool.execute("CREATE TABLE users …", &[]).await?;
// let users: Vec<User> = QueryBuilder::<User>::new(&pool)
// .where_eq("active", true)
// .order_by("name", Order::Asc)
// .limit(10)
// .fetch_all()
// .await?;Implementations§
Source§impl<'a, T: Model> QueryBuilder<'a, T>
impl<'a, T: Model> QueryBuilder<'a, T>
pub fn new(pool: &'a DbPool) -> Self
Sourcepub fn with_deleted(self) -> Self
pub fn with_deleted(self) -> Self
Soft-deleted rows too (no effect on a model without #[soft_delete]).
Sourcepub fn only_deleted(self) -> Self
pub fn only_deleted(self) -> Self
Soft-deleted rows only (none for a model without #[soft_delete]).
Sourcepub fn where_in<I>(self, col: &str, values: I) -> Self
pub fn where_in<I>(self, col: &str, values: I) -> Self
Add a col IN (?, ?, …) filter (18.30.0); no values match no rows.
Sourcepub fn where_null(self, col: &str) -> Self
pub fn where_null(self, col: &str) -> Self
Add a col IS NULL filter (18.30.0).
Sourcepub fn where_not_null(self, col: &str) -> Self
pub fn where_not_null(self, col: &str) -> Self
Add a col IS NOT NULL filter (18.30.0).
Sourcepub fn scope(self, scope: impl FnOnce(Self) -> Self) -> Self
pub fn scope(self, scope: impl FnOnce(Self) -> Self) -> Self
Apply a scope (18.30.0): a reusable function from a query to a narrower one. Scopes are plain functions (or closures), so they take arguments and compose:
fn published(q: QueryBuilder<'_, Post>) -> QueryBuilder<'_, Post> {
q.where_eq("published", true).where_null("hidden_at")
}
fn by(author: i64) -> impl FnOnce(QueryBuilder<'_, Post>) -> QueryBuilder<'_, Post> {
move |q| q.where_eq("author_id", author)
}
let mine = Post::query(&pool).scope(published).scope(by(7)).fetch_all().await?;For Post::query(&pool).published(), put them in an extension
trait:
pub trait PostScopes {
fn published(self) -> Self;
}
impl PostScopes for QueryBuilder<'_, Post> {
fn published(self) -> Self {
self.where_eq("published", true)
}
}Sourcepub fn with<R: Model + Clone + 'static>(
self,
relation: Relation<T, R>,
) -> EagerQuery<'a, T>where
T: 'static,
pub fn with<R: Model + Clone + 'static>(
self,
relation: Relation<T, R>,
) -> EagerQuery<'a, T>where
T: 'static,
Load relation for every row the query answers (18.30.0): one more
query per relation, however many rows. The fetch_* of the
EagerQuery it returns answer
Loaded models.
let posts = Post::query(&pool).with(Post::comments()).fetch_all().await?;
let first_comments: &[Comment] = posts[0].related(&Post::comments());Sourcepub fn filter(self, expr: &str, params: Vec<Value>) -> Self
pub fn filter(self, expr: &str, params: Vec<Value>) -> Self
Add a raw SQL filter fragment with bind parameters.
.filter("age >= ?", vec![Value::Int(18)])Sourcepub async fn fetch_all(self) -> Result<Vec<T>, DbError>
pub async fn fetch_all(self) -> Result<Vec<T>, DbError>
Execute SELECT * FROM table WHERE … ORDER BY … LIMIT … OFFSET ….
Sourcepub async fn fetch_one(self) -> Result<Option<T>, DbError>
pub async fn fetch_one(self) -> Result<Option<T>, DbError>
Execute SELECT * … LIMIT 1 and return the first result.
Sourcepub async fn count(self) -> Result<i64, DbError>
pub async fn count(self) -> Result<i64, DbError>
Execute SELECT COUNT(*) FROM table WHERE … (without the chain’s
ORDER BY, which PostgreSQL refuses next to an aggregate).
Sourcepub async fn delete(self) -> Result<(), DbError>
pub async fn delete(self) -> Result<(), DbError>
Execute DELETE FROM table WHERE … — for a soft-delete model,
UPDATE table SET deleted_at = now WHERE … AND deleted_at IS NULL
instead (force_delete removes the rows).
Sourcepub async fn force_delete(self) -> Result<u64, DbError>
pub async fn force_delete(self) -> Result<u64, DbError>
Execute DELETE FROM table WHERE …, removing the rows whatever the
soft-delete column says (within the query’s scope: add
with_deleted to include deleted rows).
Answers the number of rows removed.
Sourcepub async fn restore(self) -> Result<u64, DbError>
pub async fn restore(self) -> Result<u64, DbError>
Clear the soft-delete column of the matching deleted rows; answers
how many. An error for a model without #[soft_delete].
Sourcepub async fn update(self, col: &str, val: impl ToColumn) -> Result<(), DbError>
pub async fn update(self, col: &str, val: impl ToColumn) -> Result<(), DbError>
Execute UPDATE table SET col = ? WHERE … (also updated_at = now
and version = version + 1 when the model has them).
Sourcepub async fn paginate(
self,
page: u64,
per_page: u64,
) -> Result<Page<T>, DbError>
pub async fn paginate( self, page: u64, per_page: u64, ) -> Result<Page<T>, DbError>
Offset-paginated fetch: runs a COUNT(*) (with the same filters, no
limit/offset) and a SELECT … LIMIT … OFFSET …, and wraps both into a
Page. page is 1-based; both page and per_page are clamped to
a minimum of 1. Overrides any .limit()/.offset() set earlier in
the chain — page/per_page are authoritative.
Two queries, not one — if you’re paginating a very large, frequently
appended-to table and don’t need total_pages, Self::paginate_after
(keyset pagination) needs only one.
Sourcepub async fn paginate_after(
self,
cursor: Option<&str>,
per_page: u64,
) -> Result<CursorPage<T>, DbError>
pub async fn paginate_after( self, cursor: Option<&str>, per_page: u64, ) -> Result<CursorPage<T>, DbError>
Cursor (keyset) paginated fetch: orders by the primary key ascending,
fetches per_page + 1 rows to cheaply detect whether there’s a next
page, and returns a CursorPage whose next_cursor is the last
returned row’s primary key (as a string) — pass it back as cursor to
get the next page. cursor: None fetches the first page.
A single query, unlike Self::paginate — no COUNT(*), and no
OFFSET to skip over on every subsequent page, which is what makes
keyset pagination scale on large tables where offset pagination gets
slower page by page. The tradeoff: no total_items/total_pages, and
only forward iteration (no jumping to an arbitrary page).
Overrides any .order_by() set earlier in the chain — keyset
pagination requires ordering by the cursor column. Requires the
primary key to be numeric (parsed as i64); returns Err if cursor
isn’t a valid integer.
Trait Implementations§
Auto Trait Implementations§
impl<'a, T> !RefUnwindSafe for QueryBuilder<'a, T>
impl<'a, T> !UnwindSafe for QueryBuilder<'a, T>
impl<'a, T> Freeze for QueryBuilder<'a, T>where
PhantomData<T>: Freeze,
impl<'a, T> Send for QueryBuilder<'a, T>where
PhantomData<T>: Send,
impl<'a, T> Sync for QueryBuilder<'a, T>where
PhantomData<T>: Sync,
impl<'a, T> Unpin for QueryBuilder<'a, T>where
PhantomData<T>: Unpin,
impl<'a, T> UnsafeUnpin for QueryBuilder<'a, T>where
PhantomData<T>: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more