Squeryl Many to Many: compositeKey(field1, field2) doesn't exist - scala

I'm learning Scala (2.11) and the Play Framework, and i'm trying to implement a Many to Many relation between Person and Skill with Squeryl (0.9.5-7). Multiple Persons can share Skills, and a Person can have many Skills.
The Squeryl docs tell me to do this:
class PersonToSkill(val personId: Long, val skillId: Long) extends KeyedEntity[CompositeKey2[Long,Long]] {
def id = compositeKey(personId, skillId)
}
But the compiler tells me this:
not found: value compositeKey
[error] def id = compositeKey(personId, skillId)
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
Also, i cannot find compositeKey(fieldId, fieldId) in the docs. I have no idea where it it originates from..
I'm hoping someone can help me solve my problem, or at least explain me where to look for a solution. Thanks!

You can find compositeKey located within org.squery.dsl.QueryDsl
You'll want to make sure you import org.squeryl.PrimitiveTypeMode._ which extends QueryDsl. That should resolve the error you are receiving.

Related

Slick generic query with distinctOn

I would want to make a generic slick query using distinctOn on table to count distinct elements in the column.
def countDistinct(table: TableQuery[_], column: Rep[_]): DBIO[Int] =
table.distinctOn(_ => column).length.result
This code above doesn't compile because:
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[_]
[error] Unpacked type: T
[error] Packed type: Any
[error] table.distinctOn(_ => column).length.result
FlatShapeLevel instead of Rep[_] also doesn't work. I'm using slick 3.
distinctOn doesn't work properly in Slick due to incorrect projection to a particular field/column. The bug was raised 5 years ago, surprisingly still hasn't been resolved.

Grouping in Slick

I am trying to get a group by result in from the slick table .
Sql : Select * from Jobs GROUP BY category ;
Class
case class JobEntity(id:Option[Long],category:String,properties:String)
My slick function
def getJobsByCategory() :(String,Future[Seq[JobsEntity]]) =
db.run(jobs.groupBy(_.category).map{ case(category,res) =>
(category,res)}.result)
Error:
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[String], slick.sql.FixedSqlStreamingAction[Seq[org.exadatum.xstream.persistence.models.SparkConfigEntity],org.exadatum.xstream.persistence.models.SparkConfigEntity,slick.dbio.Effect.Read])
[ERROR] Unpacked type: T
[ERROR] Packed type: G
[ERROR]
There is probably some issue with the return type but am not sure what
as the IDE generates error as
Expression of type Future[Seq[Nothing]] doesn't conform to expected type (String,Future[Seq[JobsEntity]])
Sql : Select * from Jobs GROUP BY category ;
This query would only work (even in SQL) if your table only consists of the category field.
With a group by statement every field in the select statement which is not in the group by statement (in your case everything (*) aside from category), needs to be aggregated in some way, since standard SQL only supports flat result tables.
Same stands for Slick. In the map call following the groupBy call, you'll have to define aggregation functions for everything aside your category. Otherwise Slick does not know how to map the result (as stated in the exception)
case class JobEntity(id:Option[Long],category:String,properties:String)
db.run(jobs.groupBy(_.category).map{ case(category,res) =>(category,res)}.result)
Does not work as it is.
Something like:
db.run(jobs.groupBy(_.category).map{ case(category,res) =>(category,res.map(_.1).sum)}.result)
Would work, since it results in a flat shape: Your category and the sum of the IDs with that category. I know this does not make sense for you as is, but hopefully illustrates the problem.
If really just want to group your jobs by the category, I would do it in Scala after fetching them from the database:
val groupedJobs: Future[Seq[String, JobEntity]] = db.run(jobs).map {
case jobs => jobs.groupBy(_.category)
}
If you tell me what you want to achieve exactly, I can propose another solution for you.

Cannot make slick 3.2 Mapped table example working

