Dieselでfilter関数に渡す条件を関数として別出しする方法

タイトルだけではなんのこっちゃわかりませんが、以下のような当たり前のコードを考えます。

fn test() {
    schema::table_name::table
            .filter(schema::table_name::dsl::age.gt(19));
}

自明な処理ではありますが、「なぜidが指定された値より大きいものを抜きだすの?」という情報が上記のコードからは読み取れません。

コレがもし、

fn test() {
    schema::table_name::table
            .filter(is_adult());
}

であれば、一体何をしようとしているのかがよりわかりやすくなります。

is_adult関数は以下のように実装できます。

type I = schema::table_name::columns::age;
type B = diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>;

fn is_adult() -> diesel::expression::operators::Gt<I, B> {
    schema::table_name::dsl::age.gt(19)
}

かなり煩雑なコードになってしまったな、というのが第1印象です。 しかし、もしかしたらそのうち成人とされる年齢が法律によって変更されるかもしれません。 こうしておくことでこの「成人したユーザのみ抽出する」という条件を様々な箇所で再利用することが出来、上記の関数だけ修正すればOKになります。

当然それはあくまで一つの例に過ぎないので、関数に渡された条件によってSQLの検索条件が変わる、という際にも利用できます。

なお、ちゃんとuseを指定すれば当然コードはかなりスッキリします。

use schema::table_name;

use diesel::sql_types::Integer;
use diesel::expression::bound::Bound;
use diesel::expression::operators::{Eq, Gt};

type I = table_name::columns::age;
type B = Bound<Integer, i32>;
fn is_adult() -> Eq<I, B> {
    table_name::dsl::age.gt(19)
}

fn test() {
    table_name::table.filter(is_adult());
}

Boundは、RustとDB(Diesel)の間でどのような型の関連性なのかを示すものです。 上記のBound<Integer, i32>であれば、DB側のIntegerはRustではi32、ということになります。 (厳密にはDBの型というよりはDieselの型)

今回は例としてageというカラムを用意していますが、通常はDB側ではTimestamp型等で生年月日を保存しているはずです。 ということは、「任意のタイミングで成人しているかどうか」という検索条件になります。 こう考えると、やはり検索条件を別出しして置いたほうがコードがスッキリしそうですね。

公開日:2020/11/27

Diesel Rust

About me

ドイツの現地企業でWeb Developer/System Administratorとして働いているアラフォーおじさんです。

プログラミングとかコンピュータに関する事がメインですが、日常的なメモとか雑多なことも書きます。

Links :
目次