how to make the Query to list in slick2? - scala

My table:
case class Subject(id: Int, name:String, describe: String, sub_resource:String, addId:Long, recommand:Int, commentsum :Int, commentnumber: Int, userId: Int)
class Subjects(tag: Tag) extends Table[Subject](tag, "Subject") {
def id=column[Int]("ID", O.PrimaryKey)
def name=column[String]("Name")
def describe=column[String]("describe")
def sub_resource=column[String]("Resource")
def keywords=column[String]("Keyword")
def addID=column[Long]("Address")
def recommandrate=column[Int]("Recommand")
def commentsum=column[Int]("Sum_of_rate")
def commentnumber=column[Int]("Rate_number")
def userId=column[Int]("owner")
def uniqueName = index("idx_grp_name", name, unique = true)
def * = (id, name,sub_resource,keywords, addID, recommandrate, commentsum, commentnumber,userId)<> (Subject.tupled, Subject.unapply)
def sub_res=foreignKey("sub_res_FK", sub_resource, resource)(_.link)
def sub_address=foreignKey("sub_add_FK", addID, address)(_.id)
def sub_user=foreignKey("sub_user_FK", userId, user)(_.id)
}
val subject = TableQuery[Subjects]
I want get the list name contain "USA" and "China":
How to write the filter and list the name, userId and describe?
I want to use subject.filter(....).....

you need both a filter (to filter on USA and China) and a map to transform to name, userId, and decribe so something like (untested)
subject.filter(i => i.name === "USA" || i.name === "China")
.map(i => (i._2, i._10, i._3)
as the result is a list of tuples you have to access the fields with the ._[index] thingie
[update]
I see not all your fields are in your def * = ... so that's also something to look at
Edited by cvogt: I corrected the Syntax. It's just like using Scala collections, except for the ===.

Related

How to optionally update field if parameters not None

I have a table with non nullable columns:
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def surname = column[String]("surname")
}
I want update some columns only (if not None):
def update(id: String, name: Option[String], surname: Option[String]) = {
(name, surname) match {
case (Some(n), Some(s)) => byId(id)
.map(l => (l.name, l.surname))
.update((n, s))
case (None, Some(s)) => byId(id)
.map(l => (l.surname))
.update(s)
case (Some(n),None) => byId(id)
.map(l => (l.name))
.update(n)
}
}
Is there more elegant way to do this? What if there are lot of update parameters?
Although I am able to make two queries, I am left with the option to use the existing one and always make only one update:
def byId(id: String) = ???
def update(id: String, name: Option[String], surname: Option[String]) = {
val filterById = byId(id).map(u => (u.name, u.surname))
for {
(existingName, existingSurname) <- filterById.result.head
rowsAffected <- filterById.update((name.getOrElse(existingName), surname.getOrElse(existingSurname)))
} yield rowsAffected
}
PD: Same for large objects .. we map the entire row and then make a kind of patch to update it again

Typed projection with Slick

I'm trying to define a Type on the query side to map my join so that I can avoid returning a tuple of values that I have to manually apply to my projection case class post query.
Given a relationship like:
case class Parent(id: Int, name: String, extra: String)
class ParentTable(tag: Tag) extends Table[Parent](tag, "parent") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def extra = column[String]("extra")
def * = (id, name, extra) <> (Parent.tupled, Parent.unapply)
}
val parents = TableQuery[ParentTable]
case class Child(id: Int, parentId: Int, name: String, extra: String)
class ChildTable(tag: Tag) extends Table[Child](tag, "child") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def parentId = column[Int]("parent_id")
def parent = foreignKey("parent_fk", parentId, parents)(_.id)
def name = column[String]("name")
def extra = column[String]("extra")
def * = (id, parentId, name, extra) <> (Child.tupled, Child.unapply)
}
val children = TableQuery[ChildTable]
I want to project into a case class like:
case class ChildWithParentName(id: Int, name: String, parentName: String)
The join and projection looks like:
val q = for {
c <- children
p <- parents if c.parentId === p.id
} yield (c.id,c.name,p.name)
I put this in a function and allow children and parents to be parameterized. The function doesn't run the query, because sometimes I want .result and sometimes I want .result.headOption, so my function signature is:
Query[(Rep[Int], Rep[String], Rep[String]), (Int, String, String), Seq]
I would like to create a Type on the Query side with a shape something like:
class ChildParentProjection(val id: Rep[Int],
val name: Rep[String],
val parentName[String])
so that I could get a function signature like:
Query[ChildParentProjection, ChildWithParentName, Seq]
Is that possible in slick?
I don't really understand why you would like to use the class ChildParentProjection.
if you want to return a Seq[ChildWithParentName] when executing result on the query, you'll have to map the tuple resulting from your monadic join to the ChildWithParentName class like this :
val q = for {
c <- children
p <- parents if c.parentId === p.id
} yield (c.id,c.name,p.name) <> (ChildWithParentName.tupled,ChildWithParentName.unapply)
I wish I have understood your question

