【问题标题】:Conditionally map a nullable field to None value in Slick有条件地将一个可为空的字段映射到 Slick 中的 None 值
【发布时间】:2015-01-24 04:01:55
【问题描述】:

在 Slick 2.1 中,我需要执行一个 query/map 操作,如果它包含某个非空值,我将一个可空字段转换为无。不确定它是否重要,但在我的情况下,有问题的列类型是映射列类型。这是一个代码 sn-p 试图说明我正在尝试做的事情。它不会编译,因为编译器不喜欢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 ? is not a member of 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
    }
  }

}

【问题讨论】:

    标签: scala group-by option slick


    【解决方案1】:

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

    鉴于您在更新中拥有的示例数据,并假设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"
    

    【讨论】:

    • 这基本上是正确的(您混淆了 then/else 子句,但这是一个小细节)。我知道我错过了一些简单的东西,男孩很简单。总而言之,在“Then”子句中,编译器不喜欢我们将“value”(或“value.?”)与“None”混合,因此关键的见解是混合“record.value”(已经具有“无”的 Column[Option[Int]])。谢谢!
    【解决方案2】:

    您需要将 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])
    

    【讨论】:

    • 我尝试了这两种方法,但它们无法编译。我得到“价值?不是 MyEnum 的成员”。我尝试通过使用 Option[Int] 而不是 Option[MyEnum] 进一步简化示例,但我得到了相同的编译错误(“值?不是 Int 的成员”)。我已经导入了 driver.simple._,所以不确定还需要什么来获取“.?”工作。
    • 对...你还需要告诉 Slick 如何映射 MyEnum。见slick.typesafe.com/doc/2.1.0/…请随时关注我
    • 是的,我已经定义了一个映射列类型,如我最初的帖子中所述。另外,你看到我最近的更新了吗?我无法得到“.?”甚至可以使用简单的 Column[Int]。我一定错过了一些非常简单的东西。
    • 错误消息会很有帮助。尝试向上转换为正确的选项类型,这很可能是问题
    • 关于我提供的更新中显示的代码,我在 IDE 中收到的编译器错误消息是“值?不是 Int 的成员”。抱歉,我不确定我应该向上转换什么。想评论更新中的代码吗?
    猜你喜欢
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 2020-04-27
    相关资源
    最近更新 更多