I'm attempting to dynamically include a sortBy to my query which sorts based on its string name from a query parameter. In Slick 3 this has proven to be quite tricky. Currently my setup is:
trait Model {
type ATable <: AbstractTable[_]
def tableQuery: TableQuery[ATable]
def sortMap: Map[String, Rep[_]]
private def sortKey[T](e: ATable, sort: (String, SortOrder)): ColumnOrdered[_] = sort match {
case (field, SortOrder.Asc) => ColumnOrdered(sortMap.getOrElse(field, throw new ClientException(s"Can't sort by $field")), Ordering(Ordering.Asc))
case (field, SortOrder.Desc) => ColumnOrdered(sortMap.getOrElse(field, throw new ClientException(s"Can't sort by $field")), Ordering(Ordering.Desc))
}
def all(sort: (String, SortOrder)) = tableQuery.sortBy(sortKey(_, sort)).result
}
object User extends Model {
type ATable = Tables.User
val tableQuery = Tables.User
val sortMap = Map( "id" -> tableQuery.id )
}
But running db.run(User.all(("id", SortOrder.Asc)) throws the following error:
slick.SlickException: No type for symbol name found in Vector[t2<#t3<UnassignedType>>]
Does anyone know of a better solution or where I'm going wrong?
The way I solved this was to change my sortMap to contain the table type with column type implicit (from Map[String, Rep[_]] to Map[String, ATable => Rep[_]]):
trait Model {
type ATable <: AbstractTable[_]
def tableQuery: TableQuery[ATable]
def sortMap: Map[String, ATable => Rep[_]]
private def sortKey(baseQ: Query[ATable, ATable#TableElementType, Seq], sort: (String, SortOrder)) = {
val rep = sortMap.getOrElse(sort._1, throw new ClientException(s"Can't sort by ${sort._1}"))
val orderedRep = sort._2 match {
case SortOrder.Asc => (t: ATable) => ColumnOrdered(rep(t), slick.ast.Ordering(slick.ast.Ordering.Asc))
case SortOrder.Desc => (t: ATable) => ColumnOrdered(rep(t), slick.ast.Ordering(slick.ast.Ordering.Desc))
}
q.sortBy(orderedRep)
}
def all(sort: (String, SortOrder)) = sortKey(tableQuery, sort).result
}
object User extends Model {
type ATable = Tables.User
val tableQuery = Tables.User
val sortMap = Map( "id" -> { (t: ATable) => t.id } )
}
The only suboptimal aspect of this solution is that if a new column is added to the database it has to be manually added to the sortKey. I'm looking into including these maps in the table code generation scripts.
You just need to include in generation something like
val columns = Map( "id" -> { (t: User) => t.id } )
and use it like so:
class UserDAO{
//...
baseQuery = filterContext.sort.foldLeft[Query[User, UserRow, Seq]](User) ((_, s) => UserColumnFilter.getSort(s,User.baseTableRow.columns));
//...
}
case class Sort(columnName:String,value: String) {}
case class FilterContext(filters:List[Filter],page:Int,pageSize:Int,sort:List[Sort])
object UserColumnSort extends ColumnSort[User,UserRow](User)
class ColumnSort[T<:Table[C],C](query:TableQuery[T])
{
def getSort(s:Sort,columns:Map[String, T => Rep[_]]):Query[T, C, Seq] = {
val aux = columns.getOrElse(s.columnName, throw new Exception(s"Can't sort by ${s.columnName}"));
val orderedRep = s.value match {
case "asc" => (t: T) => ColumnOrdered(aux(t), slick.ast.Ordering(slick.ast.Ordering.Asc))
case _ => (t: T) => ColumnOrdered(aux(t), slick.ast.Ordering(slick.ast.Ordering.Desc))
}
return query.sortBy(orderedRep);
}
}
Related
I wrote this
def computeMap(map:Map[String, DataFrame], f: (String) => String, g: (DataFrame) => DataFrame ) : Map[String, DataFrame] = {
map.map{ case (key, value) => (f(key), g(value) }
}
My problem here is that f and g function are provided by 2 implicit classes wrapped in objects ( one implicit class for string transform and the second one is for dataframe transorm)
I rather want to write:
def computeMap(map:Map[String, DataFrame], f: tobecompleted, g: tobecompleted ) : Map[String, DataFrame] = {
map.map{ case (key, value) => (key.f, value.g) }
}
f for example can be defined
object Test {
implicit class Transform(s:String) {
def colm():String = {
s + "ded"
}
}
Is there any solution for this please ?
i want to make function as parameter of class construction.
i have some functions that i want to calculate in class (some kind of lazy calculation)
class Foo(calc: PGSimpleDataSource => Connection => PreparedStatement => ResultSet => Option[A] ??? ) {
here some logic like:
def runCalc = calc ( resultSet ( preparedStatement ( connection )
resultSet.close()
preparedStatement.close()
connection.close()
send result somewhere ...
}
prepare some functions
implicit val dataSource: PGSimpleDataSource = Service.ds
implicit def getConnection(implicit ds: PGSimpleDataSource): Connection = ds.getConnection
implicit def getPreparedStatement(userId: UUID)(implicit con: Connection): PreparedStatement ={
val st = con.prepareStatement("SELECT * FROM bar WHERE id = CAST(? as UUID)")
st.setString(1, id.toString)
st
}
implicit def getResultSet(implicit ps: PreparedStatement): ResultSet = ps.executeQuery()
implicit def mapping(implicit rs: ResultSet): Option[Bar] = {
Iterator.continually(rs.next)
.takeWhile(identity)
.map(_ => Bar(UUID.fromString(rs.getString(1)))).toList.headOption
}
and then some function like:
def getInstance(id: UUID) = new Foo(???)
val foo = getInstance(id)
foo.runCalc()
Is it possible? Please Help
upd:
i tried to do my class like this :
class Foo[A](
con: => Connection,
ps: => PreparedStatement,
rs: => ResultSet,
mapping: => Option[A],
calc: Connection => PreparedStatement => ResultSet => Option[A]
) {
def run(): Unit = {
val result: Option[A] = calc(con)(ps)(rs)(mapping)
rs.close()
ps.close()
con.close()
}
}
but i don't understand how to write "f" function as written below
it need do mapping( getResultSet ( getPreparedStatement (id, getConnection))
in other words mapping need ResultSet that need PreparedStatement that need Id and Connection
You can do it. It will look like this
case class Foo(calc: String => String => String => Option[String]) {
def run = {
val p1 = "p1"
val p2 = "p2"
val p3 = "p3"
calc(p1)(p2)(p3)
}
}
Usage example
object App extends App {
val f = (x: String) => (y: String) => (z: String) => {
Option(x + y + z)
}
val foo = Foo(f)
println(foo.run)
}
Or you can use currying
object App extends App {
val f = (x: String, y: String, z: String) => {
Option(x + y + z)
}
val foo = Foo(f.curried)
println(foo.run)
}
EDIT
For you extended question I can propose this class:
case class Foo[A](
getConnection: () => Connection,
getStatement: Connection => PreparedStatement,
getResultSet: PreparedStatement => ResultSet,
mapping: ResultSet => Option[A]) {
def run(): Option[A] = {
val connection = getConnection()
try{
val statement = getStatement(connection)
try{
val resultSet = getResultSet(statement)
try{
mapping(resultSet)
}finally {
resultSet.close()
}
}finally{
statement.close()
}
} finally {
connection.close()
}
}
}
Usage example:
val foo = Foo(
() => new Connection(),
connection => new PreparedStatement(),
statement => new ResultSet(),
resultSet => Option("")
)
All those function have parameter from previous step so when for example you create ResultSet you can use statement (but you can't use connection).
I have three class's
class A (
param1:Int,
param2:Int
)
class B (
param3:Int,
param4:Int
)
class C(
param1:A,
param2:B
)
And i need map between name of field to object, like
param1->1
param2->2
param3->3
param4->4
So i tired to did it as below but field its not type of Product
def func1(c: C): Map[String, Object] = {
var map = Map[String, Object]()
for (field <- c.getClass.getDeclaredFields) yield {
map ++ getFieldMap(field))
}
}
def getFieldMap(p: Product): Map[String, Object] = {
var values = p.productIterator
p.getClass.getDeclaredFields.map(_.getName -> {
values.next.asInstanceOf[AnyRef]
}).toMap
}
The are multiple problems with your code. The class A and class B do not implement the Product trait. You either have to do with Product manually and implement all the methods, or you declare them as case classes.
case class A (
val param1: Int,
val param2: Int
)
case class B (
val param3: Int,
val param4: Int
)
To ensure that Scala actually generates fields for the constructor parameters, add the val keyword in front.
Furthermore, you are passing the field (java.lang.reflect.Field), not it's value, which you have to get first.
def func1(c: C): Map[String, Object] = {
var map = Map[String, Object]()
for (field <- c.getClass.getDeclaredFields) yield {
field.setAccessible(true) // <- to avoid IllegalAccessException
map ++ getFieldMap(field.get(c)) // <- inserted .get(c)
}
}
If you can make your classes case classes, you can do it using shapeless:
import shapeless._
import shapeless.record._
case class A (param1:Int, param2:Int)
val a = A(12, 34)
val AGen = LabelledGeneric[A]
val map = AGen.to(a).toMap.map { case (k, v) => (k.name, v) }
println(map("param1")) // prints "12"
Edit: I've fixed the problem - I was incorrectly calling .map(f => f.typeSignature.asInstanceOf[TypeRef].args.head) on recursiveOpt, which meant that field.name was giving me the wrong field name in my copy method. I've removed the map and everything is working now.
I am writing a macro that will create a map of update methods for a case class, e.g.
case class Inner(innerStr: String)
case class Outer(outerStr: String, inner: Inner, innerOpt: Option[Inner])
should produce an update map for Outer that is something like
val innerMap = Map("innerStr" -> {(json: JsValue) => Try{(inner: Inner) => inner.copy(innerStr = json)}})
val outerMap = Map("outerStr" -> {(json: JsValue) => Try{(outer: Outer) => outer.copy(outerStr = json)}},
"inner.innerStr" -> {(json: JsValue) => Try{(outer: Outer) => innerMap.get("innerStr").get(json).flatMap(update => outer.copy(inner = update(outer.inner)))},
"innerOpt.innerStr" -> {(json: JsValue) => Try{(outer: Outer) => innerMap.get("innerStr").get(json).flatMap(update => outer.copy(inner = outer.inner.map(inner => update(inner))))})
which would then be called like
val oldOuter = Outer("str", Inner("str"), Some(Inner("str")))
val updatedOuter = outerMap.get("inner.innerStr").get(JsString("newStr")).get(oldOuter)
The idea is that given a json kv pair, I can retrieve the appropriate update method from the map using the key and then apply the update using the value, using implicit conversions to convert from the json value to the appropriate type.
My macro is working for the case of a flat case class, e.g. Inner(innerStr: String), and for a nested case class, e.g. Outer(outerStr: String, inner: Inner). However, the case of the nested option case class, Outer(outerStr: String, innerOpt: Option[Inner]), is crashing the compiler. I'm not sure if I'm doing something disastrously wrong, or if there's a bug in the compiler, or third option. This was done using the Scala 2.11.0-M7 REPL
Below is my code - I'm constructing a Map that accepts String input instead of JsValue input so that I don't need to import the play framework into my REPL. The blacklist filters out fields that should not be in the update map (e.g. one of the case classes we're applying this to has fields like "crypted_password" and "salt" that should never be updated via json sent in through a REST route). baseMethods constructs the key -> method tuples for the flat case, recursiveMethods constructs the key-method tuples for the nested case, and recursiveOptMethods constructs the key-value tuples for the nested option case; at the bottom of the macro these are all merged into a flat sequence and a placed in a Map.
I've tested the code in the recursiveOptMethods quasiquotes to ensure that I'm constructing a properly typed sequence of tuples and haven't found an error (also, this code is extremely similar to the recursiveMethods quasiquotes, which are functioning correctly), and I've tested the code that constructs the base, recursive, and recursiveOpt sequences of symbols and they seem to be doing their job.
Any help as to why I'm crashing the compiler would be greatly appreciated.
import scala.language.experimental.macros
def copyTestImpl[T: c.WeakTypeTag](c: scala.reflect.macros.Context)(blacklist: c.Expr[String]*): c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] = {
import c.universe._
val blacklistList: Seq[String] = blacklist.map(e => c.eval(c.Expr[String](c.resetAllAttrs(e.tree))))
def isCaseClass(tpe: Type): Boolean = tpe.typeSymbol.isClass && tpe.typeSymbol.asClass.isCaseClass
def isCaseClassOpt(tpe: Type): Boolean = tpe.typeSymbol.name.decoded == "Option" && isCaseClass(tpe.asInstanceOf[TypeRef].args.head)
def rec(tpe: Type): c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] = {
val typeName = tpe.typeSymbol.name.decoded
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head.filterNot(field => blacklistList.contains(typeName + "." + field.name.decoded))
val recursive = fields.filter(f => isCaseClass(f.typeSignature))
val recursiveOpt = fields.filter(f => isCaseClassOpt(f.typeSignature))
val base = fields.filterNot(f => isCaseClass(f.typeSignature) || isCaseClassOpt(f.typeSignature))
val recursiveMethods = recursive.map {
field => {
val fieldName = field.name
val fieldNameDecoded = fieldName.decoded
val map = rec(field.typeSignature)
q"""{
val innerMap = $map
innerMap.toSeq.map(tuple => ($fieldNameDecoded + "." + tuple._1) -> {
(str: String) => {
val innerUpdate = tuple._2(str)
innerUpdate.map(innerUpdate => (outer: $tpe) => outer.copy($fieldName = innerUpdate(outer.$fieldName)))
}
})}"""
}}
val recursiveOptMethods = recursiveOpt.map {
field => {
val fieldName = field.name
val fieldNameDecoded = fieldName.decoded
val map = rec(field.typeSignature.asInstanceOf[TypeRef].args.head)
q"""{
val innerMap = $map
innerMap.toSeq.map(tuple => ($fieldNameDecoded + "." + tuple._1) -> {
(str: String) => {
val innerUpdate = tuple._2(str)
innerUpdate.map(innerUpdate => (outer: $tpe) => outer.copy($fieldName = (outer.$fieldName).map(inner => innerUpdate(inner))))
}
})}"""
}}
val baseMethods = base.map {
field => {
val fieldName = field.name
val fieldNameDecoded = fieldName.decoded
val fieldType = field.typeSignature
val fieldTypeName = fieldType.toString
q"""{
$fieldNameDecoded -> {
(str: String) => scala.util.Try {
val x: $fieldType = str
(t: $tpe) => t.copy($fieldName = x)
}.recoverWith {
case e: Exception => scala.util.Failure(new IllegalArgumentException("Failed to parse " + str + " as " + $typeName + "." + $fieldNameDecoded + ": " + $fieldTypeName))
}
}}"""
}}
c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] {
q"""{ Map((List(..$recursiveMethods).flatten ++ List(..$recursiveOptMethods).flatten ++ List(..$baseMethods)):_*) }"""
}
}
rec(weakTypeOf[T])
}
def copyTest[T](blacklist: String*) = macro copyTestImpl[T]
And the top and bottom of my error from the 2.11.0-M7 REPL when calling copyTest[Outer]() (where Outer has an Option[Inner] field)
scala> copyTest[Outer]()
scala.MatchError: AnyRef
with Product
with Serializable {
val innerStr: String
private[this] val innerStr: String
def <init>(innerStr: String): Inner
def copy(innerStr: String): Inner
def copy$default$1: String #scala.annotation.unchecked.uncheckedVariance
override def productPrefix: String
def productArity: Int
def productElement(x$1: Int): Any
override def productIterator: Iterator[Any]
def canEqual(x$1: Any): Boolean
override def hashCode(): Int
override def toString(): String
override def equals(x$1: Any): Boolean
} (of class scala.reflect.internal.Types$ClassInfoType)
at scala.reflect.internal.Variances$class.inType$1(Variances.scala:181)
at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
at scala.reflect.internal.util.Collections$class.map2(Collections.scala:55)
at scala.reflect.internal.SymbolTable.map2(SymbolTable.scala:14)
at scala.reflect.internal.Variances$class.inArgs$1(Variances.scala:176)
at scala.reflect.internal.Variances$class.inType$1(Variances.scala:189)
at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
at scala.reflect.internal.util.Collections$class.map2(Collections.scala:55)
at scala.reflect.internal.SymbolTable.map2(SymbolTable.scala:14)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:93)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1603)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1588)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1583)
at scala.tools.nsc.interpreter.IMain.compileSourcesKeepingRun(IMain.scala:387)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compileAndSaveRun(IMain.scala:816)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compile(IMain.scala:775)
at scala.tools.nsc.interpreter.IMain$Request.compile$lzycompute(IMain.scala:951)
at scala.tools.nsc.interpreter.IMain$Request.compile(IMain.scala:946)
at scala.tools.nsc.interpreter.IMain.compile(IMain.scala:530)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:518)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:516)
at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:748)
at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:660)
at scala.tools.nsc.interpreter.ILoop.processLine(ILoop.scala:427)
at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:444)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:862)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:848)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:848)
at scala.reflect.internal.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:95)
at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:848)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:81)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:94)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
That entry seems to have slain the compiler. Shall I replay
your session? I can re-run each line except the last one.
I found the problem - originally I had val recursiveOpt = fields.filter(f => isCaseClassOpt(f.typeSignature)).map(f => f.typeSignature.asInstanceOf[TypeRef].args.head), which meant that when I called field.name on the recursiveOpt fields I was getting the wrong name back.
I cannot find the way to define a MongoRecord with a Map[String,String] field inside it in Lift - MongoRecord.
The Lift documentation says:
All standard Record Fields are supported. There is also support for Mongo specific types; ObjectId, UUID, Pattern, List, and Map.
How can I define Map and List fields?
I defined a BsonRecordMapField:
class BsonRecordMapField[OwnerType <: BsonRecord[OwnerType], SubRecordType <: BsonRecord[SubRecordType]]
(rec: OwnerType, valueMeta: BsonMetaRecord[SubRecordType])(implicit mf: Manifest[SubRecordType])
extends MongoMapField[OwnerType, SubRecordType](rec: OwnerType) {
import scala.collection.JavaConversions._
override def asDBObject: DBObject = {
val javaMap = new HashMap[String, DBObject]()
for ((key, element) <- value) {
javaMap.put(key.asInstanceOf[String], element.asDBObject)
}
val dbl = new BasicDBObject(javaMap)
dbl
}
override def setFromDBObject(dbo: DBObject): Box[Map[String, SubRecordType]] = {
val mapResult: Map[String, SubRecordType] = (for ((key, dboEl) <- dbo.toMap.toSeq) yield (key.asInstanceOf[String], valueMeta.fromDBObject(dboEl.asInstanceOf[DBObject]))).toMap
setBox(Full(mapResult))
}
override def asJValue = {
val fieldList = (for ((key, elem) <- value) yield JField(key, elem.asJValue)).toList
JObject(fieldList)
}
override def setFromJValue(jvalue: JValue) = jvalue match {
case JNothing | JNull if optional_? => setBox(Empty)
case JObject(fieldList) => val retrievedMap = fieldList.map {
field =>
val key = field.name
val valRetrieved = valueMeta.fromJValue(field.value) openOr valueMeta.createRecord
(key, valRetrieved)
}.toMap
setBox(Full(retrievedMap))
case other => setBox(FieldHelpers.expectedA("JObject", other))
}
}
This is the implicit query for Rogue:
class BsonRecordMapQueryField[M <: BsonRecord[M], B <: BsonRecord[B]](val field: BsonRecordMapField[M, B])(implicit mf: Manifest[B]) {
def at(key: String): BsonRecordField[M, B] = {
val listBox = field.setFromJValue(JObject(List(JField("notExisting", JInt(0)))))
val rec = listBox.open_!.head._2
new BsonRecordField[M, B](field.owner, rec.meta)(mf) {
override def name = field.name + "." + key
}
}
}
object ExtendedRogue extends Rogue {
implicit def bsonRecordMapFieldToBsonRecordMapQueryField[M <: BsonRecord[M], B <: BsonRecord[B]](f: BsonRecordMapField[M, B])(implicit mf: Manifest[B]): BsonRecordMapQueryField[M, B] = new BsonRecordMapQueryField[M, B](f) (mf)
}
You can use the at operator in map now.
What about MongoMapField?