Scala Play: Implement custom QueryStringBindable with an optional field - scala

I'm using Scala with Play framework (2.8.1) and have a Sort class for capturing sort based query string parameters. Sample url: http://myurl:9000?sortBy=name&sortOrder=asc. Here, the sortOrder field is optional and will have default value of "asc" if nothing is provided. I have implemented own QueryStringBindable class as below:
object Sort {
val asc = "asc"
implicit def queryStringBinder(implicit stringBinder: QueryStringBindable[String]) = new QueryStringBindable[Sort] {
override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Sort]] = {
for {
sortBy <- stringBinder.bind("sortBy", params)
if(params.contains("sortOrder")) {sortOrder <- stringBinder.bind("sortOrder", params)}
} yield {
(sortBy, sortOrder) match {
case (Right(sortBy), Right(sortOrder)) => Right(Sort(sortBy, Some(sortOrder)))
case _ => Left("Unable to bind Sort")
}
}
}
override def unbind(key: String, sort: Sort): String = {
stringBinder.unbind("sortBy", sort.sortBy) + "&" + stringBinder.unbind("sortOrder", sort.sortOrder.getOrElse(asc))
}
}
}
case class Sort(sortBy: String, sortOrder: Option[String] = Some(Sort.asc))
However, I'm unable to capture optional field sortOrder with default value if nothing is provided in the query string of url. I would want http://myurl:9000?sortBy=name to still sort by ascending order (default) even if &sortOrder isn't provided.

It might be easier not to use a for comprehension:
object Sort {
val asc = "asc"
implicit def queryStringBinder(implicit stringBinder: QueryStringBindable[String]) = new QueryStringBindable[Sort] {
override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Sort]] = {
val sortBy = stringBinder.bind("sortBy", params) // Option[Either[String, String]]
val sortOrder = stringBinder.bind("sortOrder", params) // Option[Either[String, String]]
val result = // Either[String, Sort]
(sortBy, sortOrder) match {
case (Some(Right(field)), Some(Right(order))) => Right(Sort(field, Some(order)))
case (Some(Right(field)), None) => Right(Sort(field))
case _ => Left("Unable to bind Sort")
}
Option(result)
}
override def unbind(key: String, sort: Sort): String = {
stringBinder.unbind("sortBy", sort.sortBy) + "&" + stringBinder.unbind("sortOrder", sort.sortOrder.getOrElse(asc))
}
}
}
case class Sort(sortBy: String, sortOrder: Option[String] = Some(Sort.asc))

Related

For Comprehension Return type

