如何在Slick中将`ConstColumn`用于`None`值

时间:2019-04-24 16:58:03

标签: scala slick

有一些桌子:

case class Thing(name: String, color: Option[String], height: Option[String])

class ThingSchema(t: Tag) extends Table[Thing](t, "things") {
  def name = column[String]("name")
  def color = column[Option[String]]("color")
  def height = column[Option[String]]("height")
  def * = (name, color, height) <> (Thing.tupled, Thing.unapply)
}
val things = TableQuery[ThingSchema]

例如,things表中包含以下数据:

|  name   |   color   | height |
+---------+-----------+--------+
|   n1    |  green    | <null> |
|   n1    |  green    | <null> |
|   n1    |  <null>   | normal |
|   n1    |  <null>   | normal |
|   n1    |  red      | <null> |
|   n2    |  red      | <null> |

我需要从以上数据中获得以下结果:

|  name   |   color   | height | size |
+---------+-----------+--------+------+
|   n1    |  green    | <null> |  2   |
|   n1    |  <null>   | normal |  2   |
|   n1    |  red      | <null> |  1   |
|   n2    |  red      | <null> |  1   |

要解决此任务,我使用以下分组查询:

SELECT name, color, null, count(*) AS size
FROM things
GROUP BY name, color

UNION ALL

SELECT name, null, height, count(*) AS size
FROM things
GROUP BY name, height

我尝试使用Slick创建此查询:

val query1 = 
      things.groupBy(t => (t.name, t.color))
            .map { case ((name, color), g) => (name,color,None, g.size)} //Error#1

val query2 = 
      things.groupBy(t => (t.name, t.height)) 
            .map { case ((name, height), g) => (name,None,height,g.size)} //Error#1

val query = query1 ++ query2

但是上面的代码没有被编译,因为Slick无法为ConstColumn的值定义None的类型(请参见上面的代码中的//Error#1注释)。

这将适用于非空值(例如numbersstrings),但不适用于表示为Option[String]=None的可空值。

在这种情况下如何为ConstColumn的值使用None

Here is the link to the same question

2 个答案:

答案 0 :(得分:1)

The error I'd expect in this situation is a type-mismatch of some kind between Option[String] and None.type at the two //Error points in your code.

What you can do is give a type annotation on the None:

val query1 = 
      things.groupBy(t => (t.name, t.color))
            .map { case ((name, color), g) => (name,color, None: Option[String], g.size)}

That will be compiled into the SELECT name, color, null, count pattern you're using.

答案 1 :(得分:0)

我为该任务找到了另一种解决方案。也许对某人有用。

我们可以使用Rep.None[T]Rep.Some[T]为可选类型生成ConstColumn值。

此示例也适用:

val query1 = 
     things.groupBy(t => (t.name, t.color))
           .map { case ((name, color), g) => 
                        (name,color, Rep.None[String], g.size)
                }

此方法有两个优点:

1)我们可以分配给更通用的类型。例如:

val field: Rep[String] = ...
val x: (Rep[String], Rep[Option[String]]) = (field, Rep.None[String])

// it is not compiled because a tuple has type (Rep[String], Option[String])
val y: (Rep[String], Rep[Option[String]]) = (field, None: Option[String])

2)这种方法要短一些