pub struct Query<M> { /* private fields */ }Expand description
A query on a model’s table, built with chained filters.
let products = Product::query()
.where_eq("category", "coffee")
.where_op("price", "<", 25_000)
.order_by("name")
.paginate(&db, page, 20)
.await?;Column names are checked against the model; an unknown column or operator makes the query return an error instead of running.
Implementations§
Source§impl<M: Model> Query<M>
impl<M: Model> Query<M>
Sourcepub fn where_op(self, column: &str, op: &str, value: impl ToDbValue) -> Self
pub fn where_op(self, column: &str, op: &str, value: impl ToDbValue) -> Self
Filters with a comparison: =, !=, <>, <, <=, >, >=, like, not like.
like ignores ASCII case on both databases (ILIKE on PostgreSQL, as
SQLite’s LIKE already does).
Sourcepub fn where_like(self, column: &str, pattern: impl ToDbValue) -> Self
pub fn where_like(self, column: &str, pattern: impl ToDbValue) -> Self
Filters on column LIKE pattern, ignoring ASCII case (% and _ are wildcards).
Sourcepub fn where_null(self, column: &str) -> Self
pub fn where_null(self, column: &str) -> Self
Filters on column IS NULL.
Sourcepub fn where_not_null(self, column: &str) -> Self
pub fn where_not_null(self, column: &str) -> Self
Filters on column IS NOT NULL.
Sourcepub fn where_in<V: ToDbValue>(
self,
column: &str,
values: impl IntoIterator<Item = V>,
) -> Self
pub fn where_in<V: ToDbValue>( self, column: &str, values: impl IntoIterator<Item = V>, ) -> Self
Filters on column IN (…); an empty list matches no rows.
Sourcepub fn where_not_in<V: ToDbValue>(
self,
column: &str,
values: impl IntoIterator<Item = V>,
) -> Self
pub fn where_not_in<V: ToDbValue>( self, column: &str, values: impl IntoIterator<Item = V>, ) -> Self
Filters on column NOT IN (…); an empty list matches every row.
Sourcepub fn where_between(
self,
column: &str,
low: impl ToDbValue,
high: impl ToDbValue,
) -> Self
pub fn where_between( self, column: &str, low: impl ToDbValue, high: impl ToDbValue, ) -> Self
low <= column <= high.
Sourcepub fn where_in_query<N: Model>(
self,
column: &str,
sub: Query<N>,
sub_column: &str,
) -> Self
pub fn where_in_query<N: Model>( self, column: &str, sub: Query<N>, sub_column: &str, ) -> Self
Rows whose column is among sub_column’s values in another model’s
query, e.g. products in active categories:
let products = Product::query()
.where_in_query("category_id", Category::where_eq("active", true), "id")
.get(&db)
.await?;Sourcepub fn where_not_in_query<N: Model>(
self,
column: &str,
sub: Query<N>,
sub_column: &str,
) -> Self
pub fn where_not_in_query<N: Model>( self, column: &str, sub: Query<N>, sub_column: &str, ) -> Self
Like where_in_query, keeping the rows whose column is not among
the sub-query’s values.
Sourcepub fn where_has<N: Model>(self, children: Query<N>, foreign_key: &str) -> Self
pub fn where_has<N: Model>(self, children: Query<N>, foreign_key: &str) -> Self
Rows that have at least one related row in children, joined by the
children’s foreign_key column to this model’s id (EXISTS):
products with a 5-star review.
let loved = Product::query()
.where_has(Review::where_eq("stars", 5), "product_id")
.get(&db)
.await?;
let unreviewed = Product::query().where_doesnt_have(Review::query(), "product_id").count(&db).await?;Sourcepub fn where_doesnt_have<N: Model>(
self,
children: Query<N>,
foreign_key: &str,
) -> Self
pub fn where_doesnt_have<N: Model>( self, children: Query<N>, foreign_key: &str, ) -> Self
Rows without any related row in children (NOT EXISTS).
Sourcepub fn where_any(self, group: impl FnOnce(Self) -> Self) -> Self
pub fn where_any(self, group: impl FnOnce(Self) -> Self) -> Self
Any of the conditions group adds must hold (OR), in parentheses:
.where_any(|q| q.where_eq("status", "new").where_op("total", ">", 100)).
Sourcepub fn where_all(self, group: impl FnOnce(Self) -> Self) -> Self
pub fn where_all(self, group: impl FnOnce(Self) -> Self) -> Self
All of the conditions group adds must hold, in parentheses; useful
inside where_any: .where_any(|q| q.where_eq("a", 1).where_all(|q| …)).
Sourcepub fn when(self, condition: bool, add: impl FnOnce(Self) -> Self) -> Self
pub fn when(self, condition: bool, add: impl FnOnce(Self) -> Self) -> Self
Applies add only when condition holds, e.g. an optional search:
.when(!q.is_empty(), |query| query.where_like("name", format!("%{q}%"))).
Sourcepub fn search(self, words: &str) -> Self
pub fn search(self, words: &str) -> Self
The rows matching a full-text search, best matches first (as
where_search then
order_by_relevance); more order_by
calls break ties. The model needs #[model(search = "…")] and its
index: see renox::db::search. A text without any
word changes nothing.
#[derive(Model, serde::Serialize, Default)]
#[model(table = "posts", search = "title, body", soft_deletes)]
struct Post {
id: i64,
title: String,
body: String,
author_id: i64,
deleted_at: Option<DateTime>,
}
let page = Post::query()
.where_eq("author_id", 7)
.search(&q)
.order_by_desc("id")
.paginate(&db, 1, 20)
.await?;Sourcepub fn where_search(self, words: &str) -> Self
pub fn where_search(self, words: &str) -> Self
Keeps the rows matching a full-text search (every word, as a word or the start of one), without ordering them. User input is safe here: only its words reach the database, bound as a value.
Sourcepub fn order_by_relevance(self, words: &str) -> Self
pub fn order_by_relevance(self, words: &str) -> Self
Sorts by how well rows match a full-text search, best first (rows
that don’t match last); call order_by after it to break ties.
Sourcepub fn order_by(self, column: &str) -> Self
pub fn order_by(self, column: &str) -> Self
Sorts by column, ascending; call again to add tie-breakers.
Sourcepub fn order_by_desc(self, column: &str) -> Self
pub fn order_by_desc(self, column: &str) -> Self
Sorts by column, descending; call again to add tie-breakers.
Sourcepub fn latest(self) -> Self
pub fn latest(self) -> Self
Newest first, by created_at when the model has it, otherwise by id.
Sourcepub fn limit(self, limit: u64) -> Self
pub fn limit(self, limit: u64) -> Self
At most limit rows (a limit past i64::MAX means no limit).
Sourcepub fn where_raw<V: ToDbValue>(
self,
sql: &str,
values: impl IntoIterator<Item = V>,
) -> Self
pub fn where_raw<V: ToDbValue>( self, sql: &str, values: impl IntoIterator<Item = V>, ) -> Self
A condition in SQL, for what the builder doesn’t cover (dates, JSON,
full-text search, …), with ? for each value. Column names are yours
to quote; never build sql from user input.
let today = Order::query()
.where_raw("DATE(created_at) = DATE(?)", [renox::db::now()])
.order_by_raw("total DESC, id")
.get(&db)
.await?;Sourcepub fn order_by_raw(self, sql: &str) -> Self
pub fn order_by_raw(self, sql: &str) -> Self
An ORDER BY term in SQL, e.g. "total DESC, id" (no values; never
from user input).
Sourcepub fn group_by(self, column: &str) -> Self
pub fn group_by(self, column: &str) -> Self
Groups rows by column, for select_as and count:
.group_by("user_id").select_as::<(i64, i64), _>(&db, "user_id, COUNT(*)").
Sourcepub fn having_raw<V: ToDbValue>(
self,
sql: &str,
values: impl IntoIterator<Item = V>,
) -> Self
pub fn having_raw<V: ToDbValue>( self, sql: &str, values: impl IntoIterator<Item = V>, ) -> Self
A condition on the groups, in SQL with ? for each value:
.having_raw("COUNT(*) > ?", [2]).
Sourcepub fn lock_for_update(self) -> Self
pub fn lock_for_update(self) -> Self
Locks the matching rows until the transaction ends (FOR UPDATE),
e.g. to read a balance and write it back safely. Use it on &mut tx.
PostgreSQL only: on SQLite a write transaction already holds the whole
database, so start it with db.begin_immediate() instead.
Like lock_for_update, but others may still read-lock (FOR SHARE).
Sourcepub fn with_trashed(self) -> Self
pub fn with_trashed(self) -> Self
Include soft-deleted rows.
Sourcepub fn only_trashed(self) -> Self
pub fn only_trashed(self) -> Self
Only soft-deleted rows.
Sourcepub fn to_sql(&self, dialect: Dialect) -> Result<(String, Vec<DbValue>)>
pub fn to_sql(&self, dialect: Dialect) -> Result<(String, Vec<DbValue>)>
The SELECT this query runs and its values, e.g. to log or debug it.
Sourcepub async fn select_as<'c, T: FromRow, E: Executor<'c>>(
self,
db: E,
columns: &str,
) -> Result<Vec<T>>
pub async fn select_as<'c, T: FromRow, E: Executor<'c>>( self, db: E, columns: &str, ) -> Result<Vec<T>>
Selects columns (SQL, e.g. "user_id, COUNT(*) AS orders") of the
matching rows, with group_by/having_raw, read into a
#[derive(FromRow)] struct or a tuple:
let big_spenders: Vec<(i64, i64)> = Order::where_eq("status", "paid")
.group_by("user_id")
.having_raw("SUM(total) > ?", [1_000_000])
.order_by_raw("2 DESC")
.select_as(&db, "user_id, CAST(SUM(total) AS BIGINT)")
.await?;Sourcepub async fn sum<'c, T: Number, E: Executor<'c>>(
self,
db: E,
column: &str,
) -> Result<T>
pub async fn sum<'c, T: Number, E: Executor<'c>>( self, db: E, column: &str, ) -> Result<T>
The sum of column, 0 without rows: sum::<i64>(…) for whole
numbers (money in its smallest unit), sum::<f64>(…) for measures.
Sourcepub async fn avg<'c, E: Executor<'c>>(
self,
db: E,
column: &str,
) -> Result<Option<f64>>
pub async fn avg<'c, E: Executor<'c>>( self, db: E, column: &str, ) -> Result<Option<f64>>
The average of column, or None without rows.
Sourcepub async fn min<'c, T: FromDb, E: Executor<'c>>(
self,
db: E,
column: &str,
) -> Result<Option<T>>
pub async fn min<'c, T: FromDb, E: Executor<'c>>( self, db: E, column: &str, ) -> Result<Option<T>>
The smallest value of column, or None without rows.
Sourcepub async fn max<'c, T: FromDb, E: Executor<'c>>(
self,
db: E,
column: &str,
) -> Result<Option<T>>
pub async fn max<'c, T: FromDb, E: Executor<'c>>( self, db: E, column: &str, ) -> Result<Option<T>>
The largest value of column, or None without rows.
Sourcepub async fn pluck<'c, T: FromDb, E: Executor<'c>>(
self,
db: E,
column: &str,
) -> Result<Vec<T>>
pub async fn pluck<'c, T: FromDb, E: Executor<'c>>( self, db: E, column: &str, ) -> Result<Vec<T>>
One column of the matching rows, in the query’s order:
Product::query().order_by("name").pluck::<String, _>(&db, "name").
Sourcepub async fn update<'c, E: Executor<'c>>(
self,
db: E,
values: &[(&str, &(dyn ToDbValue + Sync))],
) -> Result<u64>
pub async fn update<'c, E: Executor<'c>>( self, db: E, values: &[(&str, &(dyn ToDbValue + Sync))], ) -> Result<u64>
Sets columns on every matching row (and updated_at when the model
has it); returns how many changed.
Order::where_eq("status", "pending")
.update(&db, &[("status", &"paid"), ("paid_at", &renox::db::now())])
.await?;Sourcepub async fn increment<'c, E: Executor<'c>>(
self,
db: E,
column: &str,
by: i64,
) -> Result<u64>
pub async fn increment<'c, E: Executor<'c>>( self, db: E, column: &str, by: i64, ) -> Result<u64>
Adds by to column on every matching row (negative to subtract),
in the database, so concurrent changes aren’t lost:
Product::where_eq("id", id).increment(&db, "stock", -1).
Sourcepub async fn first_or_404<'c, E: Executor<'c>>(self, db: E) -> Result<M>
pub async fn first_or_404<'c, E: Executor<'c>>(self, db: E) -> Result<M>
Like first, but no row becomes a 404 response.
Sourcepub fn first_or_create<'a>(
self,
db: &'a Db,
make: impl FnOnce() -> M + Send + 'a,
) -> impl Future<Output = Result<M>> + Send + 'a
pub fn first_or_create<'a>( self, db: &'a Db, make: impl FnOnce() -> M + Send + 'a, ) -> impl Future<Output = Result<M>> + Send + 'a
The first matching row, or make() saved as a new one. If another
request creates it at the same moment (a unique index stops the
second insert), the row it created is returned.
Sourcepub async fn chunk<F, Fut>(self, db: &Db, size: u64, each: F) -> Result<u64>
pub async fn chunk<F, Fut>(self, db: &Db, size: u64, each: F) -> Result<u64>
Runs each on the matching rows size at a time, in id order, so a
large table never sits in memory at once. (The query’s own order and
limit don’t apply.)
Sourcepub async fn get<'c, E: Executor<'c>>(self, db: E) -> Result<Vec<M>>
pub async fn get<'c, E: Executor<'c>>(self, db: E) -> Result<Vec<M>>
Runs the query and returns every matching row.
Sourcepub async fn first<'c, E: Executor<'c>>(self, db: E) -> Result<Option<M>>
pub async fn first<'c, E: Executor<'c>>(self, db: E) -> Result<Option<M>>
The first matching row (in the query’s order), or None.
Sourcepub async fn count<'c, E: Executor<'c>>(self, db: E) -> Result<u64>
pub async fn count<'c, E: Executor<'c>>(self, db: E) -> Result<u64>
How many rows match (with group_by: how many groups).
Sourcepub async fn paginate(
self,
db: &Db,
page: u32,
per_page: u32,
) -> Result<Paginated<M>>
pub async fn paginate( self, db: &Db, page: u32, per_page: u32, ) -> Result<Paginated<M>>
One page of results plus the numbers needed to render page links.
page starts at 1; per_page is capped at 1000.
Sourcepub async fn simple_paginate(
self,
db: &Db,
page: u32,
per_page: u32,
) -> Result<SimplePage<M>>
pub async fn simple_paginate( self, db: &Db, page: u32, per_page: u32, ) -> Result<SimplePage<M>>
One page without counting the rows (one query instead of two): for
“previous / next” links on large tables. page starts at 1.
Sourcepub async fn cursor_paginate(
self,
db: &Db,
cursor: Option<&str>,
per_page: u32,
) -> Result<CursorPage<M>>
pub async fn cursor_paginate( self, db: &Db, cursor: Option<&str>, per_page: u32, ) -> Result<CursorPage<M>>
The per_page newest rows after cursor (by id; the query’s own
order doesn’t apply), for APIs and infinite scroll on big tables:
unlike page numbers, rows added meanwhile don’t shift the pages.
“Newest” is the key’s order: creation order for i64, ULIDs and
UUID v7s; a String key pages in text order.
#[derive(serde::Deserialize)]
struct Params { cursor: Option<String> }
async fn events(State(db): State<Db>, Query(p): Query<Params>) -> Result<Json<renox::db::CursorPage<Event>>> {
Ok(Json(Event::query().cursor_paginate(&db, p.cursor.as_deref(), 50).await?))
}Sourcepub async fn first_or_new(
self,
db: &Db,
make: impl FnOnce() -> M + Send,
) -> Result<M>
pub async fn first_or_new( self, db: &Db, make: impl FnOnce() -> M + Send, ) -> Result<M>
The first matching row, or make() unsaved (firstOrNew).
Sourcepub fn update_or_create<'a>(
self,
db: &'a Db,
make: impl FnOnce() -> M + Send + 'a,
change: impl FnOnce(&mut M) + Send + 'a,
) -> impl Future<Output = Result<M>> + Send + 'a
pub fn update_or_create<'a>( self, db: &'a Db, make: impl FnOnce() -> M + Send + 'a, change: impl FnOnce(&mut M) + Send + 'a, ) -> impl Future<Output = Result<M>> + Send + 'a
Changes the first matching row with change, or creates make()
with change applied (updateOrCreate); returns it saved.
let theme = Setting::where_eq("user_id", 7)
.where_eq("key", "theme")
.update_or_create(
&db,
|| Setting { user_id: 7, key: "theme".into(), ..Default::default() },
|s| s.value = "dark".into(),
)
.await?;Sourcepub async fn delete<'c, E: Executor<'c>>(self, db: E) -> Result<u64>
pub async fn delete<'c, E: Executor<'c>>(self, db: E) -> Result<u64>
Deletes every matching row (soft-deletes them for models with soft deletes).
Sourcepub async fn force_delete<'c, E: Executor<'c>>(self, db: E) -> Result<u64>
pub async fn force_delete<'c, E: Executor<'c>>(self, db: E) -> Result<u64>
Removes every matching row, even for models with soft deletes.
Trait Implementations§
Auto Trait Implementations§
impl<M> Freeze for Query<M>
impl<M> RefUnwindSafe for Query<M>
impl<M> Send for Query<M>
impl<M> Sync for Query<M>
impl<M> Unpin for Query<M>
impl<M> UnsafeUnpin for Query<M>
impl<M> UnwindSafe for Query<M>
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,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
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