Trait diesel::expression::Selectable
source · pub trait Selectable<DB: Backend> {
type SelectExpression: Expression;
// Required method
fn construct_selection() -> Self::SelectExpression;
}
Expand description
Trait indicating that a record can be selected and queried from the database.
Types which implement Selectable
represent the select clause of a SQL query.
Use SelectableHelper::as_select()
to construct the select clause. Once you
called .select(YourType::as_select())
we enforce at the type system level that you
use the same type to load the query result into.
The constructed select clause can contain arbitrary expressions coming from different tables. The corresponding derive provides a simple way to construct a select clause matching fields to the corresponding table columns.
§Examples
If you just want to construct a select clause using an existing struct, you can use
#[derive(Selectable)]
, See #[derive(Selectable)]
for details.
use schema::users;
#[derive(Queryable, PartialEq, Debug, Selectable)]
struct User {
id: i32,
name: String,
}
let first_user = users.select(User::as_select()).first(connection)?;
let expected = User { id: 1, name: "Sean".into() };
assert_eq!(expected, first_user);
Alternatively, we can implement the trait for our struct manually.
use schema::users;
use diesel::prelude::{Queryable, Selectable};
use diesel::backend::Backend;
#[derive(Queryable, PartialEq, Debug)]
struct User {
id: i32,
name: String,
}
impl<DB> Selectable<DB> for User
where
DB: Backend
{
type SelectExpression = (users::id, users::name);
fn construct_selection() -> Self::SelectExpression {
(users::id, users::name)
}
}
let first_user = users.select(User::as_select()).first(connection)?;
let expected = User { id: 1, name: "Sean".into() };
assert_eq!(expected, first_user);
When selecting from joined tables, you can select from a
composition of types that implement Selectable
. The simplest way
is to use a tuple of all the types you wish to select.
use schema::{users, posts};
#[derive(Debug, PartialEq, Queryable, Selectable)]
struct User {
id: i32,
name: String,
}
#[derive(Debug, PartialEq, Queryable, Selectable)]
struct Post {
id: i32,
user_id: i32,
title: String,
}
let (first_user, first_post) = users::table
.inner_join(posts::table)
.select(<(User, Post)>::as_select())
.first(connection)?;
let expected_user = User { id: 1, name: "Sean".into() };
assert_eq!(expected_user, first_user);
let expected_post = Post { id: 1, user_id: 1, title: "My first post".into() };
assert_eq!(expected_post, first_post);
If you want to load only a subset of fields, you can create types with those fields and use them in the composition.
use schema::{users, posts};
#[derive(Debug, PartialEq, Queryable, Selectable)]
struct User {
id: i32,
name: String,
}
#[derive(Debug, PartialEq, Queryable, Selectable)]
#[diesel(table_name = posts)]
struct PostTitle {
title: String,
}
let (first_user, first_post_title) = users::table
.inner_join(posts::table)
.select(<(User, PostTitle)>::as_select())
.first(connection)?;
let expected_user = User { id: 1, name: "Sean".into() };
assert_eq!(expected_user, first_user);
let expected_post_title = PostTitle { title: "My first post".into() };
assert_eq!(expected_post_title, first_post_title);
You are not limited to using only tuples to build the composed
type. The Selectable
derive macro allows
you to embed other types. This is useful when you want to
implement methods or traits on the composed type.
use schema::{users, posts};
#[derive(Debug, PartialEq, Queryable, Selectable)]
struct User {
id: i32,
name: String,
}
#[derive(Debug, PartialEq, Queryable, Selectable)]
#[diesel(table_name = posts)]
struct PostTitle {
title: String,
}
#[derive(Debug, PartialEq, Queryable, Selectable)]
struct UserPost {
#[diesel(embed)]
user: User,
#[diesel(embed)]
post_title: PostTitle,
}
let first_user_post = users::table
.inner_join(posts::table)
.select(UserPost::as_select())
.first(connection)?;
let expected_user_post = UserPost {
user: User {
id: 1,
name: "Sean".into(),
},
post_title: PostTitle {
title: "My first post".into(),
},
};
assert_eq!(expected_user_post, first_user_post);
It is also possible to specify an entirely custom select expression
for fields when deriving Selectable
.
This is useful for example to
- avoid nesting types, or to
- populate fields with values other than table columns, such as
the result of an SQL function like
CURRENT_TIMESTAMP()
or a custom SQL function.
The select expression is specified via the select_expression
parameter.
Query fragments created using dsl::auto_type
are supported, which
may be useful as the select expression gets large: it may not be practical to inline it in
the attribute then.
The type of the expression is usually inferred. If it can’t be fully inferred automatically, one may either:
- Put type annotations in inline blocks in the query fragment itself
- Use a dedicated
dsl::auto_type
function asselect_expression
and usedsl::auto_type
’s type annotation features - Specify the type of the expression using the
select_expression_type
attribute
use schema::{users, posts};
use diesel::dsl;
#[derive(Debug, PartialEq, Queryable, Selectable)]
struct User {
id: i32,
name: String,
}
#[derive(Debug, PartialEq, Queryable, Selectable)]
#[diesel(table_name = posts)]
struct PostTitle {
title: String,
}
#[derive(Debug, PartialEq, Queryable, Selectable)]
struct UserPost {
#[diesel(select_expression = users::columns::id)]
#[diesel(select_expression_type = users::columns::id)]
id: i32,
#[diesel(select_expression = users::columns::name)]
name: String,
#[diesel(select_expression = complex_fragment_for_title())]
title: String,
#[diesel(select_expression = diesel::dsl::now)]
access_time: chrono::NaiveDateTime,
#[diesel(select_expression = users::columns::id.eq({let id: i32 = FOO; id}))]
user_id_is_foo: bool,
}
const FOO: i32 = 42; // Type of FOO can't be inferred automatically in the select_expression
#[dsl::auto_type]
fn complex_fragment_for_title() -> _ {
// See the `#[dsl::auto_type]` documentation for examples of more complex usage
posts::columns::title
}
let first_user_post = users::table
.inner_join(posts::table)
.select(UserPost::as_select())
.first(connection)?;
let expected_user_post = UserPost {
id: 1,
name: "Sean".into(),
title: "My first post".into(),
access_time: first_user_post.access_time,
user_id_is_foo: false,
};
assert_eq!(expected_user_post, first_user_post);
Required Associated Types§
sourcetype SelectExpression: Expression
type SelectExpression: Expression
The expression you’d like to select.
This is typically a tuple of corresponding to the table columns of your struct’s fields.
Required Methods§
sourcefn construct_selection() -> Self::SelectExpression
fn construct_selection() -> Self::SelectExpression
Construct an instance of the expression