Slick DBIO sequence failing to compile - scala

I am trying to save model ProductCategory object in database. While saving it,categoriesId is a Seq.
case class ProductCategory(productItemId: ProductItemId, categoryies: CategoryId, filterName: FilterName)
/*Inside another object starts*/
def saveCategoriesId(productItemId: ProductItemId, categoryId: Seq[CategoryId], filterName: FilterName):
Future[Seq[ProductItemId]] =
db.run({
DBIO.sequence(categoryId.map(id => save(ProductCategory(productItemId, id, filterName))))
})
def save(productCategory: ProductCategory): DBIO[ProductItemId] =
query returning query.map(_.productItemId) += productCategory
Getting following error:
[error] /Users/vish/Work/jd/app/service/ProductCategoryService.scala:20:35: type mismatch;
[error] found : Seq[slick.dbio.DBIOAction[models.ProductItemId,slick.dbio.NoStream,Nothing]]
[error] required: Seq[slick.dbio.DBIOAction[models.ProductItemId,slick.dbio.NoStream,E]]
[error] DBIO.sequence(categoryId.map(id => save(ProductCategory(productItemId, id, filterName))))
Playframework version is 2.6. This question is not duplicate of this.This issue has blocked the further development. While answering please comment if it correct way of saving categoriesId

Normally in Scala compile error found: Nothing, required: E means that compiler couldn't infer some types. Try to specify some type parameters manually
db.run({
DBIO.sequence[ProductItemId, Seq, All](categoryId.map(id => save(ProductCategory(productItemId, id, filterName))))
})
or
db.run({
DBIO.sequence(categoryId.map[DBIO[ProductItemId], Seq[DBIO[ProductItemId]]](id => save(ProductCategory(productItemId, id, filterName))))
})
or introduce a local variable (then compiler will be able to infer types itself)
val actions = categoryId.map(id => save(ProductCategory(productItemId, id, filterName)))
db.run({
DBIO.sequence(actions)
})

Related

Slick: comparing Rep[Option[Blob]] with Rep[Int]

I am using Slick to analyze a legacy MySQL database (with MyISAM engine). And I’m using implicit classes to navigate through entities, e.g. user.logs with this code:
implicit class UserNav(user: User) {
def logs = Logs.filter(_.userId === user.id)
}
However, in this case the key is an INT but the foreign key is a BLOB. Using a MySQL client I can run select userId * 1 from logs to get an INT from the BLOB, and I can even join despite the different datatypes. But with Slick I get a compile errors for the code above.
error: Cannot perform option-mapped operation [ERROR]
with type: (Option[java.sql.Blob], Int) => R [ERROR]
for base type:(java.sql.Blob, java.sql.Blob) => Boolean
error: ambiguous implicit values:
both value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => slick.lifted.CanBeQueryCondition[slick.lifted.Rep[Option[Boolean]]]
and value BooleanCanBeQueryCondition in object CanBeQueryCondition of type => slick.lifted.CanBeQueryCondition[Boolean]
match expected type slick.lifted.CanBeQueryCondition[Nothing]
Any idea how to solve this?
As I found out by trying, just pretending the column has a different data type works.
So besides the original val in class Log (which extends Table[LogRow])
val userId: Rep[Option[java.sql.Blob]] = column[Option[java.sql.Blob]]("userId", O.Default(None))
I added
val userId_asInt: Rep[Option[Int]] = column[Option[Int]]("userId", O.Default(None))
and changed the navigation def to
def logs = Logs.filter(_.userId_asInt === user.id)
This did the trick – at least with MySQL/MyISAM (no idea if this works with other engines or even other DBMS).
Instead I could also have replaced the data type in original val userId, but then I would also have to change that data type all over the place like in Log.* and Log.? ...

How can I use case classes with slicks TSQL interpolator?

