slick 2.0 define generic `find by field` method - scala

import scala.slick.driver.MySQLDriver.simple._
class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
case class QueryExt[B](q: Query[RichTable.this.type, B]) {
def whereEq[C](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
}
}
Then it complains
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:10: value === is not a member of slick.driver.MySQLDriver.simple.Column[C]
[error] col(fields) === c
[error] ^
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:9: ambiguous implicit values:
[error] both value BooleanColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Boolean]]
[error] and value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Option[Boolean]]]
[error] match expected type scala.slick.lifted.CanBeQueryCondition[Nothing]
[error] q.filter { fields =>
[error] ^
[error] two errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed Mar 6, 2014 1:21:48 AM
There have been questions about this, but the answers did not work for 2.0
How to parametrize Scala Slick queries by WHERE clause conditions?

Slick doesn't have any information about C, so it doesn't know if it can and how it should map it to a database value and if it can use === on it. So you get a type error. You will have to use Scala's type system to restrict the type to one for which Slick knows how to map it. You can do this by providing a so-called Context Bound, in this case :BaseColumnType.
def whereEq[C:BaseColumnType](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
BaseColumnType is provided by Slick and using it in this way basically tells the Scala compiler to look for an implicit value of type BaseColumnType[C] in scope, where you call whereEq. Because then it is usually known what C will actually be. Slick comes with BaseColumnType[Int], BaseColumnType[String], etc. so at the call site, the Scala compiler can find one when your C is really an Int or String in that particular call and this way pass the info further to Slick.
Same for LiuTiger's question. abstract class Crud[..., PK:BaseColumnType] should do the trick, a trait doesn't work with context bounds. When implementing an abstract DAO be prepared to face a lot of challenges and get to the edges of your Scala type system skills and learn quite a bit about type inference order, implicit parameters, etc.

Related

How to make method generic without getting "No matching Shape found"

I am not sure how to get past this "No matching Shape found" error, apart from writing lots of boilerplate.
The basic idea illustrated in the Gist is that I have a very basic version of a method (works, but is very specific), then a version that takes the mapper parameter and is more generic (works too, but is specific to one particular type), and then a third version which takes a type parameter and would be very useful, but doesn't compile because of this error.
Basic method:
def updatePD_FirstNames(id: ids.PersonalDetailsId, firstNames: StringLtd30): Future[Int] = {
Better method:
def updatePD_SL(id: ids.PersonalDetailsId, mapper: tables.PersonalDetails => tables.profile.api.Rep[StringLtd30], sl: StringLtd30): Future[Int] = {
Ideal method (but doesn't compile):
def updatePD_X[X](id: ids.PersonalDetailsId, mapper: tables.PersonalDetails => tables.profile.api.Rep[X], sl: X): Future[Int] = {
```
[server] $ compile
[info] Compiling 1 Scala source to ... target\scala-2.12\classes...
[error] ...schema\DbProxy.scala:688: No matching Shape found.
[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection,
[error] you use an unsupported type in a Query (e.g. scala List),
[error] or you forgot to import a driver api into scope.
[error] Required level: slick.lifted.FlatShapeLevel
[error] Source type: slick.lifted.Rep[X]
[error] Unpacked type: T
[error] Packed type: G
[error] val q2: Query[tables.profile.api.Rep[X], X, Seq] = q1.map(mapper)
[error] ^
[error] one error found
[error] (server/compile:compileIncremental) Compilation failed
[error] Total time: 4 s, completed 23-Mar-2017 11:15:47
```
Full code at https://gist.github.com/aholland/0845bf29d836d672d006ab58f5f1c73c
The only obvious problem I can see in the code you've posted is that X is unconstrained. It could be any type, includes ones that Slick doesn't know how to process.
What you can do is add a context bound on X. The bound you probably want is BaseTypedType, which is a "typed type" Slick uses to identify types it can work with. It's described from 11:30 in https://www.youtube.com/watch?v=tS6N5AaZTLA
You'd use it like this:
import slick.ast.BaseTypedType
def updatePD[X : BaseTypedType](
id: Long,
selector: PersonTable => Rep[X],
newValue: X
): DBIO[Int] =
people.filter(_.id === id).map(selector).update(newValue)
What that means is that when you use the method...
updatePD(anId, _.name, "Alice")
...the compiler has to prove to itself that whatever X you use, there is an approproate type representation in Slick.
This is also from Richard, but the exchange took place on gitter.
The only trouble with the first answer is that by demanding an implicit of type BaseTypedType[X] the context bound forces client code for optional columns to provide an implicit of type BaseTypedType[Option[X]] even when BaseTypedType[X] is already available.
This is unnecessary. Slick handles optional columns for you and if you provide an implicit for BaseTypedType[X] you are providing enough for it to handle columns of type Option[X].
So the context bound, while it works, is more demanding than necessary and results in having to write implicits in the client-code that involve directly referencing null and replicating logic already built into Slick. Not good.
The answer is to declare the implicit parameter as a named implicit parameter (called shape below) in its own parameter list, i.e. in long-form, not using the context bound short-hand :BaseTypedType. Then you can specify the more complicated but less demanding constraint used below.
So the solution is:
def updatePD[X] (id: Long, selector: PersonTable => Rep[X], newValue: X)
(implicit shape: Shape[_ <: FlatShapeLevel, Rep[X], X, _]): DBIO[Int] = {
people.filter(_.id === id).map(selector).update(newValue)
}
Understanding why shape has the exact type Shape[_ <: FlatShapeLevel, Rep[X], X, _] depends on an intimate understanding of Slick's types and implicit mechanisms. Richard may yet write a blog post on that!

Slick 3.1.1 ambiguous implicit values involving filter and implicit CanBeQueryCondition

I have been trying to create a Generic Dao over Slick 3.1.1 and it includes a generic filter that competes with JPA's findByExample, see the following files:
GenericDaoImpl.scala Generic level reusable across all Models
UserDao.scala Generic plus customizations for the User model
UserService.scala Wraps the UserDao into more services level functionality
In this last file I try to use the generic filter function to find a user by its registered email, like this:
// this will implicitly exec and wait indefinitely for the
// db.run Future to complete
import dao.ExecHelper._
def findByEmail(email: String): Option[UserRow] = {
userDao.filter(_.email === email).headOption
}
but this produces the compiler error:
[error] /home/bravegag/code/play-authenticate-usage-scala/app/services/UserService.scala:35: value === is not a member of String
[error] userDao.filter(email === _.email).headOption
[error] ^
[error] /home/bravegag/code/play-authenticate-usage-scala/app/services/UserService.scala:35: ambiguous implicit values:
[error] both value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => slick.lifted.CanBeQueryCondition[slick.lifted.Rep[Option[Boolean]]]
[error] and value BooleanCanBeQueryCondition in object CanBeQueryCondition of type => slick.lifted.CanBeQueryCondition[Boolean]
[error] match expected type slick.lifted.CanBeQueryCondition[Nothing]
[error] userDao.filter(email === _.email).headOption
[error] ^
Can anyone advice on how the implicit declaration of the filter function below can be improved to solve this compiler error?
The implementation of the filter function (found in GenericDaoImpl.scala) is:
// T is defined above as T <: Table[E] with IdentifyableTable[PK]
override def filter[C <: Rep[_]](expr: T => C)
(implicit wt: CanBeQueryCondition[C]) : Future[Seq[E]] =
db.run(tableQuery.filter(expr).result)
As far as I can see you are simply lacking you profile API import in UserService.
Just add there this import: import profile.api._ and it should work.
EDIT: BTW I see many people building their own version of base CRUDs for Slick. Did you try some existing thin libraries doing just that e.g. here: https://github.com/VirtusLab/unicorn ? It's not really related to this question but it may be worth to take a look.

Implementing custom foreach in order to better understand types and function composition

In order to try and understand Scala's type system I'm attempting
to implment a custom implementation for List.foreach method :
package com
object customForEach extends App {
class customForEach[B, A] extends Iterable[A] with collection.Seq[A] {
def foreach[B](f: A ⇒ B) {
var these = this
while (!these.isEmpty) {
f(these.head)
these = these.tail
}
}
def tail = this match {
case h :: t ⇒ t
}
}
}
When I complile this code I receive errors :
[error] \Desktop\Scala\src\main\scala\typeparam.scala:16: constructor cannot be instantiated to expected type;
[error] found : scala.collection.immutable.::[B(in class ::)]
[error] required: com.customForEach.customForEach[B(in class customForEach),A]
[error] case h :: t ? t
[error] ^
[error] \Desktop\Scala\src\main\scala\typeparam.scala:16: not found: value t
[error] case h :: t ? t
[error] ^
[error] \Desktop\Scala\src\main\scala\typeparam.scala:11: type mismatch;
[error] found : Seq[A]
[error] required: com.customForEach.customForEach[B,A]
[error] these = these.tail
[error] ^
[error] three errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed 31-Jan-2015 11:53:40
In particular I find it iteresting how println can be composed with List in this fashion : List(1,2,3).foreach(println)
Do I need to add extend another trait in order to access the .tail function ?
For this error :
not found: value t
[error] case h :: t ? t
Shouldn't t be found since it is created using pattern match operator :: ?
There are many reasons why this code won't work. In order to understand the first compiler error not found: value t, you must look at the error immediately before it. :: exists solely for List, but here you do not have a List, only Iterable with Seq. That pattern match can't work, which causes t to become "not found".
There are even larger problems than that, though. Even if you remove your definition of tail (which is unnecessary), you'll then find that you're missing abstract method definitions for apply, iterator, and length from the Seq trait. I imagine you're doing this because you can't extend List, which is sealed. You can copy the implementations of apply, and length from LinearSeqOptimized, then easily implement an iterator method, but there's still another problem: your class does not have a constructor.
Okay, well we'll look at what List does again. List is abstract and has two sub-types, :: and Nil. Nil is just a case object, and :: has a constructor that accepts the head and tail of the List. This isn't going to help you very much, unless you also want to duplicate the code for :: and Nil as well.
Scala collections are very large complicated beasts, and extending them to override one method is not a simple process.

Akka/Scala: onSuccess for multiple asks to different actor

I'm trying to make an async call to different sub-actors, such as:
A ---> B ---> C
\---> D
Actor A sends a request message to Actor B and B send two task messages to C and D, when C and D send the results back, B merge the results up and send it back to the A.
I was trying to use ask pattern and onSuccess to solve this:
class B(fakeInterface: String, subInterface: Array[ActorRef]) extends Actor {
val subCount = subInterface.length
var finishCount = 0
def receive = {
case reqMsg(msg) =>
if (subInterface.length == 0){
sender ! DoneMessage(msg)
} else {
implicit val timeout = Timeout(5 minutes)
val composedFutures = subInterface map { x =>
(x ? DoItMessage(msg)).mapTo[DoneMessage]
}
val allResult = Future.sequence(composedFutures)
allResult.onSuccess {
case _ => sender ! DoneMessage(msg)
}
}
}
}
But the code above does not compile at all, I got three error:
[error] inferred type arguments [dummy.DoneMessage,Array] do not conform to method sequence's type parameter bounds [A,M[_] <: TraversableOnce[_]]
[error] val allResult = Future.sequence(composedFutures)
[error] ^
[error] type mismatch;
[error] found : Array[scala.concurrent.Future[dummy.DoneMessage]]
[error] required: M[scala.concurrent.Future[A]]
[error] val allResult = Future.sequence(composedFutures)
[error] ^
[error] Cannot construct a collection of type M[A] with elements of type A based on a collection of type M[scala.concurrent.Future[A]].
[error] val allResult = Future.sequence(composedFutures)
[error] ^
[error] three errors found
How can I fix this? Or is there a more proper way to solve this scenario?
Array is not a TraversableOnce. There is an implicit conversion from Array to WrappedArray which is a TraversableOnce, but by the time the type parameter has been inferred to Array it is too late for the implicit conversion. If you replace Array with one of the classes from the collections library, e.g. List you can get past these compiler errors.
The ask pattern is inefficient because it has to create a fake actor for every request, which creates considerable overhead. It is useful for allowing non-actor code to communicate with actors, but actors themselves should message each other directly. Actor B will have to be stateful so it can keep track of messages from C and D as they come back, but it would make for a better solution.
scala.Array does not inherit from scala.collection.TraversableOnce so it cannot be used with Future.sequence. Try using a List or Seq instead.
http://www.scala-lang.org/api/current/index.html#scala.Array

Is there a way to deflate & map bit-mapped database columns into scala objects via slick

This is in continuation to the following post:
How to combine multiple columns in one case class field when using lifted embedding?
I am a great fan of bit fields, wants to use this logic in a project where I am using slick-macros. Unfortunately slick-additions is not fully updated to slick 2.0, there is a branch dedicated which is not stable, in fact won't even compile. I wanted to test above logic directly but ended up with errors below:
As per the post above:
trait Bitmasked {
type Value
def bitFor: Value => Int
def forBit: Int => Value
def values: Iterable[Value]
def longToSet: Long => Set[Value] =
bm => values.toSeq.filter(v => 0 != (bm & (1 << bitFor(v)))).toSet
def setToLong: Set[Value] => Long =
_.foldLeft(0L){ (bm, v) => bm + (1L << bitFor(v)) }
implicit lazy val enumTypeMapper: BaseTypeMapper[Value] =
MappedTypeMapper.base[Value, Int](bitFor, forBit)
implicit lazy val enumSetTypeMapper: BaseTypeMapper[Set[Value]] =
MappedTypeMapper.base[Set[Value], Long](setToLong, longToSet)
implicit lazy val getResult: GetResult[Value] =
GetResult(r => forBit(r.nextInt))
implicit lazy val getSetResult: GetResult[Set[Value]] =
GetResult(r => longToSet(r.nextLong))
}
/** Mix this class into a subclass of Enumeration to have it usable as a
* column type in a Slick Table. */
trait BitmaskedEnumeration extends Bitmasked { this: Enumeration =>
def bitFor = _.id
def forBit = apply(_)
}
object Role extends Enumeration with BitmaskedEnumeration {
type Role = Value
val Editor, Moderator, Administrator, Usermoderator, Usermoderator2, Partner, PremiumPartner, CorporatePaid = Value
}
.
[error] C:\git\slick-macros\database\src\main\scala\model\Database.scala:9: object BaseTypeMapper is not a member of package scala.slick.lifted
[error] import slick.lifted.{BaseTypeMapper, MappedTypeMapper}
[error] ^
[error] C:\git\slick-macros\database\src\main\scala\model\Database.scala:56: not found: value Role
[error] import Role._
[error] ^
[error] C:\git\slick-macros\database\src\main\scala\model\Database.scala:61: not found: type Role
[error] case class Member(login: String, rights: UserRights, roles: Role, addr: Address, company: Company, manager: Option[Member]) {
[error] ^
[error] three errors found
[error] (database/compile:compile) Compilation failed
[error] Total time: 17 s, completed Mar 18, 2014 1:18:30 PM
Is there a way to bit-mapped database columns deflated and mapped?
say, a user is an Editor when the value is 1 (binary 0001), Moderator when the value is 2 (binary 0010) and both Editor and Moderator when the value is 3 (binary 0011). I want a way to deflate this and set the flags in Scala.
Thank You,
shashi
Please read the documentation on mapping custom types. http://slick.typesafe.com/doc/2.0.0/userdefined.html#scalar-types