I am trying to return Future[Vector[String]] from for comprehension but I am getting Future[Nothing] as my return type. How to convert Future[Nothing] return type to Future[Vector[String]]?
Here is the code snippet:
def findProjectId(projectName: String, userId: String): Future[Nothing] = {
for {
projectIds <- dBService.fetchProjectIdByProjectName(projectName) // projectIds is Vector[String]
pid <- projectIds
projectId <- dBService.filterProjectId(pid, userId) // ProjectId is Vector[String]
if projectId.nonEmpty
} yield {
projectId map {
projectid =>
dBService.fetchRoleId map { // fetchRoleId returns Future[Vector[String]]
case Vector(rid) => dBService.fetchCollaborator(projectid, rid) map { // fetchCollaborator returns Future[Vector[String]]
listOfcollabs =>
if (listOfcollabs.nonEmpty) {
listOfcollabs ++ Vector(userId)
}
else {
Vector(userId)
}
}
}
}
}
}
Signatures of dbService methods are:
def checkCollaboratorOrAdmin(userId: String, projectId: String, roleId: String): Future[Vector[String]] = {
dbComponent.db.run(checkAdminOrCollab(roleId))
}
def fetchRoleId: Future[Vector[String]] = {
dbComponent.db.run(fetchRole)
}
def fetchCollaborator(roleId: String, projectId: String): Future[Vector[String]] = {
dbComponent.db.run(fetchCollaborators(roleId, projectId))
}
def fetchProjectIdByProjectName(projectName: String) = {
dbComponent.db.run(fetchProjectId(projectName))
}
def filterProjectId(projectId: String, userId: String) = {
dbComponent.db.run(filterProjectIdByUserId(projectId, userId))
}
These methods in turn call:
def fetchRoleId(userId: String, projectId: String): SqlStreamingAction[Vector[String], String, Effect] = {
sql"""select distinct(role_id) from project_user_role where user_id=$userId and project_id=$projectId""".as[String]
}
def checkAdminOrCollab(roleId: String): SqlStreamingAction[Vector[String], String, Effect] = {
sql"""select role_id from roles where role_id=$roleId and role_name="collaborator" """.as[String]
}
def fetchRole(): SqlStreamingAction[Vector[String], String, Effect] = {
sql"""select role_id from roles where role_name="collaborator"""".as[String]
}
def fetchCollaborators(roleId: String, projectId: String): SqlStreamingAction[Vector[String], String, Effect] = {
sql"""select user_id from project_user_role where roleId=$roleId and project_id=$projectId""".as[String]
}
def fetchProjectId(projectName: String): SqlStreamingAction[Vector[String], String, Effect] = {
sql"""select project_id from projects where project_name=$projectName""".as[String]
}
def filterProjectIdByUserId(projectId: String, userId: String): SqlStreamingAction[Vector[String], String, Effect] = {
sql"""select project_id from project_user_role where project_id=$projectId and user_id=$userId""".as[String]
}
I'm guessing that the Future[Nothing] comes from IntelliJ hints, rather than the compiler itself. The compiler gives a couple of errors, the first coming from this line:
pid <- projectIds
This is the error I get:
Test.scala:47:13: type mismatch;
[error] found : scala.collection.immutable.Vector[Int]
[error] required: scala.concurrent.Future[?]
The problem is that the for expression is trying to build a value of type Future[_] using using map, flatMap and filter calls. (The collection type of a for is the collection type of the first expression in the for). flatMap on Future takes a Future and turns Future[Future[_]] into Future[_]. However you are giving it a Vector which is not supported.
I'm also unsure of the broader logic because you have two nested Vectors (projectIds and listOfCollabs) but no mechanism for flattening this into a single vector.
You probably want to look at using Future.traverse or Future.sequence to turn lists of Future into Future[List].
It would also make sense to break this down into some named functions to make the code more comprehensible and give a better chance of isolating the problem.
Update
This code will call the appropriate functions and return the results. The return type is Future[Vector[Vector[Vector[String]]]] because each dBService call returns Future[Vector[String]] so you get nested Vectors. It is not clear from the question how to flatten this into the result you want, but it should be straightforward. (The result of the last call is flattened by the case statement which is why there 4 dBService calls but only 3 nested Vectors )
def findProjectId(projectName: String, userId: String): Future[Vector[Vector[Vector[String]]]] = {
dBService.fetchProjectIdByProjectName(projectName).flatMap { projectIds =>
Future.traverse(projectIds) { pid =>
dBService.filterProjectId(pid, userId).flatMap { projectId =>
Future.traverse(projectId) { projectid =>
dBService.fetchRoleId.flatMap { // fetchRoleId returns Future[Vector[String]]
case Vector(rid) =>
dBService.fetchCollaborator(projectid, rid) map { // fetchCollaborator returns Future[Vector[String]]
_ ++ Vector(userId)
}
}
}
}
}
}
}
It may be that you can just take the first element of some of these Vectors which would simplify the code.
Future.traverse(collection)(f) is equivalent to Future.sequence(collection.map(f)) but gives better layout and may be more efficient.

Slick 3.0.0 - update row with only non-null values

