scala - Slick une dos tablas y obtiene el resultado de ambos
join playframework-2.0 (1)
Tengo una configuración de relación Muchos a Muchos como esta:
Persona <-> CampoPersonal <-> Campo
Ahora quiero consultar no solo todos los campos de una Persona (puedo hacerlo), sino una versión conjunta de PersonField con Field of a Person. (¡Quiero consultar / recuperar la información en la tabla pivote / intermedio "PersonField" también!)
Persona:
case class Person(id: Long, name: String)
{
def fields =
{
person <- Persons.all.filter(_.id === this.id)
field <- person.fields
} yield field
}
class Persons(tag: Tag) extends Table[Person](tag, "persons")
{
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = (id, name) <> (Person.tupled, Person.unapply)
def fields = PersonFields.all.filter(_.personID === id).flatMap(_.fieldFK)
}
object Persons
{
lazy val all = TableQuery[Persons]
}
Campo:
case class Field(id: Long, name: String, description: Option[String])
class Fields(tag: Tag) extends Table[Field](tag, "fields")
{
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def description = column[Option[String]]("description")
def * = (id, name, description) <> (Field.tupled, Field.unapply)
}
object Fields
{
lazy val all = TableQuery[Fields]
}
PersonField:
case class PersonField(id: Long, personID: Long, fieldID: Long, value: String)
// TODO add constraint to make (personID, fieldID) unique
class PersonFields(tag: Tag) extends Table[PersonField](tag, "person_field")
{
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def personID = column[Long]("person_id")
def fieldID = column[Long]("field_id")
def value = column[String]("value")
def * = (id, personID, fieldID, value) <> (PersonField.tupled, PersonField.unapply)
def personFK = foreignKey("person_fk", personID, Persons.all)(_.id)
def fieldFK = foreignKey("field_fk", fieldID, Fields.all)(_.id)
}
object PersonFields
{
lazy val all = TableQuery[PersonFields]
}
Ahora, para consultar todos los campos de una Persona, tengo una pequeña clase auxiliar:
def getFields(p: Person): Future[Seq[Field]] =
{
val query = p.fields
db.run(query.result)
}
Entonces puedo hacer
val personX ...
personX.onSuccess
{
case p: Person =>
{
val fields = helper.getFields(p)
fields.onSuccess
{
case f: Seq[Field] => f foreach println
}
}
}
Ahora cada campo de personX se imprime en la consola. Funciona de maravilla.
La cuestión es que también quiero obtener el PersonField (con el campo).
Así que probé los siguientes cambios (entre otros que no funcionaron, que no recuerdo)
En persona :
def fields =
{
for
{
person <- Persons.all.filter(_.id === this.id)
field <- person.fields join Fields.all on (_.fieldID === _.id)
} yield field
}
En persona
def fields = PersonFields.all.filter(_.personID === id)
// ¡No hay flatMap aquí!
luego getFields (p: Person) se ve así:
def getFields(p: Person): Future[Seq[(PersonField, Field)]]
pero
personX.onSuccess
{
case p: Person =>
{
val fields = helper.getFields(p)
fields.onSuccess
{
case f: Seq[(PersonField, Field)] => f map(f => println(f._1)}
}
}
}
no me da nada, así que supongo que mi unión debe estar equivocada. ¿Pero qué estoy haciendo exactamente mal?
Puede unir los tres, luego dar el resultado
for {
((personField, person), field) <- PersonFields.all join Persons.all on (_.personId === _.id) join Fields.all on (_._1.fieldId === _.id)
if person.id === this.id
} yield (personField, person, field)
(No estoy seguro de haber obtenido exactamente lo que intentaba obtener de la consulta, por lo que puede editar la parte de rendimiento)