0

How can a generic boxed filter be applied to a queryset that contains a table and every other queryset where the table is present?

For the following toy example, is there a way to make get_filter return something that may be applied to any queryset that has a Foo table present

use diesel::*;
use diesel::expression::*;
use std::boxed::Box;

type DB = diesel::pg::Pg;

diesel::table! {
    Foo (id) {
        id -> Int4,
    }
}

diesel::table! {
    Bar (id) {
        id -> Int4,
        foo_id -> Int4,
    }
}

diesel::allow_tables_to_appear_in_same_query!(
    Foo,
    Bar,
);

pub fn get_queryset<'a>() -> Foo::BoxedQuery<'a, DB> {
    Foo::table.into_boxed()
}

use diesel::helper_types::*;
type QuerySetJoinType<'a> = IntoBoxed<
    'a,
    Filter<                                     
        InnerJoinOn<Foo::table, Bar::table, Eq<Bar::foo_id, Foo::id>>,
        Eq<Bar::id, diesel::expression::SqlLiteral<diesel::sql_types::Integer>>
    >,
    DB
>;

pub fn get_queryset_with_join<'a>() -> QuerySetJoinType<'a> {
    Foo::table
        .inner_join(Bar::table.on(Bar::foo_id.eq(Foo::id)))
        .filter(Bar::id.eq(42))
        .into_boxed()
}

type FilterReturnType = Box<dyn BoxableExpression<Foo::table, DB, SqlType = diesel::sql_types::Bool>>;

pub fn get_filter<'a>() -> FilterReturnType {
    let mut result : FilterReturnType = Box::new(Foo::id.eq(Foo::id));
    result = Box::new(result.and(Foo::id.gt(42)));
    result
}


fn main() {
    get_queryset().filter(get_filter());
    
    get_queryset_with_join().filter(get_filter());
}

This complains

   |
58 |     get_queryset_with_join().filter(get_filter());
   |                              ^^^^^^ the trait `AppearsOnTable<JoinOn<query_source::joins::Join<Foo::table, Bar::table, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<Bar::columns::foo_id, Foo::columns::id>>>>` is not implemented for `dyn diesel::BoxableExpression<Foo::table, Pg, SqlType = Bool>`

I think I could maybe get there by parameterizing the query set like:

pub fn get_filter<'a, QS>() -> Box<dyn BoxableExpression<QS, DB, SqlType = diesel::sql_types::Bool>> {
    let mut result : Box<dyn BoxableExpression<QS, DB, SqlType = diesel::sql_types::Bool>> 
        = Box::new(Foo::id.eq(Foo::id));
    result = Box::new(result.and(Foo::id.gt(42)));
    result
}

But I'm stuck at which trait bounds need to be set on QS?

Any ideas how to get either path to work?

This question is similar to this other question, except I actually want the types for the functions, and we're now on version 2 of diesel

Ross Rogers
  • 23,523
  • 27
  • 108
  • 164

1 Answers1

1

@weiznich provided guidance on gitter.im/diesel-rs/diesel to make the query function generic as:

pub fn get_filter<'a, QS>() -> Box<dyn BoxableExpression<QS, DB, SqlType = diesel::sql_types::Bool>>
where
    QS: 'static,
    dsl::Eq<Foo::id, Foo::id>: BoxableExpression<QS, DB>,
    dsl::Gt<Foo::id, i32>: BoxableExpression<QS, DB>,
    dsl::AssumeNotNull<dsl::Eq<Foo::label, String>>: BoxableExpression<QS, DB>,
{
    let mut result : Box<dyn BoxableExpression<QS, DB, SqlType = diesel::sql_types::Bool>>
        = Box::new(Foo::id.eq(Foo::id));
    result = Box::new(result.and(Foo::id.gt(42)));
    let s = "asdf".to_string();
    result = Box::new(result.and(
        Foo::label.eq(s).assume_not_null()));
    result
}
Ross Rogers
  • 23,523
  • 27
  • 108
  • 164
  • Bread crumbs for others. If compile is failing on something like `dsl::Gt: BoxableExpression,` and the field you're filtering against is `left_join`'d, you may need to add `helper_types::AssumeNotNull` like `dsl::Gt, i32>: BoxableExpression,` and `Foo_::id.assume_not_null()` – Ross Rogers Feb 07 '23 at 15:12