Having a table with the columns
class Data(tag: Tag) extends Table[DataRow](tag, "data") {
def id = column[Int]("id", O.PrimaryKey)
def name = column[String]("name")
def state = column[State]("state")
def price = column[Int]("price")
def * = (id.?, name, state, price) <> ((DataRow.apply _).tupled, DataRow.unapply)
}
I'd like to write a function that would select a single row, and update the columns where the supplied values are not null.
def update(id: Int, name: Option[String], state: Option[State], price: Option[Int])
eg.
update(1, None, None, Some(5)) would update only the price of the data row 1, leaving the name and state intact
update(1, Some("foo"), None, Some(6)) would update the name and price, but leave its state intact.
I guess some smart mapping could be used, but I'm having a hard time expressing it, not sure how it could spit out different length tuples depending on the inputs (wether their value is defined), since they are more or less "unrelated" classes.
def update(id: Int, name: Option[String], state: Option[State], price: Option[Int]) = {
table.fiter(_.id == id). ???? .update(name, state, price)
}
I solved it in the following way.
The implementation below works only if it is a Product object.
Execute the update statement except for None for the Option type and null for the object type.
package slick.extensions
import slick.ast._
import slick.dbio.{ Effect, NoStream }
import slick.driver.JdbcDriver
import slick.jdbc._
import slick.lifted._
import slick.relational.{ CompiledMapping, ProductResultConverter, ResultConverter, TypeMappingResultConverter }
import slick.util.{ ProductWrapper, SQLBuilder }
import scala.language.{ existentials, higherKinds, implicitConversions }
trait PatchActionExtensionMethodsSupport { driver: JdbcDriver =>
trait PatchActionImplicits {
implicit def queryPatchActionExtensionMethods[U <: Product, C[_]](
q: Query[_, U, C]
): PatchActionExtensionMethodsImpl[U] =
createPatchActionExtensionMethods(updateCompiler.run(q.toNode).tree, ())
}
///////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////// Patch Actions
///////////////////////////////////////////////////////////////////////////////////////////////
type PatchActionExtensionMethods[T <: Product] = PatchActionExtensionMethodsImpl[T]
def createPatchActionExtensionMethods[T <: Product](tree: Node, param: Any): PatchActionExtensionMethods[T] =
new PatchActionExtensionMethodsImpl[T](tree, param)
class PatchActionExtensionMethodsImpl[T <: Product](tree: Node, param: Any) {
protected[this] val ResultSetMapping(_, CompiledStatement(_, sres: SQLBuilder.Result, _),
CompiledMapping(_converter, _)) = tree
protected[this] val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, Product]]
protected[this] val TypeMappingResultConverter(childConverter, toBase, toMapped) = converter
protected[this] val ProductResultConverter(elementConverters # _ *) =
childConverter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, Product]]
private[this] val updateQuerySplitRegExp = """(.*)(?<=set )((?:(?= where)|.)+)(.*)?""".r
private[this] val updateQuerySetterRegExp = """[^\s]+\s*=\s*\?""".r
/** An Action that updates the data selected by this query. */
def patch(value: T): DriverAction[Int, NoStream, Effect.Write] = {
val (seq, converters) = value.productIterator.zipWithIndex.toIndexedSeq
.zip(elementConverters)
.filter {
case ((Some(_), _), _) => true
case ((None, _), _) => false
case ((null, _), _) => false
case ((_, _), _) => true
}
.unzip
val (products, indexes) = seq.unzip
val newConverters = converters.zipWithIndex
.map(c => (c._1, c._2 + 1))
.map {
case (c: BaseResultConverter[_], idx) => new BaseResultConverter(c.ti, c.name, idx)
case (c: OptionResultConverter[_], idx) => new OptionResultConverter(c.ti, idx)
case (c: DefaultingResultConverter[_], idx) => new DefaultingResultConverter(c.ti, c.default, idx)
case (c: IsDefinedResultConverter[_], idx) => new IsDefinedResultConverter(c.ti, idx)
}
val productResultConverter =
ProductResultConverter(newConverters: _*).asInstanceOf[ResultConverter[JdbcResultConverterDomain, Any]]
val newConverter = TypeMappingResultConverter(productResultConverter, (p: Product) => p, (a: Any) => toMapped(a))
val newValue: Product = new ProductWrapper(products)
val newSql = sres.sql match {
case updateQuerySplitRegExp(prefix, setter, suffix) =>
val buffer = StringBuilder.newBuilder
buffer.append(prefix)
buffer.append(
updateQuerySetterRegExp
.findAllIn(setter)
.zipWithIndex
.filter(s => indexes.contains(s._2))
.map(_._1)
.mkString(", ")
)
buffer.append(suffix)
buffer.toString()
}
new SimpleJdbcDriverAction[Int]("patch", Vector(newSql)) {
def run(ctx: Backend#Context, sql: Vector[String]): Int =
ctx.session.withPreparedStatement(sql.head) { st =>
st.clearParameters
newConverter.set(newValue, st)
sres.setter(st, newConverter.width + 1, param)
st.executeUpdate
}
}
}
}
}
Example
// Model
case class User(
id: Option[Int] = None,
name: Option[String] = None,
username: Option[String] = None,
password: Option[String] = None
)
// Table
class Users(tag: Tag) extends Table[User](tag, "users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def username = column[String]("username")
def password = column[String]("password")
override def * = (id.?, name.?, username.?, password.?) <>(User.tupled, User.unapply)
}
// TableQuery
object Users extends TableQuery(new Users(_))
// CustomDriver
trait CustomDriver extends PostgresDriver with PatchActionExtensionMethodsSupport {
override val api: API = new API {}
trait API extends super.API with PatchActionImplicits
}
// Insert
Users += User(Some(1), Some("Test"), Some("test"), Some("1234"))
// User patch
Users.filter(_.id === 1).patch(User(name = Some("Change Name"), username = Some("")))
https://gist.github.com/bad79s/1edf9ea83ba08c46add03815059acfca
Building on JonasAnso's answer, converting that to slick v3.0+, and putting it into a transaction:
def partialUpdate(id: Int, name: Option[String], login: Option[String]): Future[Int] = {
val selectQ = users.filter(_.id === id)
val query = selectQ.result.head.flatMap { data =>
selectQ.update(data.patch(name, login))
}
db.run(query)
}
As I commented the question is similar to an existing one, but you don't seem to have any extra requirements.
The simplest approach is just SELECT + UPDATE. For example you add a patch function in your DataRow class defining how you want to update your model
def patch(name: Option[String], state: Option[State], price: Option[Int]): Data {
this.copy(name = name.getOrElse(this.name), ...)
}
And you add a partialUpdate method in your repo class
class DataRepo {
private val Datas = TableQuery[Data]
val db = ???
def partialUpdate(id: Int, name: Option[String], state: Option[State], price: Option[Int]): Future[Int] = {
val query = Datas.filter(_.id === id)
for {
data <- db.run(query.result.head)
result <- db.run(query.update(data.patch(name, state, price)))
} yield result
}
}
As you see the main problem of this solution is that there are 2 SQL statements, SELECT and UPDATE.
Other solution is to use plain SQL (http://slick.typesafe.com/doc/3.0.0/sql.html) but of course this gives other problems.

Retrieve typed stored values from Map

I'd like to put some data into a HashMap and retrieve these as typed values using a function. The function takes the expected type and also a default value in case the value is not stored in the HashMap. Type erasure of the JVM makes this a tricky thing.
Q: How can I retrieve a typed value?
Code and results below.
abstract class Parameters(val name: String) {
val parameters = new HashMap[String, Any]()
def put(key: String, value: Any) = parameters(key) = value
def get(key: String) = parameters.getOrElse(key, None)
def remove(key: String) = parameters.remove(key)
def g0[T: TypeTag](key: String, defaultValue: T) = {
get(key) match {
case x: T => x
case None => defaultValue
case _ => defaultValue
}
}
def g1[T: ClassTag](key: String, defaultValue: T) = {
val compareClass = implicitly[ClassTag[T]].runtimeClass
get(key) match {
case None => defaultValue
case x if compareClass.isInstance(x) => x.asInstanceOf[T]
}
}
}
class P extends Parameters("AParmList") {
put("1", 1)
put("3", "three")
put("4", 4.0)
put("width", 600)
println(g0[Int]("width", -1))
println(g0[Int]("fail", -2))
println(g1[Int]("width", -3))
println(g1[Int]("fail", -4))
}
object TypeMatching {
def main(args: Array[String]) {
new P
}
}
The output is (comments in parenthesis):
600 (as expected)
None (expected -2)
and a match error (java.lang.Integer stored, Int required)

in Slick 3.0, how to I get from a query to a case class?

I am trying to use Slick for database in a Scala application, and running into some issues (or my misunderstandings) of how to query (find) and convert the result to a case class.
I am not mapping the case class, but the actual values, with the intent of creating the case class on the fly. so, my table is:
object Tables {
class Names(tag: Tag) extends Table[Name](tag, "NAMES") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def first = column[String]("first")
def middle = column[String]("last")
def last = column[String]("last")
def * = (id.?, first, middle.?, last) <> ((Name.apply _).tupled, Name.unapply)
}
object NamesQueries {
lazy val query = TableQuery[Names]
val findById = Compiled { k: Rep[Long] =>
query.filter(_.id === k)
}
}
}
and here is the query:
object NamesDAO {
def insertName(name: Name) {
NamesQueries.query += name.copy(id = None)
}
def findName(nameId: Long) = {
val q = NamesQueries.findById(nameId) // AppliedCompiledFunction[Long, Query[Tables.Names, Tables.Names.TableElementType, Seq],Seq[Tables.Names.TableElementType]]
val resultSeq = Database.forConfig("schoolme").run(q.result) // Future[Seq[Tables.Names.TableElementType]]
val result = resultSeq.map { r => // val result: Future[(Option[Long], String, Option[String], String) => Name]
val rr = r.map{ name => // val rr: Seq[(Option[Long], String, Option[String], String) => Name]
Name.apply _
}
rr.head
}
result
}
}
however, the findName method seems to return Future((Option[Long], String, Option[String], String) => Name) instead of a Future(Name). What am i doing wrong? Is it just a matter of just using asInstanceOf[Name]?
EDIT: expanded findName to smaller chunks with comments for each one, as sap1ens suggested.
well, i'll be damned.
following sap1ens comment above, I broke findName to multiple steps (and edited the question). but after that, i went back and gave my val an explicit type, and that worked. see here:
def findName(nameId: Long) = {
val q = NamesQueries.findById(nameId)
val resultSeq: Future[Seq[Name]] = Database.forConfig("schoolme").run(q.result)
val result = resultSeq.map { r =>
val rr = r.map{ name =>
name
}
rr.head
}
result
}
so, type inference was the (/my) culprit this time. remember, remember.

