Hello everyone,
I'm currently working on extending the filtering system for diesel
which I asked a question about around one year ago (here). The new version I'm working on should support filtering on related tables.
The database schema I'm using for testing is the following:
diesel::table! {
bookstore_addresses (bookstore_id) {
bookstore_id -> Uuid,
street -> Varchar,
city -> Varchar,
zip -> Varchar,
state -> Nullable<Varchar>,
country -> Varchar,
}
}
diesel::table! {
bookstores (id) {
id -> Uuid,
name -> Varchar,
}
}
diesel::allow_tables_to_appear_in_same_query!(
bookstore_addresses, bookstores,
);
Usage of the filtering system should ideally look something like this:
use schema::{bookstore_addresses, bookstores};
fn main() {
// Defining a WHERE-clause with nested enums, which filters for
// all bookstores with a zip code of "12345".
let filter = BookstoreFilter::Address(
BookstoreAddressFilter::Zip(
StrFilter::Eq("12345".into())
)
);
let query = bookstores::table
// The zip code value is not stored on the "bookstores" table
// but on the related "bookstore_addresses" table.
.left_join(bookstore_addresses::table.on(
bookstore_addresses::bookstore_id.eq(bookstores::id)
))
// The filter enum is converted to a diesel expression
.filter(filter.to_expression());
}
The BookstoreFilter
and BookstoreAddressFilter
enums are defined as follows:
enum BookstoreFilter {
Id(dqg::UuidFilter),
Address(BookstoreAddressFilter),
}
enum BookstoreAddressFilter {
Zip(dqg::StrFilter),
}
With their corresponding impl
blocks which define their to_expression
method:
impl BookstoreFilter {
fn to_expression(self) -> Box<dyn BoxableExpression<
bookstores::table,
diesel::pg::Pg,
SqlType = diesel::sql_types::Bool
>> {
match self {
Self::Id(f) => match f {
UuidFilter::Eq(value) => Box::new(
bookstores::id.eq(value)
),
UuidFilter::EqAny(values) => Box::new(
bookstores::id.eq_any(values)
),
},
Self::Address(f) => f.to_expression(), // <- error occurs here
}
}
}
impl BookstoreAddressFilter {
fn to_expression(self) -> Box<dyn BoxableExpression<
bookstore_addresses::table,
diesel::pg::Pg,
SqlType = diesel::sql_types::Bool
>> {
match self {
Self::Zip(f) => match f {
StrFilter::Eq(value) => Box::new(
bookstore_addresses::zip.eq(value)
),
StrFilter::Contains(value) => Box::new(
bookstore_addresses::zip.like(format!("%{}%", value))
),
_ => todo!(),
}
}
}
}
Calling to.expression()
in the impl
block of BookstoreFilter
causes an error, because the types of the match arms differ. This makes sense, as the other match arms return values of type...
Box<dyn BoxableExpression<
bookstores::table,
diesel::pg::Pg,
SqlType = diesel::sql_types::Bool
>>
...whereas the to_expression()
call returns a value of type...
Box<dyn BoxableExpression<
bookstore_addresses::table, // <- the "query source" is different
diesel::pg::Pg,
SqlType = diesel::sql_types::Bool
>>
Is there a common type which the to_expression
method could return or even a completely different approach to compose diesel
expressions like I described?
Thank you very much in advance!