Slick join two tables and get result of both

I have a Many to Many relationship setup like this:
Person <-> PersonField <-> Field
Now I want to query not only all the fields of a Person (I can do that), but a joined version of PersonField with Field of a Person. (I want to query/retrieve the Information in the Pivot/Intermediate Table "PersonField" as well!)
Person:
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]
}
Field:
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]
}
Now to query all the fields of a Person I have a little helper-class:
def getFields(p: Person): Future[Seq[Field]] =
{
val query = p.fields
db.run(query.result)
}
So I can do
val personX ...
personX.onSuccess
{
case p: Person =>
{
val fields = helper.getFields(p)
fields.onSuccess
{
case f: Seq[Field] => f foreach println
}
}
}
Now each field of personX gets printed to the console. Works like a charm.
The thing is, I want to get the PersonField as well (with the Field)!
So I tried the following changes (among others that didn't work, which I can't remember)
In Person:
def fields =
{
for
{
person <- Persons.all.filter(_.id === this.id)
field <- person.fields join Fields.all on (_.fieldID === _.id)
} yield field
}
In PersonS
def fields = PersonFields.all.filter(_.personID === id) // No flatMap here!
then getFields(p: Person) looks like this:
def getFields(p: Person): Future[Seq[(PersonField, Field)]]
but
personX.onSuccess
{
case p: Person =>
{
val fields = helper.getFields(p)
fields.onSuccess
{
case f: Seq[(PersonField, Field)] => f map(f => println(f._1)}
}
}
}
gives me nothing, so I guess my join must be wrong. But what exactly am I doing wrong?
You can join all three, then yield the result
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)
(I am not sure I got exactly what you were trying to get out of the query, so you can just edit the yield part )

how to get the Joined foregin key object in slick?

I use playframework2+slick2.0
My datamodel:
class Page(tag:Tag) extends Table[(Int,Int, String,String,String, Option[String], Option[String])](tag, "Page"){
def id=column[Int]("ID", O.PrimaryKey)
def subId=column[Int]("subject")
def title=column[String]("Title", O.NotNull)
def describe=column[String]("Describe")
def profile=column[String]("Profile")
def icon=column[Option[String]]("icon")
def resId=column[String]("Picture")
def * = (id, subId,title, describe, profile,icon, resId.?)
def page_sub=foreignKey("PA_SU_FK", subId, subject)(_.id)
def page_res=foreignKey("PA_RE_FK", resId, resource)(_.link)
}
val page=TableQuery[Page]
class Resource(tag:Tag) extends Table[(String, String, Boolean)](tag, "Resource"){
def link=column[String]("Link", O.PrimaryKey)
def rtype=column[String]("class")
def local=column[Boolean]("local")
def * = (link, rtype,local)
}
val resource=TableQuery[Resource]
I want to use the filter to get one row, and get the resource object form page:
my code is like:
val item=page.filter(_.id===id.toInt).first()(rs.dbSession)
This is only get the page real object, I want get the correspond resource object, how to get it by filter?
You can use for-comprehension:
val pageWithResouce = (for {
p <- page if (p.id === id.toInt)
r <- resource if (p.resId === r.link)
} yield { (p, r) })list

How to parametrize Scala Slick queries by WHERE clause conditions?

Assume these two simple queries:
def findById(id: Long): Option[Account] = database.withSession { implicit s: Session =>
val query = for (a <- Accounts if a.id === id) yield a.*
query.list.headOption
}
def findByUID(uid: String): Option[Account] = database.withSession { implicit s: Session =>
val query = for (a <- Accounts if a.uid === uid) yield a.*
query.list.headOption
}
I would like to rewrite it to remove the boilerplate duplication to something like this:
def findBy(criteria: ??? => Boolean): Option[Account] = database.withSession {
implicit s: Session =>
val query = for (a <- Accounts if criteria(a)) yield a.*
query.list.headOption
}
def findById(id: Long) = findBy(_.id === id)
def findByUID(uid: Long) = findBy(_.uid === uid)
I don't know how to achieve it for there are several implicit conversions involved in the for comprehension I haven't untangled yet. More specifically: what would be the type of ??? => Boolean in the findBy method?
EDIT
These are Account and Accounts classes:
case class Account(id: Option[Long], uid: String, nick: String)
object Accounts extends Table[Account]("account") {
def id = column[Option[Long]]("id")
def uid = column[String]("uid")
def nick = column[String]("nick")
def * = id.? ~ uid ~ nick <> (Account, Account.unapply _)
}
I have this helper Table:
abstract class MyTable[T](_schemaName: Option[String], _tableName: String) extends Table[T](_schemaName, _tableName) {
import scala.slick.lifted._
def equalBy[B: BaseTypeMapper]
(proj:this.type => Column[B]):B => Query[this.type,T] = { (str:B) =>
Query[this.type,T,this.type](this) where { x => proj(x) === str} }
}
Now you can do:
val q=someTable.equalBy(_.someColumn)
q(someValue)