Composite key and where in in squeryl - scala

How can I write deleteWhere clause in squeryl for entity with composite id?
val list: List[CompositeKey2[Long, Date]] = existing.map(x => x.id).toList
Schema.entities.deleteWhere(q => q.id in list)
Error:(82, 49) value in is not a member of org.squeryl.dsl.CompositeKey2[Long,java.util.Date]
Schema.entities.deleteWhere(q => q.id in list)
^

With a CompositeKey, the id method doesn't map directly to a column, so it isn't useful in an in clause. You'll have to structure your where to reference each column that makes up the private key individually. Without knowing the columns involved it's tough to be more specific, but something like
deleteWhere(q => existing.map(e => q.id1 === e.id1 and q.id2 === e.id2).reduce(_ or _))

Related

How to change many fields at the same time in Slick update query?

Lets say I have a table for movies which has many fields such as id, the name of the movie,year it was created etc. I know that the following works:
val updateMovieNameQuery = SlickTables.movieTable.filter(_.id === movieId).map(_.name).update("newName")
Is there any way how to update two or more fields in this way ? I tried the following but this doesnt work.
val updateMovieNameQuery = SlickTables.movieTable.filter(_.id === movieId).map(_.name,_.year).update("newName",1997)
I
Your answer is pretty close, but you need to extract the fields as a tuple and then pass a new tuple to update:
val updateMovieNameQuery = SlickTables
.movieTable
.filter(_.id === movieId)
.map(m => (m.name, m.year)) // Create tuple of field values
.update(("newName",1997)) // Pass new tuple of values

Slick 3.3.3 Filter table using `.inSet` on multiple columns

I have a table with a tuple (2 different columns) as my primary key. I am trying to make a function findByPrimaryKeys(pks: Vector[(Long, Long)]) that returns a query for all the rows with a primary key in the set pks. However, it seems there is no way to do this, I can do
table.filter(t => t.id1.inSet(pks.map(_._1)) && t => t.id2.inSet(pk2.map(_._2)))
However, this is not exactly correct, as it could return something that has a matching id2 but not id1.
Is there way to combine Reps?
Yes, it's possible to combine Rep value, there's an implicit conversion from (Rep[Long], Rep[Long]) to Rep[(Long, Long)]. But that doesn't help in this case because inSet is added through an implicit conversion, and that conversion requires an implicit BaseTypedType[(Long, Long)] to be available, which it isn't.
The only way I know to solve this problem is to use a fold:
table.filter { t =>
pks.foldLeft[Rep[Boolean]](false) { case (a, (x, y)) =>
a || (t.id1 === x && t.id2 === y)
}
}

Scala Tuple of seq to seq of object

