[Diesel] Factorize query builder with inner_join and into_boxed?

My schemas:

diesel::table! {
    farm_room (id) {
        id -> Int8,
        added_at -> Nullable<Timestamptz>,
        modified_at -> Nullable<Timestamptz>,
        #[max_length = 200]
        name -> Varchar,
        control_mode -> Int2,
        is_control_tolerance_enabled -> Bool,
        added_by_id -> Nullable<Uuid>,
        farm_id -> Nullable<Int8>,
        modified_by_id -> Nullable<Uuid>,
        tight_control -> Bool,
        last_control -> Nullable<Timestamptz>,
    }
}

diesel::table! {
    farm_device (id) {
        id -> Int8,
        added_at -> Nullable<Timestamptz>,
        modified_at -> Nullable<Timestamptz>,
        #[max_length = 20]
        name -> Nullable<Varchar>,
        #[max_length = 10]
        serial_number -> Nullable<Varchar>,
        #[max_length = 8]
        device_type -> Varchar,
        mac -> Nullable<Int8>,
        is_active -> Bool,
        added_by_id -> Nullable<Uuid>,
        farm_id -> Nullable<Int8>,
        modified_by_id -> Nullable<Uuid>,
    }
}
diesel::table! {
    farm_node (id) {
        id -> Int8,
        added_at -> Nullable<Timestamptz>,
        modified_at -> Nullable<Timestamptz>,
        #[max_length = 20]
        name -> Varchar,
        is_primary -> Bool,
        sampling_period -> Nullable<Interval>,
        added_by_id -> Nullable<Uuid>,
        modified_by_id -> Nullable<Uuid>,
        room_id -> Nullable<Int8>,
        device_id -> Nullable<Int8>,
    }
}

I want to define a function to build query to retrieve Nodes which belong to a Room, but its associated device must be active, so I have:

impl Room {
    pub fn active_nodes_query(&self) -> farm_node::BoxedQuery<'static, diesel::pg::Pg> {
        use crate::schema::farm_node::dsl::*;
        let x = farm_node
            .select(Node::as_select())
            .inner_join(farm_device::table)
            .filter(device_id.is_not_null())
            .filter(farm_device::is_active.eq(true))
            .filter(room_id.eq(self.id))
            .into_boxed();
        x
    }
}

But I don't know what to declare as return type for active_nodes_query. farm_node::BoxedQuery is not correct.

Diesel is apparently not in the Playground, and your code is missing the schema modules too, so I couldn't test it. However, you can trick the compiler into outputting the required type by inducing an intentional type mismatch in the signature or the body, e.g.:

pub fn active_nodes_query(&self) -> () {
    //                              ^^^ here
    ...
}

or

pub fn active_nodes_query(&self) -> farm_node::BoxedQuery<'static, diesel::pg::Pg> {
        use crate::schema::farm_node::dsl::*;
        let x: () = farm_node
        //  ^^^ or here
            .select(Node::as_select())
            .inner_join(farm_device::table)
            .filter(device_id.is_not_null())
            .filter(farm_device::is_active.eq(true))
            .filter(room_id.eq(self.id))
            .into_boxed();
        x
    }

This is usually a good advice, but in this case it's not helpful as the compiler is buggy there and suggests types that cannot be specified by diesel users. The types suggested by the compiler are private because they are considered to be a implementation details. Generally speaking: You can always get the correct return type for a diesel function from the API docs.

So in this case you want to have the type of x, which is means you need to go through a chain of method calls and nest the output type correctly:

  1. QueryDsl::select
  2. QueryDsl::inner_join
  3. QueryDsl::filter
  4. QueryDsl::filter
  5. QueryDsl::filter
  6. QueryDsl::into_boxed

Note that the API documentation lists a type def from diesel::dsl as return type for all of these functions. You need to nest them in the same order as you call your function. This results in the following return type:

dsl::IntoBoxed<'static, dsl::Filter<dsl::Filter<dsl::Filter<dsl::InnerJoin<dsl::Select<farm_node::table, dsl::AsSelect<Node>>, farm_devices::table>, dsl::IsNotNull<farm_node::device_id>>, dsl::Eq<farm_devices::is_active, bool>>, dsl:::Eq<farm_node::room_id, WhateverIsTheTypeOfSelfId>>, diesel::pg::Pg>
1 Like

Obviously. But people often complain because they don't know how to to do that in practice.

Thank you.

I recognize your name. You seems to be Diesel's author.