使用Slick 3的可选where子句的动态查询

Ros*_*ony 6 scala playframework slick

我正在尝试基于一组可能设置或可能未设置的参数来实现返回过滤结果的方法.似乎没有条件地链接多个过滤器,即从一个过滤器开始......

val slickFlights = TableQuery[Flights]
val query = slickFlights.filter(_.departureLocation === params("departureLocation").toString)
Run Code Online (Sandbox Code Playgroud)

有条件地在查询中添加另一个过滤器(如果它存在于params的Map中)似乎不起作用......

if (params.contains("arrivalLocation")) {
      query.filter(_.arrivalLocation === params("arrivalLocation").toString)
}
Run Code Online (Sandbox Code Playgroud)

可以通过其他方式使用Slick完成这种条件过滤吗?

我遇到过MaybeFilter:https://gist.github.com/cvogt/9193220 ,这似乎是处理这个问题的一个不错的方法.但它似乎不适用于Slick 3.x.

根据Hüseyin的建议,我也尝试了以下方法:

def search(departureLocation: Option[String], arrivalLocation: Option[String]) = {
    val query = slickFlights.filter(flight =>
       departureLocation.map {
          param => param === flight.departureLocation
       })
Run Code Online (Sandbox Code Playgroud)

slickFlightsTableQuery对象在哪里val slickFlights = TableQuery[Flights].但是,这会产生以下编译错误:

value === is not a member of String
Run Code Online (Sandbox Code Playgroud)

Intellij还抱怨= =是一个未知的符号.不适用于==.

iol*_*leo 13

一种更简单的方法,无需理解:

import slick.lifted.LiteralColumn

val depLocOpt = Option[Long]
val slickFlights = TableQuery[Flights]
val query = slickFlights.filter { sf => 
  if (depLocOpt.isDefined) sf.departureLocation === depLocOpt.get
  else                     LiteralColumn(true)
}
Run Code Online (Sandbox Code Playgroud)

更新:您可以通过以下方式缩短它fold:

val depLocOpt = Option[Long]
val slickFlights = TableQuery[Flights]
val query = slickFlights.filter { sf => 
  depLocOpt.fold(true.bind)(sf.departureLocation === _)
}
Run Code Online (Sandbox Code Playgroud)


Ros*_*ony 7

为了让其他任何人试图让Slick中的可选过滤器正常工作,请在此处查看答案:正确使用光滑过滤器.我终于设法让它使用以下内容:

def search(departureLocation: Option[String], arrivalLocation: Option[String]) = {
  val query = for {
    flight <- slickFlights.filter(f =>
       departureLocation.map(d => 
         f.departureLocation === d).getOrElse(slick.lifted.LiteralColumn(true)) && 
       arrivalLocation.map(a => 
         f.arrivalLocation === a).getOrElse(slick.lifted.LiteralColumn(true))
    )
  } yield flight
Run Code Online (Sandbox Code Playgroud)

关键位是.getOrElse(slick.lifted.LiteralColumn(true))映射的末尾,如果仅设置了departureLocation,则导致Slick如下呈现SQL ...

select * from `flight` 
where (`departureLocation` = 'JFK') and true
Run Code Online (Sandbox Code Playgroud)

没有它,SQL看起来像......

select * from `flight` 
where (`departureLocation` = 'JFK') and (`arrivalLocation` = '')
Run Code Online (Sandbox Code Playgroud)

这显然意味着它没有任何行回来.


And*_*mov 5

2019年一月

不再需要发明自己的轮子!

最后,Slick 3.3.0包含以下帮助程序:

因此,例如:

case class User(id: Long, name: String, age: Int)
case class UserFilter(name: Option[String], age: Option[Int])

val users = TableQuery[UsersTable]

def findUsers(filter: UserFilter): Future[Seq[User]] = db run {
  users
    .filterOpt(filter.name){ case (table, name) =>
      table.name === name
    }
    .filterOpt(filter.age){ case (table, age) =>
      table.age === age
    }
    .result
}
Run Code Online (Sandbox Code Playgroud)