読者です 読者をやめる 読者になる 読者になる

quillでクエリを条件分岐してみる

Scala

先日少し試してみたquillですが、クエリを動的に変更したい場合がちょっと面倒です。マクロで処理されているためあまり自由度がなく、以下のようにfilterの条件を分岐させることはできるのですが、Slickのようにプログラムでクエリを自由に組み立てるということはできません。

val account: Option[Account] = db.run(
  quote { (mailAddress: String, includeRemoved: Boolean) =>
    query[Account].filter { t =>
      if(includeRemoved){
        t.mailAddress.toLowerCase == mailAddress.toLowerCase
      } else {
        t.mailAddress.toLowerCase == mailAddress.toLowerCase && t.removed == false
      }
    }
  }
)(mailAddress, includeRemoved).headOption

上記のクエリは以下のようなSQLを出力します。どうやらifCASEに置き換えられるようです。

SELECT ... 
FROM account t 
WHERE CASE WHEN ? THEN 
  LOWER (t.mail_address) = LOWER (?)
ELSE
  (LOWER (t.mail_address) = LOWER (?)) AND (t.removed = false)
END

なので一応こんなこともできます。

val account: Option[Account] = db.run(
  quote { (mailAddress: String, includeRemoved: Boolean) =>
    query[Account].filter { t =>
      t.mailAddress.toLowerCase == mailAddress.toLowerCase && (
        if(includeRemoved){
          1 == 1
        } else {
          t.removed == false
        }
      )
    }
  }
)(mailAddress, includeRemoved).headOption

出力されるSQLは以下のようになります。

SELECT ... 
FROM account t
WHERE 
  (LOWER (t.mail_address) = LOWER (?)) AND 
  CASE WHEN ? THEN true ELSE t.removed = false END

シンプルなケースであれば以下のようにクエリそのものをまるっと置き換えたほうが手っ取り早いかもしれません。

val accounts: List[Account] = db.run(
  if(includeRemoved){
    quote { query[Account].sortBy(_.userName) }
  } else {
    quote { query[Account].filter(_.removed == false).sortBy(_.userName) }
  }
)

ユーザの入力に応じて検索条件が変わるようなクエリの場合はちょっと面倒そうです。