I am having tuples of format as (DBIO[Seq[Person]], DBIO[Seq[Address]]) as one to one mapping. Person and Address is separate table in RDBMS. Profile definition is Profile(person: Person, address: Address). Now I want to convert the former into DBIO[Seq[Profile]]. Following is code snippet for how I have got (DBIO[Seq[Person]], DBIO[Seq[Address]])
for {
person <- personQuery if person.personId === personId
address <- addressQuery if address.addressId === profile.addressId
} yield (person.result, address.result)
Need help with this transformation to DBIO[Seq[Profile].
Assuming you can't use a join and you need to work with two actions (two DBIOs), what you can do is combine the two actions into a single one:
// Combine two actions into a single action
val pairs: DBIO[ ( Seq[Person], Seq[Address] ) ] =
(person.result).zip(address.result)
(zip is just one of many combinators you can use to manipulate DBIO).
From there you can use DBIO.map to convert the pair into the datastructure you want.
For example:
// Use Slick's DBIO.map to map the DBIO value into a sequence of profiles:
val profiles: DBIO[Seq[Profile]] = pairs.map { case (ppl, places) =>
// We now use a regular Scala `zip` on two sequences:
ppl.zip(places).map { case (person, place) => Profile(person, place) }
}
I am unfamiliar with whatever DBIO is. Assuming it is a case class of some type T :
val (DBIO(people), DBIO(addresses)) = for {
person <- personQuery if person.personId === personId
address <- addressQuery if address.addressId === profile.addressId
} yield (person.result, address.result)
val profiles = DBIO(people.zip(addresses).map{ case (person, address) => Profile(person, address)})

Slick query with one to many relationship

I'm using slick 3.2.3 and I'm trying to build a query that returns a Seq[Entity1, Seq[Entity2]] for two entities that have a one to many relationship (for each Entity1 are associated multiple Entity2).
So I have my two entities
case class Entity1(name: String, id: Option[Long] = None)
case class Entity2(entity1Id: Long, name: String, id: Option[Long] = None
with the table definitions (that are generated by slick codegen task)
class entity1Table(_tableTag: Tag) extends profile.api.Table[Entity1](_tableTag, "ENTITY_1") {
...
}
lazy val groupTable = new TableQuery(tag => new groupTable(tag))
class entity2Table(_tableTag: Tag) extends profile.api.Table[Entity2](_tableTag, "ENTITY_2") {
...
}
lazy val entity2Table = new TableQuery(tag => new entity2Table(tag))
Reading this article I've created a query like this
val q = (for {
e1 <- entity1Table
e2 <- entity2Table if e2.entity1Id === e1.id
} yield (e1, e2)).groupBy(_._1) map {
case (entity1, tuples) => (entity1, tuples.map(_._2))
}
db.run(q.result)
but I get this error at compile time:
Error:(19, 35) No matching Shape found.
Slick does not know how to map the given types.
Possible causes: T in Table[T] does not match your * projection,
you use an unsupported type in a Query (e.g. scala List),
or you forgot to import a driver api into scope.
Required level: slick.lifted.FlatShapeLevel
Source type: (my.namespace.models.entity1Table, slick.lifted.Query[my.namespace.models.entity2Table,my.namespace.models.Entity2,[+A]Seq[A]])
Unpacked type: T
Packed type: G
} yield (e1, e2)).groupBy(_._1) map {
I suspect that it cannot map the entity1Table and entity2Table.
How can I fix the error?
As specified in the Slick doc, groupBy currently doesn't support executing a query with nested values of type Query:
The intermediate query (i.e. the query ended with groupBy() without an
aggregate function) contains nested values of type Query. These would
turn into nested collections when executing the query, which is not
supported at the moment. Therefore it is necessary to flatten the
nested queries immediately by aggregating their values (or individual
columns)
In other words, your Slick groupBy query must be coupled with an aggregate function equivalent to SQL's count(), sum(), etc. For example, the following query which is equivalent to select count(*) ... group by ... having count(*) > 1 would work:
val query = ( for {
e1 <- entity1Table
e2 <- entity2Table if e2.entity1Id === e1.id
} yield (e1, e2)
).
groupBy(_._1).map{
case (entity1, group) => (entity1, group.size)
}.
filter(_._2 > 1)

Slick/Scala - how do I access fields of the mapped projection/projected table part of a join in a where query

I have a number of basic queries define, and am using query composition to add stuff such as ordering, paging, where clauses and so on...
But I have a problem accessing the fields of the joined 2nd table in the where clause...
Here's my table queries and my table. All tables are mapped to case classes.
val basicCars = TableQuery[CarTable]
val basicCarValues = TableQuery[CarValueTable]
val carsWithValues = for {
(c, v) <- basicCars leftJoin basicCarValues on (_.id === _.carId)
} yield (c, v.?)
Now I reuse/compose queries by doing stuff such as
carsWithValues.where(_._1.id === someId)
which works perfectly...
But if I want to access any value of the 2nd table... and I try
carsWithValues.where(_._2.latestPrice === somePrice)
It tells me that somePrice is not a member of MappedProjection......
error: value somePrice is not a member of scala.slick.lifted.MappedProjection[Option[com......datastore.slick.generated.Tables.CarValue],(Option[Long], Option[Long], Option[String],.....
I understand that this kind of can't work, cause _._2 is a MappedProjection and not just a CarValue sitting in the tuple..
But I can't figure out how to use any field of the table that is in the MappedProjection in a where clause?
The .? from the Slick code generator is implemented using a MappedProjection, which doesn't have the members anymore. If you postpone the call to .? it works:
val carsWithValues = for {
(c, v) <- basicCars leftJoin basicCarValues on (_.id === _.carId)
} yield (c, v)
carsWithValues.where(_._2.latestPrice === somePrice).map{ case (c,v) => (c,v.?) }