Как использовать `ConstColumn` для необязательных значений в Slick - PullRequest
0 голосов
/ 24 апреля 2019

Существует некоторая таблица:

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 в приведенном выше коде).

Это будет работать для значений NOT-NULL (например, numbers, strings), но не работает для значений Nullable, которые представлены, например, как Option[String]=None.

Как использовать ConstColumn для None значений для этого случая?

Вот ссылка на тот же вопрос

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Ошибка, которую я ожидаю в этой ситуации, заключается в некотором несоответствии типов между Option[String] и None.type в двух //Error точках в вашем коде.

Что вы можете сделать, это дать аннотацию типа для None:

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

, которая будет скомпилирована в используемый вами шаблон SELECT name, color, null, count.

0 голосов
/ 03 июля 2019

Я нашел другое решение для этой задачи. Возможно, это будет кому-то полезно.

Мы можем использовать 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) Этот подход немного короче

...