为什么它不起作用
正如 cvot 所指出的 in his comment,这不起作用的原因是:
Slick 将 None 翻译为 SQL NULL,包括 SQL 的 3 值逻辑 NULL 传播,因此 (None === a) 为 None 无论 a 的值如何...基本上如果表达式中的任何内容为 None,则整个表达式将为None,因此过滤器表达式将被视为false,查询结果将为空。
也就是说,有一种方法可以获得您想要的相同行为(仅在提供可选值时过滤)。
达到预期行为的方法
需要注意的关键是,Scala 将 for comprehension 编译为 map / flatMap / withFilter / filter 调用的组合。如果我理解正确的话,Slick 在将 Scala 推导式编译成 SQL 查询时可以使用结果结构。
这让我们可以分部分构建查询:
val baseQuery = for {
u <- users if u.companyId === companyId
} yield u
val possiblyFilteredByLocation = if (locationId.isDefined) {
baseQuery.withFilter(u => u.locationId === locationId.get
} else baseQuery
val possiblyFilteredBySalaryAndOrLocation = if (salary.isDefined) {
possiblyFilteredByLocation.withFilter(u => u.salary >= salary.get)
} else possiblyFilteredByLocation
possiblyFilteredBySalaryAndOrLocation.list()
我们可以通过使用var 和fold 来简化这一过程:
var query = for {
u <- users if u.companyId === companyId
} yield u
query = locationId.fold(query)(id => query.withFilter(u => u.locationId === id))
query = salary.fold(query)(salary => query.withFilter(u => u.salary >= salary))
query.list()
如果我们经常这样做,我们可以将Option 上的这种过滤模式概括为如下所示:
// Untested, probably does not compile
implicit class ConditionalFilter(query: Query) {
def ifPresent[T](value: Option[T], predicate: (Query, T) => Query) = {
value.fold(query)(predicate(query, _))
}
}
然后我们可以将整个过滤器链简化为:
query
.ifPresent[Int](locationId, (q, id) => q.withFilter(u => u.locationId === id))
.ifPresent[Int](salary, (q, s) => q.withFilter(u => u.salary >= s))
.list()