How to reuse Slick Relationships on a tuple? - scala

Using documentation in Slick 3.1.0 as source for classes :
implicit class PersonExtensions[C[_]](q: Query[People, Person, C]) {
def withAddress = q.join(addresses).on(_.addressId === _.id)
def withContact = q.join(contacts).on(_.contactId === _.id)
}
I would like to be able to do something like this :
val chrisQuery = people.filter(_.id === 2)
val chrisWithAddressWithContact: Future[((Person, Address), Contact)] =
db.run(chrisQuery.withAddress.withContact.result.head)
val chrisWithContactWithAddress: Future[((Person, Contact), Address)] =
db.run(chrisQuery.withContact.withAddress.result.head)

You can compose queries with zip.
val q1: Query[Xs, X, Seq]
val q2: Query[Ys, Y, Seq]
val query: Query[(Xs, Ys), (X, Y), Seq] = q1 zip q2
val results: DBIO[Seq[(X, Y)]] = query.result
val result: DBIO[(X, Y)] = results.head
db.run(result).map { case (r1: X, r2: Y) => ...}
Of course you can omit types and inline everything :)
Doc: http://slick.typesafe.com/doc/3.0.0/queries.html#zip-joins

Related

How to define an empty query

Inside a for-comp I need to set up some queries that will be then run in a single shot later... say, something like:
val queries = for {
_ <- query1
_ <- query2
id <- someInsertQuery
} yield id
db.run(queries.transactionally)
Tip: the queries are instances of slick.jdbc.JdbcActionComponent#ProfileAction
Now, I need to set up conditions on the first 2 queries. If a condition is met, the queries should run, nothing otherwise. So I thought:
val queries = for {
_ <- if (condition1) query1 else Query.empty
_ <- if (condition2) query2 else Query.empty
id <- someInsertQuery
} yield id
db.run(queries.transactionally)
But this doesn't work:
value flatMap is not a member of Object
[error] _ <- if (condition1) query1 else Query.empty
[error] ^
So I bet what I am trying to do is not this way. What would be the proper way to achieve this?
Update1:
More details. The first item in the for-comp looks like this (considering Boris' idea):
val queries = for {
_ <- someOption.map(someRow => SomeTableQuery += someRow).geOrElse(Query.empty.result)
}
SomeTableQuery is an instance of slick.lifted.TableQuery[SomeClass] and someOption is Option[SomeClass]
I think you have some misunderstanding with for-yield construction.
In the scala, for-yield is just syntactic sugar under some combinations of flatMap, map functions.
val queries = for {
_ <- if (condition1) query1 else Query.empty
_ <- if (condition2) query2 else Query.empty
res <- someInsertQuery
} yield res
for the compiler it is the same as:
query1.flatMap(_ => query2.flatMap(_ => someInsertQuery.map(res => res)))
if your query1 and query2 is some ProfileAction:
val query1: ProfileAction[Int, NoStream, Effect.Write] = ???
val query2: ProfileAction[Int, NoStream, Effect.Write] = ???
so, the type of the expression if (condition1) query1 else Query.empty is Object, because Query.empty has type - Query[Unit, Unit, Seq] and the nearest common ancestor of Query and ProfileAction is an Object type, which has not flatMap function. To make your code compilable, you should make all branches of if ... else construction having the same type which has flatMap function, here it would be so if we call result on Query.empty:
val result1: FixedSqlAction[Any, NoStream, Effect.Write with Effect.Read] = if (condition1) query1 else Query.empty.result
val result2: FixedSqlAction[Any, NoStream, Effect.Write with Effect.Read] = if (condition2) query2 else Query.empty.result
Possible version of your code:
import slick.jdbc.JdbcBackend
import slick.lifted.Query
import slick.jdbc.H2Profile.api._
val db: JdbcBackend.Database = ???
val query1: ProfileAction[Int, NoStream, Effect.Write] = ???
val query2: ProfileAction[Int, NoStream, Effect.Write] = ???
val someInsertQuery: ProfileAction[Int, NoStream, Effect.Write] = ???
val condition1 = false
val condition2 = true
val queries = for {
_ <- if (condition1) query1 else Query.empty.result
_ <- if (condition2) query2 else Query.empty.result
res <- someInsertQuery
} yield res
db.run(queries.transactionally)
If your query is actually options and not just queries, you can write option composition using flatMap for filtering empty options and use seq for sequential execution of result queries sequence. Example, using table in slick documentation:
import slick.jdbc.H2Profile.api._
case class Coffees(tag: Tag) extends Table[(String, Double)](tag, "COFFEES") {
def name = column[String]("COF_NAME")
def price = column[Double]("PRICE")
def * = (name, price)
}
val coffees: TableQuery[Coffees] = TableQuery[Coffees]
val maybeQuery1 = Option(("Colombian", 7.99))
val maybeQuery2 = Option(("Another name", 14.59))
val maybeQuery3 = Option(("Name", 4.39))
val queries = Seq(maybeQuery1, maybeQuery2, maybeQuery3).flatMap(_.map(someRow => coffees += someRow))
val db: Database = ???
db.run(DBIO.seq(queries:_*).transactionally)
The best way I have been able to solve it til now is by getting the condition inside a filter. Roughly:
val queries = for {
_ <- query1.filter(condition1)
_ <- query2.filter(condition2)
id <- someInsertQuery
} yield id
db.run(queries.transactionally)
Let me know if you have other ideas.

generate list of case class with int field without repeat

I want to generate a List of some class which contains several fields. One of them is Int type and it doesn’t have to repeat. Could you help me to write the code?
I tried next:
case class Person(name: String, age: Int)
implicit val genPerson: Gen[Person] =
for {
name <- arbitrary[String]
age <- Gen.posNum[Int]
} yield Person(name, age)
implicit val genListOfPerson: Gen[scala.List[Person]] = Gen.listOfN(3, genPerson)
The problem is that I got an instance of a person with equal age.
If you're requiring that no two Persons in the generated list have the same age, you can
implicit def IntsArb: Arbitrary[Int] = Arbitrary(Gen.choose[Int](0, Int.MaxValue))
implicit val StringArb: Arbitrary[String] = Arbitrary(Gen.listOfN(5, Gen.alphaChar).map(_.mkString))
implicit val PersonGen = Arbitrary(Gen.resultOf(Person.apply _))
implicit val PersonsGen: Arbitrary[List[Person]] =
Arbitrary(Gen.listOfN(3, PersonGen.arbitrary).map { persons =>
val grouped: Map[Int, List[Person]] = persons.groupBy(_.age)
grouped.values.map(_.head) // safe because groupBy
})
Note that this will return a List with no duplicate ages but there's no guarantee that the list will have size 3 (it is guaranteed that the list will be nonempty, with size at most 3).
If having a list of size 3 is important, at the risk of generation failing if the "dice are against you", you can have something like:
def uniqueAges(persons: List[Person], target: Int): Gen[List[Person]] = {
val grouped: Map[Int, List[Person]] = persons.groupBy(_.age)
val uniquelyAged = grouped.values.map(_.head)
val n = uniquelyAged.size
if (n == target) Gen.const(uniquelyAged)
else {
val existingAges = grouped.keySet
val genPerson = PersonGen.arbitrary.retryUntil { p => !existingAges(p.age) }
Gen.listOf(target - n, genPerson)
.flatMap(l => uniqueAges(l, target - n))
.map(_ ++ uniquelyAged)
}
}
implicit val PersonsGen: Arbitrary[List[Person]] =
Arbitrary(Gen.listOfN(3, PersonGen.arbitrary).flatMap(l => uniqueAges(l, 3)))
You can do it as follows:
implicit def IntsArb: Arbitrary[Int] = Arbitrary(Gen.choose[Int](0, Int.MaxValue))
implicit val StringArb: Arbitrary[String] = Arbitrary(Gen.listOfN(5, Gen.alphaChar).map(_.mkString))
implicit val PersonGen = Arbitrary(Gen.resultOf(Person.apply _))
implicit val PersonsGen: Arbitrary[List[Person]] = Arbitrary(Gen.listOfN(3, PersonGen.arbitrary))

map on a TreeMap returns a Map and not a TreeMap in Scala

I am new to Scala, and I am implementing a TreeMap with a multidimensional key like this:
class dimSet (val d:Vector[Int]) extends IndexedSeq[Int] {
def apply(idx:Int) = d(idx)
def length: Int = d.length
}
…
var vals : TreeMap[dimSet, A] = TreeMap[dimSet, A]()(orddimSet)
I have this method
def appOp0(t:TreeMap[dimSet,A], t1:TreeMap[dimSet,A], op:(A,A) => A, unop : (A) => A):TreeMap[dimSet,A] = {
if (t.isEmpty) t1.map((e:Tuple2[dimSet, A]) => (e._1, unop(e._2)))
else if (t1.isEmpty) t.map((t:Tuple2[dimSet, A]) => (t._1, unop(t._2)))
else {
val h = t.head
val h1 = t1.head
if ((h._1) == (h1._1)) appOp0(t.tail, t1.tail, op, unop) + ((h._1, op(h._2, h1._2)))
else if (orddimSet.compare(h._1,h1._1) == 1) appOp0(t, t1.tail, op, unop) + ((h1._1, unop(h1._2)))
else appOp0(t.tail, t1, op, unop) + ((h._1, unop(h._2)))
}
}
But the map method on the TreeMaps (second and third lines) returns a Map, not a TreeMap
I tried on repl with a simplier example and I got this:
scala> val t = TreeMap[dimSet, Double]( (new dimSet(Vector(1,1)), 5.1), (new dimSet(Vector(1,2)), 6.3), (new dimSet(Vector(3,1)), 7.1), (new dimSet(Vector(2,2)), 8.4)) (orddimSet)
scala> val tsq = t.map[(dimSet,Double), TreeMap[dimSet,Double]]((v:Tuple2[dimSet, Double]) => ((v._1, v._2 * v._2)))
<console>:41: error: Cannot construct a collection of type scala.collection.immutable.TreeMap[dimSet,Double] with elements of type (dimSet, Double) based on a collection of type scala.collection.immutable.TreeMap[dimSet,Double].
val tsq = t.map[(dimSet,Double), TreeMap[dimSet,Double]]((v:Tuple2[dimSet, Double]) => ((v._1, v._2 * v._2)))
^
scala> val tsq = t.map((v:Tuple2[dimSet, Double]) => ((v._1, v._2 * v._2)))
tsq: scala.collection.immutable.Map[dimSet,Double] = Map((1, 1) -> 26.009999999999998, (1, 2) -> 39.69, (2, 2) -> 70.56, (3, 1) -> 50.41)
I think CanBuildFrom cannot build my TreeMap as it can do with other TreeMaps, but I couldn't find why, ¿What can I do to return a TreeMap?
Thanks
The problem probably is that there is no implicit Ordering[dimSet] available when you call map. That call requires a CanBuildFrom, which in turn requires an implicit Ordering for TreeMap keys: see in docs.
So make orddimSet implicitly available before calling map:
implicit val ev = orddimSet
if (t.isEmpty) t1.map((e:Tuple2[dimSet, A]) => (e._1, unop(e._2)))
Or you can make an Ordering[dimSet] always automatically implicitly available, if you define an implicit Ordering in dimSet's companion object:
object dimSet {
implicit val orddimSet: Ordering[dimSet] = ??? // you code here
}

Scala+Slick - Get counts on GroupBy results

I'm trying to get counts from a DB using a groupBy on my Scala+Slick code.
Here's my partial code :
object DBJobs extends Table[DBJob]("encoder_job") {
object Status extends Enumeration {
val local = Value("LOCAL")
val encoding = Value("ENCODING")
val done = Value("DONE")
val error = Value("ERROR")
}
implicit val StatusMapper = MappedTypeMapper.base[Status.Value, String] (
{x => x.toString},
{x => x match {case "LOCAL"=>Status(0);case "ENCODING"=>Status(1);case "DONE"=>Status(2);case "ERROR"=>Status(3)}}
)
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def status = column[DBJobs.Status.Value]("status", O.NotNull)
def getStats()(implicit session:Session):mutable.Map[Status.Value, Int] = {
var map = mutable.Map[Column[Status.Value], Column[Int]]()
val q = (for { j <- DBJobs } yield (j)).groupBy(_.status).map{
case (s, results) =>
map = map += (s -> results.length)
}
map
}
}
My problem is how to put data in my Map as [DBJobs.Status, Int] instead of [Column[Status.Value], Column[Int]].
Here's the SQL equivalent :
SELECT COUNT( 1 ), status FROM encoder_job GROUP BY STATUS
Slick version: 1.0.1
Thanks
def getStats()(implicit session:Session):mutable.Map[Status.Value, Int] = {
Query(DBJobs).groupBy(_.status).map{
case (s, results) => (s -> results.length)
}
}
// usage
val results = getStats.run
Be aware that putting methods in the table object directly cannot be easily migrated to Slick 2.0. Put them separate, e.g. as method extensions. Also see https://groups.google.com/d/msg/scalaquery/xNtPT6sexXI/zlkgxv6lZ6YJ

How can I extend Scala collections with an argmax method?

I would like to add to all collections where it makes sense, an argMax method.
How to do it? Use implicits?
On Scala 2.8, this works:
val list = List(1, 2, 3)
def f(x: Int) = -x
val argMax = list max (Ordering by f)
As pointed by mkneissl, this does not return the set of maximum points. Here's an alternate implementation that does, and tries to reduce the number of calls to f. If calls to f don't matter that much, see mkneissl's answer. Also, note that his answer is curried, which provides superior type inference.
def argMax[A, B: Ordering](input: Iterable[A], f: A => B) = {
val fList = input map f
val maxFList = fList.max
input.view zip fList filter (_._2 == maxFList) map (_._1) toSet
}
scala> argMax(-2 to 2, (x: Int) => x * x)
res15: scala.collection.immutable.Set[Int] = Set(-2, 2)
The argmax function (as I understand it from Wikipedia)
def argMax[A,B](c: Traversable[A])(f: A=>B)(implicit o: Ordering[B]): Traversable[A] = {
val max = (c map f).max(o)
c filter { f(_) == max }
}
If you really want, you can pimp it onto the collections
implicit def enhanceWithArgMax[A](c: Traversable[A]) = new {
def argMax[B](f: A=>B)(implicit o: Ordering[B]): Traversable[A] = ArgMax.argMax(c)(f)(o)
}
and use it like this
val l = -2 to 2
assert (argMax(l)(x => x*x) == List(-2,2))
assert (l.argMax(x => x*x) == List(-2,2))
(Scala 2.8)
Yes, the usual way would be to use the 'pimp my library' pattern to decorate your collection. For example (N.B. just as illustration, not meant to be a correct or working example):
trait PimpedList[A] {
val l: List[A]
//example argMax, not meant to be correct
def argMax[T <% Ordered[T]](f:T => T) = {error("your definition here")}
}
implicit def toPimpedList[A](xs: List[A]) = new PimpedList[A] {
val l = xs
}
scala> def f(i:Int):Int = 10
f: (i: Int) Int
scala> val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)
scala> l.argMax(f)
java.lang.RuntimeException: your definition here
at scala.Predef$.error(Predef.scala:60)
at PimpedList$class.argMax(:12)
//etc etc...
Nice and easy ? :
val l = List(1,0,10,2)
l.zipWithIndex.maxBy(x => x._1)._2
You can add functions to an existing API in Scala by using the Pimp my Library pattern. You do this by defining an implicit conversion function. For example, I have a class Vector3 to represent 3D vectors:
class Vector3 (val x: Float, val y: Float, val z: Float)
Suppose I want to be able to scale a vector by writing something like: 2.5f * v. I can't directly add a * method to class Float ofcourse, but I can supply an implicit conversion function like this:
implicit def scaleVector3WithFloat(f: Float) = new {
def *(v: Vector3) = new Vector3(f * v.x, f * v.y, f * v.z)
}
Note that this returns an object of a structural type (the new { ... } construct) that contains the * method.
I haven't tested it, but I guess you could do something like this:
implicit def argMaxImplicit[A](t: Traversable[A]) = new {
def argMax() = ...
}
Here's a way of doing so with the implicit builder pattern. It has the advantage over the previous solutions that it works with any Traversable, and returns a similar Traversable. Sadly, it's pretty imperative. If anyone wants to, it could probably be turned into a fairly ugly fold instead.
object RichTraversable {
implicit def traversable2RichTraversable[A](t: Traversable[A]) = new RichTraversable[A](t)
}
class RichTraversable[A](t: Traversable[A]) {
def argMax[That, C](g: A => C)(implicit bf : scala.collection.generic.CanBuildFrom[Traversable[A], A, That], ord:Ordering[C]): That = {
var minimum:C = null.asInstanceOf[C]
val repr = t.repr
val builder = bf(repr)
for(a<-t){
val test: C = g(a)
if(test == minimum || minimum == null){
builder += a
minimum = test
}else if (ord.gt(test, minimum)){
builder.clear
builder += a
minimum = test
}
}
builder.result
}
}
Set(-2, -1, 0, 1, 2).argmax(x=>x*x) == Set(-2, 2)
List(-2, -1, 0, 1, 2).argmax(x=>x*x) == List(-2, 2)
Here's a variant loosely based on #Daniel's accepted answer that also works for Sets.
def argMax[A, B: Ordering](input: GenIterable[A], f: A => B) : GenSet[A] = argMaxZip(input, f) map (_._1) toSet
def argMaxZip[A, B: Ordering](input: GenIterable[A], f: A => B): GenIterable[(A, B)] = {
if (input.isEmpty) Nil
else {
val fPairs = input map (x => (x, f(x)))
val maxF = fPairs.map(_._2).max
fPairs filter (_._2 == maxF)
}
}
One could also do a variant that produces (B, Iterable[A]), of course.
Based on other answers, you can pretty easily combine the strengths of each (minimal calls to f(), etc.). Here we have an implicit conversion for all Iterables (so they can just call .argmax() transparently), and a stand-alone method if for some reason that is preferred. ScalaTest tests to boot.
class Argmax[A](col: Iterable[A]) {
def argmax[B](f: A => B)(implicit ord: Ordering[B]): Iterable[A] = {
val mapped = col map f
val max = mapped max ord
(mapped zip col) filter (_._1 == max) map (_._2)
}
}
object MathOps {
implicit def addArgmax[A](col: Iterable[A]) = new Argmax(col)
def argmax[A, B](col: Iterable[A])(f: A => B)(implicit ord: Ordering[B]) = {
new Argmax(col) argmax f
}
}
class MathUtilsTests extends FunSuite {
import MathOps._
test("Can argmax with unique") {
assert((-10 to 0).argmax(_ * -1).toSet === Set(-10))
// or alternate calling syntax
assert(argmax(-10 to 0)(_ * -1).toSet === Set(-10))
}
test("Can argmax with multiple") {
assert((-10 to 10).argmax(math.pow(_, 2)).toSet === Set(-10, 10))
}
}