I have the following in a library:
Case Class:
case class Foo(
id: Option[Long],
bar: Long
...
)
Table:
object Foos extends Mapper[Foo]("foo"){ //I'm using slick-inegration so the id is free
def bar = column[Long]("bar")
def cols = bar ~ ...
def * = id.? ~: cols <> (Foo, Foo.unapply _)
def returningId = cols returning id
def insert(f: Foo)(implicit s: Session) = returningId.insert(Generic[Foo].to(f).tail.tupled)
...
}
The data access layer is set up in the binary that utilizes these models. If I try a comprehension such as "for(f<-Foos) yield f", inside of the Foos definition, we're happy. If I try it anywhere in the codebase which uses this library, I get:
value map is not a member of object DB.this.Foos
My guess is it's not getting lifted into a Query, but I'm not entirely sure. Any clarity would be appreciated.
Try doing:
import slick.driver.MySQLDriver.simple._
or whichever driver it is that you need. You'll need the implicit classes in there to provide the map 'extension' method on Foos.
Related
I am attempting to use scala-cass in order to read from cassandra and convert the resultset to a case class using resultSet.as[CaseClass]. This works great when running the following.
import com.weather.scalacass.syntax._
case class TestTable(id: String, data1: Int, data2: Long)
val resultSet = session.execute(s"select * from test.testTable limit 10")
resultSet.one.as[TestTable]
Now I am attempting to make this more generic and I am unable to find the proper type constraint for the generic class.
import com.weather.scalacass.syntax._
case class TestTable(id: String, data1: Int, data2: Long)
abstract class GenericReader[T] {
val table: String
val keyspace: String
def getRows(session: Session): T = {
val resultSet = session.execute(s"select * from $keyspace.$table limit 10")
resultSet.one.as[T]
}
}
I implement this class with the desired case class and attempt to call getRows on the created Object.
object TestTable extends GenericReader[TestTable] {
val keyspace = "test"
val table = "TestTable"
}
TestTable.getRows(session)
This throws an exception could not find implicit value for parameter ccd: com.weather.scalacass.CCCassFormatDecoder[T].
I am trying to add a type constraint to GenericReader in order to ensure the implicit conversion will work. However, I am unable to find the proper type. I am attempting to read through scala-cass in order to find the proper constraint but I have had no luck so far.
I would also be happy to use any other library that can achieve this.
Looks like as[T] requires an implicit value that you don't have in scope, so you'll need to require that implicit parameter in the getRows method as well.
def getRows(session: Session)(implicit cfd: CCCassFormatDecoder[T]): T
You could express this as a type constraint (what you were looking for in the original question) using context bounds:
abstract class GenericReader[T:CCCassFormatDecoder]
Rather than try to bound your generic T type, it might be easier to just pass through the missing implicit parameter:
abstract class GenericReader[T](implicit ccd: CCCassFormatDecoder[T]) {
val table: String
val keyspace: String
def getRows(session: Session): T = {
val resultSet = session.execute(s"select * from $keyspace.$table limit 10")
resultSet.one.as[T]
}
}
Finding a concrete value for that implicit can then be deferred to when you narrow that T to a specific class (like object TestTable extends GenericReader[TestTable])
Let's say I have a following case class:
case class Product(name: String, categoryId: Option[Long]/*, other fields....*/)
Here you can see that categoryId is optional.
Now let's say I have a following method in my DAO layer:
getCategoryProducts(): List[Product] = {
// query products that have categoryId defined
}
You see, that this method returns products, that are guaranteed to have categoryId defined with some value.
What I would like to do is something like this:
trait HasCategory {
def categoryId_!: Long
}
// and then specify in method signature
getCategoryProducts(): List[Product with HasCategory]
This will work, but then such a product will have two methods: categoryId_! and categoryId that smells bad.
Another way would be:
sealed trait Product {
def name: String
/*other fields*/
}
case class SimpleProduct(name: String, /*, other fields....*/) extends Product
case class ProductWithCategory(name: String, categoryId: Long/*, other fields....*/) extends Product
def getCategoryProducts: List[ProductWithCategory] = ...
This method helps to avoid duplicate methods categoryId and categoryId_!, but it requires you to create two case classes and a trait duplicating all the fields, which also smells.
My question: how can I use Scala type system to declare this specific case without these fields duplications ?
Not sure how much this will scale for your particular case, but one solution that comes to mind is to parameterize over the Option type using a higher-kinded generic type:
object Example {
import scala.language.higherKinds
type Id[A] = A
case class Product[C[_]](name: String, category: C[Long])
def productsWithoutCategories: List[Product[Option]] = ???
def productsWithCategories: List[Product[Id]] = ???
}
A way to do it is to use type classes -
import scala.language.implicitConversions
object Example {
sealed class CartId[T]
implicit object CartIdSomeWitness extends CartId[Some[Long]]
implicit object CartIdNoneWitness extends CartId[None.type]
implicit object CartIdPresentWitness extends CartId[Long]
case class Product[T: CartId](name: String, categoryId: T /*, other fields....*/)
val id: Long = 7
val withId = Product("dsds", id)
val withSomeId = Product("dsds", Some(id))
val withNoneId = Product("dsds", None)
val presentId: Long = withId.categoryId
val maybeId: Some[Long] = withSomeId.categoryId
val noneId: None.type = withNoneId.categoryId
val p = Product("sasa", true) //Error:(30, 18) could not find implicit value for evidence parameter of type com.novak.Program.CartId[Boolean]
}
This solution involves some code and dependent on implicits but does what you're trying to achieve.
Be aware that this solution is not completely sealed and can be 'hacked'. You can cheat and do something like -
val hack: Product[Boolean] = Product("a", true)(new CartId[Boolean])
val b: Boolean =hack.categoryId
For some more - advanced solutions which include
* Miles Sabin (#milessabin)’s Unboxed union types in Scala via the Curry-Howard isomorphism
* Scalaz / operator
http://eed3si9n.com/learning-scalaz/Coproducts.html
I'd like do perform something like that using Slick (I have updated to 3.0.0-M1):
class MyTable extends Table[(Int, Int)](tag, "MyTable) {
def a = column[Int]("a")
def b = column[Int]("b")
def * = (a, b)
def total: Int = a + b // That's THE thing
}
So that I can later perform:
val values = TableQuery[MyTable]
values.map(_.total)
Of course, I am stuck on the total method. The total method can be fairly complex (I have an application where it should compute the median of three counts), so I think it should be actual Scala code to be executed in the end.
How anything like this could be developed in Slick?
As long as you can express it using Slick, e.g.
def total/*: Column[Int]*/ = a + b
It will be run on the server side. Instead of placing it in the Table subclass, you can alternatively use an implicit class to patch on a method from the outside:
implicit class ExtendMyTable(t: MyTable){
def total/*: Column[Int]*/ = t.a + t.b
}
It just needs to be in scope where you try to call .total. Or if you really need client-side Scala coding, extend the result type instead, e.g.
implicit class ExtendMyTableResult(t: (Int,Int)){
def total/*: Int*/ = t._1 + t._2
}
And then do
TableQuery[MyTable].run.map(_.total)
I use case class to transform the class object to data for slick2 before, but current I use another play plugin, the plugin object use the case class, my class is inherent from this case class. So, I can not use case class as the scala language forbidden use case class to case class inherent.
before:
case class User()
class UserTable(tag: Tag) extends Table[User](tag, "User") {
...
def * = (...)<>(User.tupled,User.unapply)
}
it works.
But now I need to change above to below:
case class BasicProfile()
class User(...) extends BasicProfile(...){
...
def unapply(i:User):Tuple12[...]= Tuple12(...)
}
class UserTable(tag: Tag) extends Table[User](tag, "User") {
...
def * = (...)<>(User.tupled,User.unapply)
}
I do not know how to write the tupled and unapply(I am not my writing is correct or not) method like the case class template auto generated. Or you can should me other way to mapping the class to talbe by slick2.
Any one can give me an example of it?
First of all, this case class is a bad idea:
case class BasicProfile()
Case classes compare by their member values, this one doesn't have any. Also the name is not great, because we have the same name in Slick. May cause confusion.
Regarding your class
class User(...) extends BasicProfile(...){
...
def unapply(i:User):Tuple12[...]= Tuple12(...)
}
It is possible to emulate case classes yourself. Are you doing that because of the 22 field limit? FYI: Scala 2.11 supports larger case classes. We are doing what you are trying at Sport195, but there are several aspects to take care of.
apply and unapply need to be members of object User (the companion object of class User). .tupled is not a real method, but generated automatically by the Scala compiler. it turns a method like .apply that takes a list of arguments into a function that takes a single tuple of those arguments. As tuples are limited to 22 columns, so is .tupled. But you could of course auto-generated one yourself, may have to give it another name.
We are using the Slick code generator in combination with twirl template engine (uses # to insert expressions. The $ are inserted as if into the generated Scala code and evaluated, when the generated code is compiled/run.). Here are a few snippets that may help you:
Generate apply method
/** Factory for #{name} objects
#{indentN(2,entityColumns.map(c => "* #param "+c.name+" "+c.doc).mkString("\n"))}
*/
final def apply(
#{indentN(2,
entityColumns.map(c =>
colWithTypeAndDefault(c)
).mkString(",\n")
)}
) = new #{name}(#{columnsCSV})
Generate unapply method:
#{if(entityColumns.size <= 22)
s"""
/** Extractor for ${name} objects */
final def unapply(o: ${name}) = Some((${entityColumns.map(c => "o."+c.name).mkString(", ")}))
""".trim
else
""}
Trait that can be mixed into User to make it a Scala Product:
trait UserBase with Product{
// Product interface
def canEqual(that: Any): Boolean = that.isInstanceOf[#name]
def productArity: Int = #{entityColumns.size}
def productElement(n: Int): Any = Seq(#{columnsCSV})(n)
override def toString = #{name}+s"(${productIterator.toSeq.mkString(",")})"
...
case-class like .copy method
final def copy(
#{indentN(2,columnsCopy)}
): #{name} = #{name}(#{columnsCSV})
To use those classes with Slick you have several options. All are somewhat newer and not documented (well). The normal <> operator Slick goes via tuples, but that's not an option for > 22 columns. One option are the new fastpath converters. Another option is mapping via a Slick HList. No examples exist for either. Another option is going via a custom Shape, which is what we do. This will require you to define a custom shape for your User class and another class defined using Column types to mirror user within queries. Like this: http://slick.typesafe.com/doc/2.1.0/api/#scala.slick.lifted.ProductClassShape Too verbose to write by hand. We use the following template code for this:
/** class for holding the columns corresponding to #{name}
* used to identify this entity in a Slick query and map
*/
class #{name}Columns(
#{indent(
entityColumns
.map(c => s"val ${c.name}: Column[${c.exposedType}]")
.mkString(", ")
)}
) extends Product{
def canEqual(that: Any): Boolean = that.isInstanceOf[#name]
def productArity: Int = #{entityColumns.size}
def productElement(n: Int): Any = Seq(#{columnsCSV})(n)
}
/** shape for mapping #{name}Columns to #{name} */
object #{name}Implicits{
implicit object #{name}Shape extends ClassShape(
Seq(#{
entityColumns
.map(_.exposedType)
.map(t => s"implicitly[Shape[ShapeLevel.Flat, Column[$t], $t, Column[$t]]]")
.mkString(", ")
}),
vs => #{name}(#{
entityColumns
.map(_.exposedType)
.zipWithIndex
.map{ case (t,i) => s"vs($i).asInstanceOf[$t]" }
.mkString(", ")
}),
vs => new #{name}Columns(#{
entityColumns
.map(_.exposedType)
.zipWithIndex
.map{ case (t,i) => s"vs($i).asInstanceOf[Column[$t]]" }
.mkString(", ")
})
)
}
import #{name}Implicits.#{name}Shape
A few helpers we put into the Slick code generator:
val columnsCSV = entityColumns.map(_.name).mkString(", ")
val columnsCopy = entityColumns.map(c => colWithType(c)+" = "+c.name).mkString(", ")
val columnNames = entityColumns.map(_.name.toString)
def colWithType(c: Column) = s"${c.name}: ${c.exposedType}"
def colWithTypeAndDefault(c: Column) =
colWithType(c) + colDefault(c).map(" = "+_).getOrElse("")
def indentN(n:Int,code: String): String = code.split("\n").mkString("\n"+List.fill(n)(" ").mkString(""))
I know this may a bit troublesome to replicate, especially if you are new to Scala. I hope to to find the time get it into the official Slick code generator at some point.
(I'm a complete beginner with Scala and Slick, so code review of any kind is appreciated)
I have the following class and Slick Table defined:
case class Foo(title: String, description: String, id: Int = 0)
class FooTable(tag: Tag) extends Table[Foo](tag, "FOO") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def title = column[String]("TITLE", O.NotNull)
def description = column[String]("DESCRIPTION")
def * = (title, description, id) <> (Foo.tupled, Foo.unapply)
}
I want to add a method which will return a List of Foos which match a specified title. Something like this:
def findByTitle(title: String) = DB.withSession { implicit s: Session =>
<FooTable TableQuery>.filter(_.title === title)
}
I'd then be able to use the method like this:
val foos = TableQuery[FooTable]
DB.withSession { implicit s: Session =>
val anId = foos.findByTitle("bar")
}
How/where can I add a method which can act on a TableQuery for a particular Table? Is this even the correct way to be arranging my application?
implicit class FooTableExtensions(q: Query[FooTable,Foo]){
def findByTitle(t: String) = q.filter(_.title === t)
}
foos.findByTitle("Bar")
See Scala eXchange 2013 talk our website.
For pre-compiled queries it may be useful to have a DAO though, where you can cache the pre-compiled query. See Scala Days 2013 talk. Syntax changed since then though. Check the manual for Compiled.
I think what you want is to introduce a DAO (data access object), depending on your needs you could let the companion object of the FooTable class be the DAO which would let you call FooTable.findByTitle() from the rest of your codebase.