The sql interporlator allows case classes as a result given a GetResult instance
val action:DBIO[Seq[VisibilityRow]] = sql"SELECT * FROM visibility".as[VisibilityRow] //compiles fine
Checking the docs, I don't see an example of utilizing tsql along with a case class.
I decided to just try it out
val action2:DBIO[Seq[VisibilityRow]] = tsql"SELECT * FROM visibility"
However I am getting errors
[error] ... : type mismatch;
[error] found : slick.profile.SqlStreamingAction[Vector[(Long, String, java.sql.Timestamp, java.sql.Timestamp, java.sql.Timestamp)],(Long, String, java.sql.Timestamp, java.sql.Timestamp, java.sql.Timestamp),slick.dbio.Effect]
[error] required: slick.driver.PostgresDriver.api.DBIO[Seq[com.fevo.slick.Schema.VisibilityRow]]
[error] (which expands to) slick.dbio.DBIOAction[Seq[com.fevo.slick.Schema.VisibilityRow],slick.dbio.NoStream,slick.dbio.Effect.All]
[error] val action2:DBIO[Seq[VisibilityRow]] = tsql"SELECT * FROM visibility"
Is there an elegant way for me to extract these types using tsql, I could map over the apply but then I still need to specify (and update) the tuple type for DBIO's parameter accordingly.
If the case class constructor matches your fields, you can simply map over the result to get the type you need:
val action2:DBIO[Seq[VisibilityRow]] =
tsql"SELECT * FROM visibility".map(_.map(VisibilityRow.apply.tupled))

Type mismatch in scala quasiquote of macro definition: "type mismatch; found : field.NameType required: c.universe.TermName"

I asked a longer question, but it seems it's too much code for people to sort through so I've created this question to focus on one smaller, specific problem I'm facing regarding use of macros in Scala.
Consider the following code snippet:
val tpe = weakTypeOf[T]
val companion = tpe.typeSymbol.companionSymbol
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head
val toMapParams = fields.map { field =>
val name = field.name
val decoded = name.decoded
q"$decoded -> t.$name"
}
Note that fields is just the list of parameters for the primary constructor of a case class in this code. Where I'm confused is the result of the quasiquote q"$decoded -> t.$name". What does this mean exactly? And what type should it have? I'm getting a compile error stating the following:
Multiple markers at this line
- Implicit conversions found: q"$decoded -> t.$name" => Quasiquote(q"$decoded -> t.
$name")
- type mismatch; found : field.NameType required: c.universe.TermName
- type mismatch; found : field.NameType required: c.universe.TermName
Can anyone explain this error? Thanks.
The type of fields is List[Symbol], which means that the type of names of those fields is inconclusive (unknown whether it's a TermName or TypeName). This means that you can't insert such names essentially anywhere in a quasiquote.
A simple fix would be to do val name = field.name.toTermName, explicitly telling the compiler that it's looking at a term name, so that quasiquote knows how to process it.

slick 2.0 define generic `find by field` method

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.

how do I express a Vaadin BeanItemContainer construtor in Scala?

I'm trying to load a bunch of com.mongodb.DBObject objects in to a Vaadin BeanItemContainer to display in a table. I'm getting stuck on the translation of the constructor from Java to Scala.
The constructor definition is:
BeanItemContainer(Class<? extends BT> type)
This passes the scala compiler:
val bic = new BeanItemContainer(Class.forName("com.mongodb.DBObject"))
However, when I try to add an item:
mtl.toArray.foreach {t => bic.addBean(t)}
I get the following error:
[ERROR]com/sentientswarm/traderdashboard/UploadTradesWindow.scala:140: error: type mismatch;
found : t.type (with underlying type com.mongodb.DBObject)
required: ?0 where type ?0
mtl.toArray.foreach {t => bic.addBean(t)}
Any thoughts/suggestions?
UPDATE:
Tried:
val bic: BeanItemContainer[DBObject] = new BeanItemContainer(Class.forName("com.mongodb.DBObject"))
Result:
[ERROR]com/sentientswarm/traderdashboard/UploadTradesWindow.scala:140: error: type mismatch;
found : java.lang.Class[?0(in value bic)] where type ?0(in value bic)
required: java.lang.Class[_ <: com.mongodb.DBObject]
val bic: BeanItemContainer[DBObject] = new BeanItemContainer(Class.forName("com.mongodb.DBObject"))
^
Thanks,
John
Any reason you're using Class.forName? I don't think the compiler can infer the type from the returned object from that call, it would just be Class[_]. If you use classOf, it should let the compiler determine the type:
val bic = new BeanItemContainer[DBObject](classOf[DBObject]))
In other words: DBObject.class in Java translates to classOf[DBObject] in Scala.
Try this:
val bic: BeanItemContainer[BT] = new BeanItemContainer(Class.forName("com.mongodb.DBObject"))
By the way, you removed the "^" marker of where in the line the error is. Please, keep it when pasting error messages.