Slick: Model Option [(col1, col2)], так что оба отсутствуют или оба определены - PullRequest
1 голос
/ 09 мая 2020

У меня есть таблица с несколькими Option[X] столбцами. Я хочу смоделировать тот факт, что для этих столбцов либо определены все , либо все отсутствуют. Как это сделать, если столбцы определены как отдельные Option[X]?

def startDate = column[Option[LocalDate]]("start_date", O.Default(None))

def endDate = column[Option[LocalDate]]("end_date", O.Default(None))

def dateRange: Rep[Option[(LocalDate,LocalDate)]] = ??? // How to make something like this?

1 Ответ

1 голос
/ 10 мая 2020

Вы можете вкладывать сопоставления при построении проекции с помощью Slick. То есть вы можете определить * для включения других вызовов mapTo (для класса case) или <> (для настраиваемого сопоставления).

Используя пример с Int вместо LocalDate, мы можем начать с класса case, с которым хотим работать:

case class Row(
  name  : String,
  range : Option[(Int, Int)],
  id    : Long = 0L
)

Обратите внимание, что поле range имеет одно значение (кортеж), но в базе данных мы можем представить это как два столбца называемые start и end:

class RowTable(tag: Tag) extends Table[Row](tag, "row") {
  def id    = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name  = column[String]("name")
  def start = column[Option[Int]]("start")
  def end   = column[Option[Int]]("end")

  def range = (start, end) <> (into_range, from_range)

  def * = (name, range, id).mapTo[Row]
}

Проекция * в Row построена из другого сопоставления, которое я назвал range.

Отображение range находится в двух столбцах start и end. Для <> мы предоставляем две функции: одна функция преобразует пару параметров в нужный нам тип (вариант кортежа); другая функция идет другим путем. Функции отражают стандартные библиотечные функции tupled и unappy (unapply - это экстрактор ).

Мы можем писать эти функции, как хотим, и я написал их вне с сопоставлением с образцом:

def into_range(pair: (Option[Int], Option[Int])): Option[(Int, Int)] =
  pair match {
    case (Some(x), Some(y)) => Some((x, y))
    case _                  => None
  }

def from_range(r: Option[(Int, Int)]): Option[(Option[Int], Option[Int])] =
  r match {
     case Some((x, y)) => Some((Some(x), Some(y)))
     case _            => Some((None, None))
  }

Таким образом, ваш dateRange эквивалентен сопоставлению range в этом примере, но вместо того, чтобы вызывать его напрямую, мы включаем его в нашу проекцию по умолчанию, * .

...