Trait diesel::expression::BoxableExpression
source · pub trait BoxableExpression<QS, DB, GB = (), IsAggregate = No>where
DB: Backend,
Self: Expression + SelectableExpression<QS> + QueryFragment<DB> + Send,{ }
Expand description
Helper trait used when boxing expressions.
In Rust you cannot create a trait object with more than one trait.
This type has all of the additional traits you would want when using
Box<Expression>
as a single trait object.
By default BoxableExpression
is not usable in queries that have a custom
group by clause. Setting the generic parameters GB
and IsAggregate
allows
to configure the expression to be used with a specific group by clause.
This is typically used as the return type of a function. For cases where you want to dynamically construct a query, boxing the query is usually more ergonomic.
Examples
Usage without group by clause
use diesel::sql_types::Bool;
enum Search {
Id(i32),
Name(String),
}
type DB = diesel::sqlite::Sqlite;
fn find_user(search: Search) -> Box<dyn BoxableExpression<users::table, DB, SqlType = Bool>> {
match search {
Search::Id(id) => Box::new(users::id.eq(id)),
Search::Name(name) => Box::new(users::name.eq(name)),
}
}
let user_one = users::table
.filter(find_user(Search::Id(1)))
.first(conn)?;
assert_eq!((1, String::from("Sean")), user_one);
let tess = users::table
.filter(find_user(Search::Name("Tess".into())))
.first(conn)?;
assert_eq!((2, String::from("Tess")), tess);
Allow usage with group by clause
use diesel::sql_types::Text;
use diesel::dsl;
use diesel::expression::ValidGrouping;
enum NameOrConst {
Name,
Const(String),
}
type DB = diesel::sqlite::Sqlite;
fn selection<GB>(
selection: NameOrConst
) -> Box<
dyn BoxableExpression<
users::table,
DB,
GB,
<users::name as ValidGrouping<GB>>::IsAggregate,
SqlType = Text
>
>
where
users::name: BoxableExpression<
users::table,
DB,
GB,
<users::name as ValidGrouping<GB>>::IsAggregate,
SqlType = Text
> + ValidGrouping<GB>,
{
match selection {
NameOrConst::Name => Box::new(users::name),
NameOrConst::Const(name) => Box::new(name.into_sql::<Text>()),
}
}
let user_one = users::table
.select(selection(NameOrConst::Name))
.first::<String>(conn)?;
assert_eq!(String::from("Sean"), user_one);
let with_name = users::table
.group_by(users::name)
.select(selection(NameOrConst::Const("Jane Doe".into())))
.first::<String>(conn)?;
assert_eq!(String::from("Jane Doe"), with_name);
More advanced query source
This example is a bit contrived, but in general, if you want to for example filter based on
different criteria on a joined table, you can use InnerJoinQuerySource
and
LeftJoinQuerySource
in the QS parameter of BoxableExpression
.
use diesel::sql_types::Bool;
use diesel::dsl::InnerJoinQuerySource;
enum UserPostFilter {
User(i32),
Post(i32),
}
type DB = diesel::sqlite::Sqlite;
fn filter_user_posts(
filter: UserPostFilter,
) -> Box<dyn BoxableExpression<InnerJoinQuerySource<users::table, posts::table>, DB, SqlType = Bool>>
{
match filter {
UserPostFilter::User(user_id) => Box::new(users::id.eq(user_id)),
UserPostFilter::Post(post_id) => Box::new(posts::id.eq(post_id)),
}
}
let post_by_user_one = users::table
.inner_join(posts::table)
.filter(filter_user_posts(UserPostFilter::User(2)))
.select((posts::title, users::name))
.first::<(String, String)>(conn)?;
assert_eq!(
("My first post too".to_string(), "Tess".to_string()),
post_by_user_one
);
Trait Implementations§
source§impl<'a, QS, ST, DB, GB, IsAggregate> QueryId for dyn BoxableExpression<QS, DB, GB, IsAggregate, SqlType = ST> + 'a
impl<'a, QS, ST, DB, GB, IsAggregate> QueryId for dyn BoxableExpression<QS, DB, GB, IsAggregate, SqlType = ST> + 'a
source§const HAS_STATIC_QUERY_ID: bool = false
const HAS_STATIC_QUERY_ID: bool = false
Self
be uniquely identified by its type? Read more