Удобный подзапрос: для Ref s2 не найден тип символа s2.

У меня есть запрос в slick с подзапросом, который выглядит так:

(User join Address on (_.id === _.userId))
.map { case (user, address) =>
    (
        user.id,
        address.id,
        address.street,
        Detail.filter(_.userId === user.id).map(_.value).first
    )
}.list

что вызывает исключение:

[SlickException: No type for symbol s2 found for Ref s2]

В чем проблема? Чтобы было ясно, это упрощенная версия моего производственного кода для удобства, однако ошибка точно такая же.

Стек: Slick 2.1 / Scala 2.11.4 / Play Framework 2.3.6 / Java 8

Классы таблиц генерируются гладким генератором кода (я бы хотел, чтобы так и было):

package db
// AUTO-GENERATED Slick data model
/** Stand-alone Slick data model for immediate use */
object Tables extends {
  val profile = scala.slick.driver.PostgresDriver
} with Tables

/** Slick data model trait for extension, choice of backend or usage in the cake pattern. (Make sure to initialize this late.) */
trait Tables {
  val profile: scala.slick.driver.JdbcProfile
  import profile.simple._
  import scala.slick.model.ForeignKeyAction
  // NOTE: GetResult mappers for plain SQL are only generated for tables where Slick knows how to map the types of all columns.
  import scala.slick.jdbc.{GetResult => GR}

  /** DDL for all tables. Call .create to execute. */
  lazy val ddl = Address.ddl ++ Detail.ddl ++ User.ddl

  /** Entity class storing rows of table Address
   *  @param id Database column id DBType(serial), AutoInc, PrimaryKey
   *  @param userId Database column user_id DBType(int4)
   *  @param street Database column street DBType(varchar), Length(2147483647,true) */
  case class AddressRow(id: Int, userId: Int, street: String)
  /** GetResult implicit for fetching AddressRow objects using plain SQL queries */
  implicit def GetResultAddressRow(implicit e0: GR[Int], e1: GR[String]): GR[AddressRow] = GR{
    prs => import prs._
    AddressRow.tupled((<<[Int], <<[Int], <<[String]))
  }
  /** Table description of table address. Objects of this class serve as prototypes for rows in queries. */
  class Address(_tableTag: Tag) extends Table[AddressRow](_tableTag, "address") {
    def * = (id, userId, street) <> (AddressRow.tupled, AddressRow.unapply)
    /** Maps whole row to an option. Useful for outer joins. */
    def ? = (id.?, userId.?, street.?).shaped.<>({r=>import r._; _1.map(_=> AddressRow.tupled((_1.get, _2.get, _3.get)))}, (_:Any) =>  throw new Exception("Inserting into ? projection not supported."))

    /** Database column id DBType(serial), AutoInc, PrimaryKey */
    val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
    /** Database column user_id DBType(int4) */
    val userId: Column[Int] = column[Int]("user_id")
    /** Database column street DBType(varchar), Length(2147483647,true) */
    val street: Column[String] = column[String]("street", O.Length(2147483647,varying=true))
  }
  /** Collection-like TableQuery object for table Address */
  lazy val Address = new TableQuery(tag => new Address(tag))

  /** Entity class storing rows of table Detail
   *  @param id Database column id DBType(serial), AutoInc, PrimaryKey
   *  @param userId Database column user_id DBType(int4)
   *  @param value Database column value DBType(varchar), Length(2147483647,true) */
  case class DetailRow(id: Int, userId: Int, value: String)
  /** GetResult implicit for fetching DetailRow objects using plain SQL queries */
  implicit def GetResultDetailRow(implicit e0: GR[Int], e1: GR[String]): GR[DetailRow] = GR{
    prs => import prs._
    DetailRow.tupled((<<[Int], <<[Int], <<[String]))
  }
  /** Table description of table detail. Objects of this class serve as prototypes for rows in queries. */
  class Detail(_tableTag: Tag) extends Table[DetailRow](_tableTag, "detail") {
    def * = (id, userId, value) <> (DetailRow.tupled, DetailRow.unapply)
    /** Maps whole row to an option. Useful for outer joins. */
    def ? = (id.?, userId.?, value.?).shaped.<>({r=>import r._; _1.map(_=> DetailRow.tupled((_1.get, _2.get, _3.get)))}, (_:Any) =>  throw new Exception("Inserting into ? projection not supported."))

