Slickにおける動的where句の作り方
現在最新は3系ですが環境としては、Slick2.1での書き方での紹介です。
Documentation | Slick
Slickにおいて動的にwhere句を作るのは、案外Scalaを始めたばかりだとうまくいきません。
以下の様なクラスに対して複数の検索をかけるとき、
type Address = (Int,String,String) class Addresses(tag: Tag) extends Table[Address](tag, "ADDRESS") { def id = column[Int]("ID", O.PrimaryKey, O.AutoInc) def street = column[String]("STREET") def city = column[String]("CITY") def * = (id,street,city) } lazy val addresses = TableQuery[Addresses]
多分一番最初に思い浮かぶ方法は以下だと思います。
def find(streetOp: Option[String], cityOp: Option[String])(implicit session: Session) = { val q = streetOp.map(s => addresses.filter(_.street === s).getOrElse(addresses) val q1 = cityOp.map(c => q.filter(_.city === c).getOrElse(q) q1.list }
しかし、実はfilter()の中をColumn[Option[Boolean]]を返すことで条件として満たします。
なので、以下の様な書き方ができます。
def makeCondition(row: Addresses, streetOp: Option[String], cityOp: Option[String]): Column[Option[Boolean]] = { val True: Column[Option[Boolean]] = Some(true) val a: Option[Boolean] = (if (streetOp.nonEmpty) row.street === streetOp.get else True) && (if (citiOp.isDefined) row.city === cityOp.get else True) a } addresses.filter(q => makeCondition(q, Some("東海道"), None)).list
この書き方の場合、検索条件を関数化できるので、複数の部品による組み合わせができます。
例えば、
addresses.filter(q => makeCondition(q, Some("東海道"), None) || q.id === 1).list
みたいなことができます。
使う機会は多いのに、ドキュメントからではなかなかわからないのですが、ぜひ活用してみてください。