How to filter on a Option[Boolean] column in slick

时间:2017-10-12 09:44:36

标签: scala slick slick-3.0

I have the following column in my db, thats a Boolean, but also accepts NULL, so true, false, and NULL are all valid:

def rtb = column[Option[Boolean]]("rtb")

and have the following optional input from the client that I'd like to filter on:

rtbFromClient: Option[Boolean] = ... 

I have the following (based on this answer on how to do queries in slick: https://stackoverflow.com/a/40888918/5300930):

val query = userTable.
      filter(row => 
          if (rtbFromClient.isDefined) 
              row.rtb.get === rtbFromClient.get 
          else 
              LiteralColumn(true)
      )

but am getting this error when the code runs:

Caught exception while computing default value for Rep[Option[_]].getOrElse -- This cannot be done lazily when the value is needed on the database side

I thought it may be because row.rtb.get was throwing exception on call to get because the value in the db was null, so tried changing it to row.rtb.getOrElse(null) and row.rtb.getOrElse(None) but neither of these worked either)

Also tried the following:

if (rtbFromClient.isDefined) {
    val query = query.filter(_.rtb.isDefined).filter(_.rtb.get === rtbFromClient.get)
}

But this also throws the same error at runtime:

Caught exception while computing default value for Rep[Option[_]].getOrElse -- This cannot be done lazily when the value is needed on the database side

To summarise:

  • I have an Option[Boolean] column in my db that can contain true, false or NULL (the actual mysql type is a tinyint(1), which is mapped to a slick Option[Boolean])
  • I have an optional filter provided by the user, rtbFromClient that I'd like to filter on if present. If present, this will be either true or false
  • I have other optional filters (not shown here) that have similar behavior so I'd like to be able to combine them easily

5 个答案:

答案 0 :(得分:2)

Fist check if the column is null and then go ahead with another comparison. Ensure that rtbFromClient is not an option.

val query = userTable.filter(row => !row.rtb.isEmpty &&
                                    row.rtb === rtbFromClient)

The first condition ensures that nulls are filtered and the second condition checks the value in case the column value is not null.

In case you have optional value, then below code helps.

def query(value: Option[Boolean]) = value match {
   case Some(userGivenRtbFromClient) =>
    userTable.filter(row => !row.rtbFromClient.isNull &&
         row.rtbFromClient === userGivenRtbFromClient)
   case None => userTable.filter(row => row.rtbFromClient.isNull)
}

The Much cleaner version is here.

rtbFromClient: Option[Boolean] 

rtbFromClient User given optional value to compare with slick column.

userTable.filter(row => rtbFromClient.map(value => row.rtb === Some(value): Option[Boolean]).getOrElse(row.rtb.isEmpty))

答案 1 :(得分:2)

我有同样的问题。我的解决方案是(使用Slick 3.3.x测试):

val query = usersTable.filterOpt(rtbFromClient)(_.rtb === _)

情况1(当rtbFromClient为空时)对应以下SQL:

select * from users;

情况2(已定义rtbFromClient):

select * from users where rtb = ?;

答案 2 :(得分:0)

var query = userTable.drop(page.skip).take(page.top)

if (rtbFromClient.isDefined) {
    query = query.filter(row => row.rtb.isDefined && row.rtb === rtbFromClient)
}

// More if blocks with optional filters here
...

dbProvider.db.run(query.result)

答案 3 :(得分:0)

尝试

val query = userTable.filter(row => rtbFromClient.map(x => Option(x) === row.rtb).getOrElse(row.rtb.isEmpty.?))

答案 4 :(得分:0)

我面临着同样的问题。玩了之后,我意识到可以直接通过该选项进行过滤,所以如果我们有专栏

def rtb = column[Option[Boolean]]("rtb")

我们正在尝试过滤

rtbFromClient: Option[Boolean] = ... 

然后我们可以做:

val query: DBIO[Seq[TableElementType]] = 
  if (rtbFromClient.isDefined)
    userTable.filter(_.rtb === rtbFromClient).result
  else
    userTable.result