    /** Database column id DBType(serial), AutoInc, PrimaryKey */
    val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
    /** Database column user_id DBType(int4) */
    val userId: Column[Int] = column[Int]("user_id")
    /** Database column value DBType(varchar), Length(2147483647,true) */
    val value: Column[String] = column[String]("value", O.Length(2147483647,varying=true))
  }
  /** Collection-like TableQuery object for table Detail */
  lazy val Detail = new TableQuery(tag => new Detail(tag))

  /** Entity class storing rows of table User
   *  @param id Database column id DBType(serial), AutoInc, PrimaryKey
   *  @param name Database column name DBType(varchar), Length(2147483647,true) */
  case class UserRow(id: Int, name: String)
  /** GetResult implicit for fetching UserRow objects using plain SQL queries */
  implicit def GetResultUserRow(implicit e0: GR[Int], e1: GR[String]): GR[UserRow] = GR{
    prs => import prs._
    UserRow.tupled((<<[Int], <<[String]))
  }
  /** Table description of table user. Objects of this class serve as prototypes for rows in queries. */
  class User(_tableTag: Tag) extends Table[UserRow](_tableTag, "user") {
    def * = (id, name) <> (UserRow.tupled, UserRow.unapply)
    /** Maps whole row to an option. Useful for outer joins. */
    def ? = (id.?, name.?).shaped.<>({r=>import r._; _1.map(_=> UserRow.tupled((_1.get, _2.get)))}, (_:Any) =>  throw new Exception("Inserting into ? projection not supported."))

    /** Database column id DBType(serial), AutoInc, PrimaryKey */
    val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
    /** Database column name DBType(varchar), Length(2147483647,true) */
    val name: Column[String] = column[String]("name", O.Length(2147483647,varying=true))
  }
  /** Collection-like TableQuery object for table User */
  lazy val User = new TableQuery(tag => new User(tag))
}

ОБНОВЛЕНИЕ: теперь я понимаю, что .first в подзапросе выдает String там, где мне действительно нужно, чтобы это было Column[String] Что-то вроде

Detail.filter(_.userId === user.id).map(_.value).max

работает, но тогда подзапрос выглядит так:

SELECT MAX(value) FROM Detail WHERE userId = user.id

когда мне нужно, чтобы это выглядело примерно так:

SELECT value FROM Detail WHERE userId = user.id LIMIT 1


person Caballero    schedule 21.11.2014    source источник


Ответы (1)


Я думаю, вы хотите что-то вроде этого:

val q = for { 
  ((u, a), d) <- User.innerJoin(Addres).on(_.id === _.userId)
    .innerJoin(Detail).on(_._1.id === _.userId)
  } yield (u, a, d.value)

val result = q.list 
person tfh    schedule 21.11.2014
comment
Спасибо за предложение, но нет. Я упомянул, что это упрощенная версия задачи, и это обязательно должен быть подзапрос. - person Caballero; 21.11.2014
comment
Хорошо, я думаю, я понимаю вашу точку зрения. Обратите внимание, что и first, и list являются методами Invoker и попадают в базу данных. Это означает, что вы не выполняете подзапрос в своем коде. - person tfh; 21.11.2014
comment
Первоначально я ошибочно думал, что .first на самом деле является функцией агрегации SQL FIRST() - w3schools.com/sql/sql_functions.asp - person Caballero; 21.11.2014
comment
Почему вам нужно использовать подзапрос вместо соединения? Обычно можно заменить подзапрос соединением (см. мою суть по адресу: gist.github.com /mobiworx/e2472d87a87d3b2622e7#file-query — пример может помочь, если вы найдете способ использовать объединение вместо подзапроса). - person tfh; 21.11.2014