I'm just trying to make "User" example working (http://slick.lightbend.com/doc/3.2.0/schemas.html#mapped-tables), but it doesn't compile.
As I'm targeting MySQL, I added th folowing imports :
import slick.jdbc.MySQLProfile.Table
import slick.jdbc._
import slick.lifted._
That didn't compiled either, I got a lot of errors like
Error:(16, 23) could not find implicit value for parameter tt: slick.ast.TypedType[Int]
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
After looking for implicits, I added with MySQLProfile.ImplicitColumnTypes to the Users class extending Table:
class Users(tag: Tag) extends Table[User](tag, "users") with MySQLProfile.ImplicitColumnTypes
Now I'm stuck with
Error:(19, 15) value ? is not a member of slick.lifted.Rep[Int]
def * = (id.?, first, last) <> (User.tupled, User.unapply _)
<> is not found either.
You may notice User.unapply _ instead of User.unapply as stated in doc; but the compiler was complaining whith User.unapply
What I'm I doing wrong ? Why is the doc so unclear ?
The code imports slick.jdbc.MySQLProfile.Table but instead it needs to bring in the whole api:
import slick.jdbc.MySQLProfile.api._
That will give you the implicts you were looking for, and the code should compile.
BTW: the Slick examples are compiled
Incidently, the Slick manual examples are compiled. This means you can get to the code to see if there are extra details in there you need.
For example, for the page you linked to, if you scroll to the top there's an "Edit this page on github" link. Clicking that takes you to the source and in there you'll find a reference to the Scala source:
.. includecode:: code/LiftedEmbedding.scala#mappedtable
...and that file is also in GitHub: LiftedEmbedding.scala
A bit long-winded, but useful sometimes to know the examples are compiled and you can find them.
The details of how that happens are just about to change to a different system, but the principles should remain the same. The details (filenames, import syntax) above will be different.

Problems with stubbing spy'ed object in scala

I have a pretty complex test where I decided to go with approach of partial stubbing of tested class. in my test I have something like this:
val srv = new Service()
val srvSpy = spy(srv)
doReturn(now).when(srvSpy).getRunDateInTimeZone(futureChecklist)
doReturn("boo").when(srvSpy).interpolateChecklistName("name", now)
val returnTuple = (createdChlRev, createdChl)
doReturn(returnTuple).when(srvSpy).create(fChlCreatorId,
fChlCreatorEmail,
"username",
true,
templateId,
"boo",
optDueDate)(connection)
val (chlRev, chl) = srv.createFromFutureChecklist(futureChecklist)(connection)
In the code above, the first two stubbed methods working as expected. However, the last one throws an error:
[error] Tuple2 cannot be returned by create$default$8() [error]
create$default$8() should return JsObject [error] *** [error] If
you're unsure why you're getting above error read on. [error] Due
to the nature of the syntax above problem might occur because: [error]
1. This exception might occur in wrongly written multi-threaded tests. [error] Please refer to Mockito FAQ on limitations of
concurrency testing. [error] 2. A spy is stubbed using
when(spy.foo()).then() syntax. It is safer to stub spies - [error]
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
I've trying bunch of different approached of stubbing and still getting the same error. At this point I have no idea even where to look.
What am I doing wrong?
Any advice would be helpful.
Thanks
Sorry, it was my own dumb mistake. I was stubbing the spied class, but calling the original one :(
Thanks,

Adding Strings to an Array out of a List in a Scala template

I have a List of Objects and want to iterate every Element in this List to get their Id Strings. Those Strings must be saved into another List. I always get this Compiler Error:
[error] FilePath:line:64: illegal start of simple expression
[error] #var formNameList : Array[String] = new Array[String](formList.size())
[error] ^
[error] File Path:69: ')' expected but '}' found.
[error] }
[error] ^
[error] two errors found
[error] (compile:compile) Compilation failed
[error] Total time: 3 s, completed 05.12.2013 14:03:37
So please guys help, before I drive insane.
My Code:
#var formNameList : Array[String] = new Array[String](formList.size())
#for(i <- 0 until formList.size()) {
#formNameList.add(formList.get(i).getFormId())
}
#views.html.formmanager.showresults(formNameList, formManager)
Im a Newbie in Scala and this is a very simple Task in Java but Scala is such a tough language. Its also very hard to read: What does this .:::, ::: or this <++= mean?
Short answer:
#views.html.formmanager.showresults(formList.map(_.getFormId).toArray, formManager)
Long answer:
Scala templates are templates - they should be used to generate some presentation of data and not be used as placeholders for a general code. I would strongly advice against doing any mutable or complex computations inside of templates. If you have complex code you should either pass it as a parameter or create a helper object like this:
# in helper.scala:
object Helper {
def toArrayOfIds(formList:List[Form]) = formList.map(_.getFormId).toArray
}
# in view.scala.html:
#Helper.toArrayOfIds(formList)
Another thing - prefer List to an Array. Usually I never use Array in my scala programms. Also notice the use of higher order function map instead of creating array an manually populating it. This is highly recommended. Just see how short the first example is.
.:::, ::: <++= could mean different things in different contexts. Usually first two operators mean the same thing which is concatenation of two lists. You can read about this in the "Programming in Scala" by Martin Odersky, first edition is available for free.
And if you need to introduce new variable in the template you can do it like this:
#defining(user.firstName + " " + user.lastName) { fullName =>
<div>Hello #fullName</div>
}
see play documentation