Semi-long explanation but just giving as much info as I can - would really appreciate help
I have the following table structure
table! {
use diesel::sql_types::*;
items (id) {
id -> Int4,
name -> Text,
// Some other irrelevant fields
}
}
table! {
use diesel::sql_types::*;
weapons (item_id) {
item_id -> Int4,
primary_end -> Int4,
secondary_end -> Nullable<Int4>,
// Some other irrelevant fields
}
}
table! {
use diesel::sql_types::*;
use crate::db::sql_types::*;
weapon_ends (id) {
id -> Int4,
range -> Int2,
// Some other irrelevant fields
}
}
joinable!(weapons -> items (item_id));
So basically, a weapon belongs to/is an item and a weapon has a primary end and possibly a secondary end as well. Notice the joinable! on weapons -> items since there’s a one-to-one correspondance. However, weapons are not joinable! on weapon_ends.
I want to extract all the items that are weapons which have a weapon end (either end) with a certain criteria. For this I’ve been doing the following:
let mut results = items::table
.select((items::name,))
.into_boxed();
results = results.filter(items::id.eq(any(weapons::table
.select(weapons::item_id)
.left_join(weapon_ends::table.on(weapons::primary_end.eq(weapon_ends::id).or(weapons::secondary_end.eq(weapon_ends::id.nullable()))))
.filter(weapon_ends::range.le(user_input_minimum_range))
)))
And this does work. But, I have a lot of these filters and I’d rather avoid doing a lot of these inner select queries, so I thought I could make a function to return those inner weapons_ends filters like so:
type WeaponEndsFilter = Box<dyn BoxableExpression<weapon_ends::table, Pg, SqlType = Bool>>;
pub fn get_weapon_details_filters(...) -> Option<WeaponEndsFilter> {
let mut filters: Vec<WeaponEndsFilter> = Vec::new();
filters.push(Box::new(weapon_ends::range.le(user_input_minimum_range)))
filters.push(Box::new(weapon_ends::some_other_column.le(some_other_input)))
// Etc...
filters.into_iter().fold_first(|acc, f| {
Box::new(acc.and(f))
})
}
And this function does compile. However, now I try to plug it back in my previous solution:
if let Some(f) = get_weapon_details_filters(...) {
results = results.filter(items::id.eq(any(weapons::table
.select(weapons::item_id)
.left_join(weapon_ends::table.on(weapons::primary_end.eq(weapon_ends::id).or(weapons::secondary_end.eq(weapon_ends::id.nullable()))))
.filter(f)
)))
}
Now the compiler gets very mad at me. Concrete error here:
Checking SorteKanin v0.1.0 (X:\SorteKanin\Documents\GitHub\SorteKanin)
warning: unused imports: `JoinOnDsl`, `NullableExpressionMethods`
--> src\db\helpers.rs:2:89
|
2 | use diesel::{QueryDsl, ExpressionMethods, BoolExpressionMethods, TextExpressionMethods, NullableExpressionMethods, JoinOnDsl};
| ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0277]: the trait bound `dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>: diesel::AppearsOnTable<diesel::query_source::joins::Join<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, db::schema::items::table, diesel::query_source::joins::Inner>>` is not satisfied
--> src\subpages\archive\views\browse.rs:269:28
|
269 | results = results.filter(items::id.eq(any(
| __________________________________^
270 | | weapons::table
271 | | .select(weapons::item_id)
272 | | .left_join(weapon_ends::table.on(weapons::primary_end.eq(weapon_ends::id).or(weapons::secondary_end.eq(weapon_ends::id.nullable()))))
273 | | .filter(f)
274 | | )))
| |__________^ the trait `diesel::AppearsOnTable<diesel::query_source::joins::Join<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, db::schema::items::table, diesel::query_source::joins::Inner>>` is not implemented for `dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>`
|
= note: required because of the requirements on the impl of `diesel::AppearsOnTable<diesel::query_source::joins::Join<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, db::schema::items::table, diesel::query_source::joins::Inner>>` for `std::boxed::Box<dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>>`
= note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause<diesel::query_source::joins::Join<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, db::schema::items::table, diesel::query_source::joins::Inner>>` for `diesel::query_builder::where_clause::WhereClause<std::boxed::Box<dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>>>`
= note: required because of the requirements on the impl of `diesel::expression::subselect::ValidSubselect<db::schema::items::table>` for `diesel::query_builder::SelectStatement<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, diesel::query_builder::select_clause::SelectClause<db::schema::weapons::columns::item_id>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<std::boxed::Box<dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>>>>`
= note: required because of the requirements on the impl of `diesel::AppearsOnTable<db::schema::items::table>` for `diesel::expression::subselect::Subselect<diesel::query_builder::SelectStatement<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, diesel::query_builder::select_clause::SelectClause<db::schema::weapons::columns::item_id>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<std::boxed::Box<dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>>>>, diesel::sql_types::Array<diesel::sql_types::Integer>>`
= note: required because of the requirements on the impl of `diesel::AppearsOnTable<db::schema::items::table>` for `diesel::pg::expression::array_comparison::Any<diesel::expression::subselect::Subselect<diesel::query_builder::SelectStatement<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, diesel::query_builder::select_clause::SelectClause<db::schema::weapons::columns::item_id>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<std::boxed::Box<dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>>>>, diesel::sql_types::Array<diesel::sql_types::Integer>>>`
= note: required because of the requirements on the impl of `diesel::AppearsOnTable<db::schema::items::table>` for `diesel::expression::operators::Eq<db::schema::items::columns::id, diesel::pg::expression::array_comparison::Any<diesel::expression::subselect::Subselect<diesel::query_builder::SelectStatement<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, diesel::query_builder::select_clause::SelectClause<db::schema::weapons::columns::item_id>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<std::boxed::Box<dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>>>>, diesel::sql_types::Array<diesel::sql_types::Integer>>>>`
= note: required because of the requirements on the impl of `diesel::query_dsl::filter_dsl::FilterDsl<diesel::expression::operators::Eq<db::schema::items::columns::id, diesel::pg::expression::array_comparison::Any<diesel::expression::subselect::Subselect<diesel::query_builder::SelectStatement<diesel::query_source::joins::JoinOn<diesel::query_source::joins::Join<db::schema::weapons::table, db::schema::weapon_ends::table, diesel::query_source::joins::LeftOuter>, diesel::expression::grouped::Grouped<diesel::expression::operators::Or<diesel::expression::operators::Eq<db::schema::weapons::columns::primary_end, db::schema::weapon_ends::columns::id>, diesel::expression::operators::Eq<db::schema::weapons::columns::secondary_end, diesel::expression::nullable::Nullable<db::schema::weapon_ends::columns::id>>>>>, diesel::query_builder::select_clause::SelectClause<db::schema::weapons::columns::item_id>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<std::boxed::Box<dyn diesel::BoxableExpression<db::schema::weapon_ends::table, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>>>>, diesel::sql_types::Array<diesel::sql_types::Integer>>>>>` for `diesel::query_builder::BoxedSelectStatement<'_, (diesel::sql_types::Text, diesel::sql_types::Float, diesel::sql_types::Float, db::sql_types::db_enum_impl_Slot::Item_slot, diesel::sql_types::Bool, db::sql_types::db_enum_impl_Source::Item_source), db::schema::items::table, diesel::pg::Pg>`
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0277`.
error: could not compile `SorteKanin`.
To learn more, run the command again with --verbose.
So, what I read from this is that I can’t use my WeaponEndsFilter type (3 code block up) as a filter here because I am joining on weapon_ends, but not filtering on it directly. So what type should I construct that would work? Is there some simpler way to achieve what I want to do?
I can’t for the life of me figure out how I am supposed to denote the type of WeaponEndsFilter so that this works. The documentation seems very minimal when it comes to the boxable types.
Any help hugely appreciated.