Исполняя право присоединиться к дизелю - PullRequest
4 голосов
/ 23 января 2020

У меня есть функция, которая принимает два необязательных аргумента фильтрации и фильтрует данные в таблице, если эти аргументы предоставлены. Для этого я создал коробочный запрос. Я хотел бы создать правильное соединение с этим коробочным запросом. В текущей документации Diesel не упоминается правильное соединение, но, похоже, он предпочитает метод belonging_to. Я сократил свой код до одного примера:

#[macro_use] extern crate diesel;

use diesel::prelude::*;

table! {
    groups (id) {
        id -> Int4,
        name -> Text,
    }
}

table! {
    user_groups (id) {
        id -> Int4,
        user_id -> Int4,
        group_id -> Int4,
    }
}

allow_tables_to_appear_in_same_query!(groups, user_groups);

#[derive(Debug, Queryable)]
struct Group {
    id: i32,
    name: String,
}

#[derive(Debug, Queryable, Associations)]
#[belongs_to(Group)]
struct UserGroup {
    id: i32,
    user_id: i32,
    group_id: i32,
}

fn filter(
    name: Option<&str>,
    user_id: Option<i32>,
    conn: &diesel::PgConnection,
) -> Result<Vec<(Group, Vec<UserGroup>)>, Box<dyn std::error::Error>> {
    let mut query = groups::table
        .right_join(user_groups::table.on(groups::id.eq(user_groups::group_id))) // this method does not exist
        .into_boxed();
    if let Some(name) = name {
        query = query.filter(groups::name.eq(name));
    }
    if let Some(user_id) = user_id {
        query = query.filter(user_groups::user_id.contains(user_id)); // so this is just a guess
    }
    Ok(query.get_results(conn)?)
}

fn main() {
    let url = "postgres://question_usr:question_pwd:localhost:5432/question_db";
    let conn = diesel::PgConnection::establish(url).unwrap();
    let _ = filter(Some("groupname"), Some(4), &conn).unwrap();
}

Намерение состоит в том, что если указан аргумент user_id, возвращаются только строки Groups, в которых есть хотя бы один UserGroup, такой, что user_group.user_id == user_id. Как мне выполнить этот запрос? Нужно ли как-то использовать функцию belonging_to

1 Ответ

6 голосов
/ 27 января 2020

Правое соединение в любом случае эквивалентно левому соединению с перевернутым порядком таблиц. (См. здесь , например). Поэтому Diesel предоставляет только функции для построения LEFT JOIN.

Ваш пример выглядит следующим образом:

fn filter(
    name: Option<&str>,
    user_id: Option<i32>,
    conn: &diesel::PgConnection,
) -> Result<Vec<(UserGroup, Option<Group>)>, Box<dyn std::error::Error>> {
    let mut query = user_groups::table
        .left_join(groups::table.on(groups::id.eq(user_groups::group_id))) /
        .into_boxed();
    if let Some(name) = name {
        query = query.filter(groups::name.eq(name));
    }
    if let Some(user_id) = user_id {
        query = query.filter(user_groups::user_id.eq(user_id)); 
    }
    Ok(query.get_results(conn)?)
}

Обратите внимание, что это возвращает Vec<(UserGroup, Option<Group>)> дефолт. Если вы хотите что-то другое, вам нужно написать специальное предложение select, определяющее требуемую форму результата.

...