How to do this with Scala generic

Currently I have couple of methods that are very similar and I would like to merge them into 1 method. Here are the 2 methods
def toInt(attrType: String, attrValue: String): Int = {
attrType match {
case "N" => attrValue.toInt
case _ => -1
}
}
def toString(attrType: String, attrValue: String): String = {
attrType match {
case "S" => attrValue
case _ => ""
}
}
I am thinking there is an easier way to do this in Scala using generic?
You could do the following:
trait Converter[T] {
def convert(attrType: String, attrValue: String): T
}
object ConverterTest {
implicit object IntConverter extends Converter[Int] {
def convert(attrType: String, attrValue: String): Int = {
attrType match {
case "N" => attrValue.toInt
case _ => -1
}
}
}
implicit object StringConverter extends Converter[String] {
def convert(attrType: String, attrValue: String): String = {
attrType match {
case "S" => attrValue
case _ => ""
}
}
}
def to[T: Converter](attrType: String, attrValue: String): T = {
implicitly[Converter[T]].convert(attrType, attrValue)
}
def main(args: Array[String]) {
println(to[String]("S", "B"))
println(to[String]("N", "B"))
println(to[Int]("S", "23"))
println(to[Int]("N", "23"))
}
}
Its more code, and I couldn't get type inferencing to work, so it is probably of limited use.
But it is a single method plus a bunch of converters that can get controlled at the call site, so you get some extra flexibility.
Is it worth the effort? Depends on the actual use case.