Слайк слева/справа/внешний соединяется с опцией

В примерах Slick есть несколько примеров объединения, когда один из результирующих столбцов может быть нулем, так как это может иметь место при выполнении левого, правого или внешнего объединений. Например:

val explicitLeftOuterJoin = for {
  (c, s) <- Coffees leftJoin Suppliers on (_.supID === _.id)
} yield (c.name, s.name.?)

Но что, если я хочу вернуть весь отображаемый объект? Я имею в виду:

val explicitLeftOuterJoin = for {
  (c, s) <- Coffees leftJoin Suppliers on (_.supID === _.id)
} yield (c, s.?)

Это не работает, поскольку он жалуется на "не смог найти неявное значение для параметра доказательства типа scala.slick.lifted.TypeMapper [Suppliers]". В основном я бы хотел, чтобы он вернул список кортежей (Coffee, Option [Supplier])

Почему это не работает и что за это исправление? Тем более, что это прекрасно работает:

val q = for {
  c <- Coffees
  s <- Suppliers
} yield (c, s)

(Я знаю, что внутреннее соединение)

Ответы

Ответ 1

ОБНОВЛЕНИЕ:. Это будет разрешено и просто будет работать в Slick 3.0 на конец 2014 года, больше нет необходимости в следующем обходном пути

Это ограничение Slick в данный момент. Вы должны позвонить.? по каждому столбцу отдельно. Тем не менее, вы можете поместить функцию под названием ? в класс таблицы, которая делает это в центральном месте и тем самым получает.? на полных строках. Этот примерный примерный код содержит общее решение, включающее некоторый сгенерированный код. У нас также есть PR, который добавляет автогенерацию? метод.

В конечном итоге мы поддержим вариант внешних соединений в Slick, где Slick полностью осведомлен о задействованных типах, и вам не нужно указывать.? в любом месте. На данный момент нам приходится сталкиваться с обходными решениями, связанными с генерированием кода.

Ответ 2

Не самое чистое решение (использует scalaz 7.0.6 и бесформенный 2.0.1), но теперь это работает (Slick 2.0.1):

Используя вышеприведенный проект ?, можно создать проекцию Slick, которая преобразует кортеж Option values ​​= > Option[TupleN] = > Option[YourClass].

Добавить Option Проекция

Примечание. sequence используется для преобразования кортежа значений Option в Option[TupleN]. Код для sequence определяется в нижней части этого ответа.

Добавить в Suppliers. Предполагается, что Supplier - это класс case.

  import scalaz._, Scalaz._
  import SequenceTupleOption._

  def option = (id.?, name.?, street.?) <> (optionApply, optionUnapply)
  def optionApply(t: (Option[Int], Option[String], Option[String])): Option[Comment] = {
    sequence(t).map(Supplier.tupled)
  }

  def optionUnapply(oc: Option[Supplier]): Option[(Option[Int], Option[String], Option[String])] = None

Использование Option Проекция

val explicitLeftOuterJoin = for {
  (c, s) <- Coffees leftJoin Suppliers on (_.supID === _.id)
} yield (c, s.option)

Дополнительно: sequence, конвертирует кортеж Option значения в Option[TupleN]

Это трудная часть, которую написал Травис Браун. sequence преобразует из кортежа значений Option в Option[TupleN] (используя scalaz и бесформенный).

import scalaz._, Scalaz._
import shapeless._, ops.hlist.{ RightFolder, Tupler }

object SequenceTupleOption {

  object applicativeFolder extends Poly2 {
    implicit def caseApplicative[A, B <: HList, F[_]](implicit
      app: Applicative[F]
    ) = at[F[A], F[B]] {
      (a, b) => app.ap(a)(app.map(b)(bb => (_: A) :: bb))
    }
  }

  def sequence[T, EL <: HList, L <: HList, OL <: HList, OT](t: T)(implicit
    gen: Generic.Aux[T, EL],
    eq: EL =:= L,
    folder: RightFolder.Aux[L, Option[HNil], applicativeFolder.type, Option[OL]],
    tupler: Tupler.Aux[OL, OT]
  ): Option[OT] =
    eq(gen.to(t)).foldRight(some(HNil: HNil))(applicativeFolder).map(tupler(_))

}

Использование sequence:

import scalaz._, Scalaz._
import SequenceTupleOption._

case class Person(id: Int, name: String, age: Int)

val t = (Option(1), Option("Bob"), Option(40))

val person: Option[Person] = sequence(t).map(Person.tupled) // Some(Person(1,Bob,40))

Высокоуровневый обзор того, что делает sequence (не соответствующие типы):

  • Преобразует кортеж Option в бесформенный HList[Option[_]].
  • sequence над HList[Option[_]] до Option[HList[_]]
  • Преобразуйте HList обратно в кортеж.

Ответ 3

В дополнение к ответу выше: Если у вас есть класс, который расширяет таблицу, а ваша проекция выглядит примерно так:

def * = (col1, col2, col3)

чем ваш? функция будет выглядеть так:

def ? = (col1.?, col2.?, col3.?)

Если вы определили такую ​​функцию, вы можете написать:

for {
    (x,y) <- x leftJoin y on (...)
} yield (x, y.?)

Ответ 4

В Slick 3.1.1 правильный ответ будет просто (как упоминалось в некоторых комментариях):

for {
  (c, s) <- coffees joinLeft suppliers on (_.supID === _.id)
} yield (c, s)