有条件地在Slick中将可空字段映射到None值

时间:2015-01-24 04:01:56

标签: scala group-by option slick

在Slick 2.1中,我需要执行查询/ map操作,如果它包含某个非空值,我将可空字段转换为None。不确定它是否重要,但在我的情况下,所讨论的列类型是映射列类型。这是一个代码片段,试图说明我正在尝试做什么。它不会编译,因为编译器不喜欢None

case class Record(field1: Int, field2: Int, field3: MyEnum)

sealed trait MyEnum
val MyValue: MyEnum = new MyEnum { }

// table is a TableQuery[Record]
table.map { r => (
  r.field1,
  r.field2,
  Case If (r.field3 === MyValue) Then MyValue Else None // compile error on 'None'
  )
}

错误是这样的:

type mismatch; found : None.type required: scala.slick.lifted.Column[MyEnum]

实际上,我想这样做的原因是我想要执行groupBy,其中我计算field3包含给定值的记录数。我无法使更复杂的groupBy表达式工作,所以我退回到这个更简单的例子,我仍然无法工作。如果有更直接的方式向我展示groupBy表达式,那也没关系。谢谢!

更新

我尝试了@cvogt建议的代码,但这会产生编译错误。这是一个SSCCE,万一有人能在这里发现我做错了什么。编译失败,“value?不是Int的成员”:

import scala.slick.jdbc.JdbcBackend.Database
import scala.slick.driver.H2Driver

object ExpMain extends App {

  val dbName = "mydb"
  val db = Database.forURL(s"jdbc:h2:mem:${dbName};DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")
  val driver = H2Driver

  import driver.simple._

  class Exp(tag: Tag) extends Table[(Int, Option[Int])](tag, "EXP") {
    def id = column[Int]("ID", O.PrimaryKey)
    def value = column[Option[Int]]("VALUE")
    def * = (id, value)
  }
  val exp = TableQuery[Exp]

  db withSession { implicit session =>
    exp.ddl.create

    exp += (1, (Some(1)))
    exp += (2, None)
    exp += (3, (Some(4)))

    exp.map { record =>
      Case If (record.value === 1) Then 1.? Else None  // this will NOT compile
      //Case If (record.value === 1) Then Some(1) Else None  // this will NOT compile
      //Case If (record.value === 1) Then 1 Else 0  // this will compile
    }.foreach {
      println
    }
  }

}

2 个答案:

答案 0 :(得分:2)

  

我需要执行查询/映射操作,如果它包含某个非空值,我将可空字段转换为None

鉴于您在更新中拥有的示例数据,并假装1是您关注的“特定”值,我相信这是您期望的输出:

None, None, Some(4)

(对于ID为1,2和3的行)

如果我正确理解了问题,这就是你需要的......?

val q: Query[Column[Option[Int]], Option[Int], Seq] = exp.map { record => 
  Case If (record.value === 1) Then (None: Option[Int]) Else (record.value)
}

......相当于:

select (case when ("VALUE" = 1) then null else "VALUE" end) from "EXP"

答案 1 :(得分:1)

您需要将MyValue包装在一个Option中,以便条件的两个结果都是选项。在Slick 2.1中你使用。?操作员。在Slick 3.0中,它可能是Rep.Some(...)。

尝试

Case If (r.field3 === MyValue) Then MyValue.? Else None

Case If (r.field3 === MyValue) Then MyValue.? Else (None: Option[MyEnum])