I have the following methods that return futures:
db.find(uuid, accountName): Future[Option[Account]]
db.add(uuid, account): Future[Option[Account]]
I want to write a program that finds a user in the database using a future.
If not, a user entry should be added to the database.
In either case, I want to get back a Future[Either[String, Option[Account]]] object.
I am getting a compiler error when I write the following:
def add(uuid: UUID, account: Account): Future[Either[String, Option[Account]]] =
db.find(userId, account.name).map {
case None =>
db.add(userId.uuid, account).map(Right(_))
case Some(existAccount) =>
Left(s"Account already exist $existAccount")
}
Edit:
This code can`t compile with error:
Error:(77, 41) type mismatch;
found : scala.concurrent.Future[scala.util.Right[Nothing,Option[Account]]]
required: Either[String,Option[Account]]
db.add(userId, account).map(Right(_))
^
Resolved:
Solution is:
object Test extends App {
import concurrent._
import ExecutionContext.Implicits._
import duration.Duration._
type Account = String
val found = """(.+)\1""".r
def find(name: String) = name match {
case found(_*) => Future(Option(name))
case _ => Future(None)
}
def add(account: Account) = Future(Option(account * 2))
def f(account: Account): Future[Either[String, Option[Account]]] = {
find(account) flatMap {
case None => add(account) map (Right(_))
case Some(x) => Future successful Left(s"already $account")
}
}
Console println Await.result(f("bob"), Inf)
Console println Await.result(f("bobbob"), Inf)
}
Related
I am trying to implement generic method in Scala operating on database using Quill.io library. Type T will be only case classes what works with Quill.io.
def insertOrUpdate[T](inserting: T, equality: (T,T) => Boolean)(implicit ctx: Db.Context): Unit = {
import ctx._
val existingQuery = quote {
query[T].filter { dbElement: T =>
equality(dbElement, inserting)
}
}
val updateQuery = quote {
query[T].filter { dbElement =>
equality(dbElement, lift(inserting))
}.update(lift(inserting))
}
val insertQuery = quote { query[T].insert(lift(inserting)) }
val existing = ctx.run(existingQuery)
existing.size match {
case 1 => ctx.run(updateQuery)
case _ => ctx.run(insertQuery)
}
}
But I am getting two types of compile error
Error:(119, 12) Can't find an implicit `SchemaMeta` for type `T`
query[T].filter { dbElement: T =>
Error:(125, 33) Can't find Encoder for type 'T'
equality(dbElement, lift(inserting))
How can I modify my code to let it work?
As I said in the issue that #VojtechLetal mentioned in his answer you have to use macros.
I added code implementing generic insert or update in my example Quill project.
It defines trait Queries that's mixed into context:
trait Queries {
this: JdbcContext[_, _] =>
def insertOrUpdate[T](entity: T, filter: (T) => Boolean): Unit = macro InsertOrUpdateMacro.insertOrUpdate[T]
}
This trait uses macro that's implementing your code with minor changes:
import scala.reflect.macros.whitebox.{Context => MacroContext}
class InsertOrUpdateMacro(val c: MacroContext) {
import c.universe._
def insertOrUpdate[T](entity: Tree, filter: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
val updateQuery = ${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).update(lift($entity))
}
val insertQuery = quote {
query[$t].insert(lift($entity))
}
run(${c.prefix}.query[$t].filter($filter)).size match {
case 1 => run(updateQuery)
case _ => run(insertQuery)
}
()
"""
}
Usage examples:
import io.getquill.{PostgresJdbcContext, SnakeCase}
package object genericInsertOrUpdate {
val ctx = new PostgresJdbcContext[SnakeCase]("jdbc.postgres") with Queries
def example1(): Unit = {
val inserting = Person(1, "")
ctx.insertOrUpdate(inserting, (p: Person) => p.name == "")
}
def example2(): Unit = {
import ctx._
val inserting = Person(1, "")
ctx.insertOrUpdate(inserting, (p: Person) => p.name == lift(inserting.name))
}
}
P.S. Because update() returns number of updated records your code can be simplified to:
class InsertOrUpdateMacro(val c: MacroContext) {
import c.universe._
def insertOrUpdate[T](entity: Tree, filter: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
if (run(${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).update(lift($entity))
}) == 0) {
run(quote {
query[$t].insert(lift($entity))
})
}
()
"""
}
As one of the quill contributors said in this issue:
If you want to make your solution generic then you have to use macros because Quill generates queries at compile time and T type has to be resolved at that time.
TL;DR The following did not work either, just playing
Anyway... just out of curiosity I tried to fix the issue by following the error which you mentioned. I changed the definition of the function as:
def insertOrUpdate[T: ctx.Encoder : ctx.SchemaMeta](...)
which yielded the following log
[info] PopulateAnomalyResultsTable.scala:71: Dynamic query
[info] case _ => ctx.run(insertQuery)
[info]
[error] PopulateAnomalyResultsTable.scala:68: exception during macro expansion:
[error] scala.reflect.macros.TypecheckException: Found the embedded 'T', but it is not a case class
[error] at scala.reflect.macros.contexts.Typers$$anonfun$typecheck$2$$anonfun$apply$1.apply(Typers.scala:34)
[error] at scala.reflect.macros.contexts.Typers$$anonfun$typecheck$2$$anonfun$apply$1.apply(Typers.scala:28)
It starts promising, since quill apparently gave up on static compilation and made the query dynamic. I checked the source code of the failing macro and it seems that quill is trying to get a constructor for T which is not known in the current context.
For more details see my answer Generic macro with quill or implementation
CrudMacro:
Complete project you will find on quill-generic
package pl.jozwik.quillgeneric.quillmacro
import scala.reflect.macros.whitebox.{ Context => MacroContext }
class CrudMacro(val c: MacroContext) extends AbstractCrudMacro {
import c.universe._
def callFilterOnIdTree[K: c.WeakTypeTag](id: Tree)(dSchema: c.Expr[_]): Tree =
callFilterOnId[K](c.Expr[K](q"$id"))(dSchema)
protected def callFilterOnId[K: c.WeakTypeTag](id: c.Expr[K])(dSchema: c.Expr[_]): Tree = {
val t = weakTypeOf[K]
t.baseClasses.find(c => compositeSet.contains(c.asClass.fullName)) match {
case None =>
q"$dSchema.filter(_.id == lift($id))"
case Some(base) =>
val query = q"$dSchema.filter(_.id.fk1 == lift($id.fk1)).filter(_.id.fk2 == lift($id.fk2))"
base.fullName match {
case `compositeKey4Name` =>
q"$query.filter(_.id.fk3 == lift($id.fk3)).filter(_.id.fk4 == lift($id.fk4))"
case `compositeKey3Name` =>
q"$query.filter(_.id.fk3 == lift($id.fk3))"
case `compositeKey2Name` =>
query
case x =>
c.abort(NoPosition, s"$x not supported")
}
}
}
def createAndGenerateIdOrUpdate[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
val filter = callFilter[K, T](entity)(dSchema)
q"""
import ${c.prefix}._
val id = $entity.id
val q = $filter
val result = run(
q.updateValue($entity)
)
if (result == 0) {
run($dSchema.insertValue($entity).returningGenerated(_.id))
} else {
id
}
"""
}
def createWithGenerateIdOrUpdateAndRead[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
val filter = callFilter[K, T](entity)(dSchema)
q"""
import ${c.prefix}._
val id = $entity.id
val q = $filter
val result = run(
q.updateValue($entity)
)
val newId =
if (result == 0) {
run($dSchema.insertValue($entity).returningGenerated(_.id))
} else {
id
}
run($dSchema.filter(_.id == lift(newId)))
.headOption
.getOrElse(throw new NoSuchElementException(s"$$newId"))
"""
}
}
How is the createUser or updateUser using the unmarshalValueJs function and passing arguments.
package controllers.user
import play.api._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import services.user.UserServiceComponent
import domain.user.User
trait UserController extends Controller {
self: UserServiceComponent =>0
def emailAlreadyExists(implicit reads: Reads[String]) =
Reads[String](js => reads.reads(js).flatMap { e =>
userService.tryFindByEmail(e).map(_ => JsError("error.custom.emailAlreadyExists")).getOrElse(JsSuccess(e))
})
implicit val userReads = (__ \ "email").read[String](email andKeep emailAlreadyExists)
.map(resource => UserResource(resource))
implicit val userWrites = new Writes[User] {
override def writes(user: User): JsValue = {
Json.obj(
"id" -> user.id,
"email" -> user.email
)
}
}
What create User passes to the unmarshalJsValue?
Where does resource come from?
def createUser = Action(parse.json) {request =>
unmarshalJsValue(request) { resource: UserResource =>
val user = User(Option.empty, resource.email)
userService.createUser(user)
Created
}
}
def updateUser(id: Long) = Action(parse.json) {request =>
unmarshalJsValue(request) { resource: UserResource =>
val user = User(Option(id), resource.email)
userService.updateUser(user)
NoContent
}
}
def findUserById(id: Long) = Action {
val user = userService.tryFindById(id)
if (user.isDefined) {
Ok(Json.toJson(user))
} else {
NotFound
}
}
def deleteUser(id: Long) = Action {
userService.delete(id)
NoContent
}
What is R over here?
What get passed back to the createUser?
def unmarshalJsValue[R](request: Request[JsValue])(block: R => Result)(implicit rds : Reads[R]): Result =
request.body.validate[R](rds).fold(
valid = block,
invalid = e => {
val error = e.mkString
Logger.error(error)
BadRequest(error)
}
)
}
case class UserResource(email: String)
R is a type variable. The definition def unmarshalJsValue[R](request: Request[JsValue])(block: R => Result)(implicit rds : Reads[R]) reads:
I'm a method called unmarshallJsValue.
I require a type parameter that could potentially be anything.
I require a value of Request[JsValue] (presumably a request contianin a JSON entity).
I require a function that, given some value of type R, produces a Result.
I require a value to be in implicit scope that is a Reads for type R. In other words, I need a way to convert a JsValue to an R, whatever R might happen to be. (If you look at the docs for Reads, you'll notice that its only method, reads, has this effect.)
To put it all together, entire function just says, give me some block of code that produces a Result from some piece of data I know how to convert to from JSON. The body of the function simply attempts to convert the JSON to the desired type (known as unmarshalling). If this succeeds, it runs the block. Otherwise, you get a BadRequest client error.
The compiler can't know whether the JSON that gets provided in real life by the client will be convertible to some type R, but it can require a JSON -> R converter, and some error handling code if it fails on real data.
I have the following functions:
//retrieves record from database
def getAll: Future[List[User]] = {
try {
Logger.info("Getting all user record.")
db.run(userTableQuery.to[List].result)
}
catch {
case ex: Exception => Logger.error("Exception in getting all user record. " + ex)
Future {
List[User]()
}
}
}
//checks whethe the email exist in list or not
def checkEmail(email : String): Future[Option[User]] ={
/* userRepo.getAll.map(userList => userList.filter(user => user.email == email).map { value => println(value)
userList
})*/
userRepo.getAll.map(userList => userList.filter(user => user.email == email).headOption)
}
//allows to sign in and redirects to dashboard
def signIn() = {
Logger.debug("signingIn in progress. ")
loginForm.bindFromRequest.fold(
formWithErrors => {
Logger.error("Sign-In badRequest.")
Future(BadRequest(views.html.home(webJarAssets, formWithErrors, signUpForm)))
},
validData => {
userService.checkEmail(validData.email).map(value => {
value match {
case Some(us) =>Redirect(routes.HomeController.homePage).flashing("ERROR" -> "User exist")
case None => Redirect(routes.HomeController.homePage).flashing("ERROR" -> "User doesn't exist")
}
}
)
}
)
}
But when I call signin() it always returns None.
I used some debugger code and I guess filter() inside checkMail() is not working properly.
However getall() working properly and gives all the record in database.
I think the problem is in how you compare the user email with the provided one inside the filter in the checkMail() function.
Strings equality is a bit tricky, if you compare them using == you are comparing the objects and not the values so you should use .equals() to compare the values.
You can read more on this blog post
Try to rewrite checkMail() like this:
def checkEmail(email : String): Future[Option[User]] ={
userRepo.getAll.map(userList => userList.filter(user => user.email.equals( email ) ).headOption)
}
You can also simplify .filter() e .headOption using find(), it does the same thing only in one command. You can rewrite it like this:
def checkEmail(email : String): Future[Option[User]] ={
userRepo.getAll.map(userList => userList.find(user => user.email.equals( email ) ) )
}
Instead of using filter you may use find method under checkmail. And also, since this is scala you are using "==" correctly, please see blog here
I hope this code would fix:
//checks whethe the email exist in list or not
def checkEmail(email : String): Future[Option[User]] ={
/* userRepo.getAll.map(userList => userList.filter(user => user.email == email).map { value => println(value)
userList
})*/
userRepo.getAll.map(userList => userList.find(user => user.email == email))
}
I tried to simulate/experiment regarding your implementation in direct way using the terminal:
scala> case class User(email: String)
defined class User
scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
scala> val allUser = scala.concurrent.Future {List(User("carl#test.com"), User("los#test.com"), User("pos#test.com"))}
allUser: scala.concurrent.Future[List[User]] = scala.concurrent.impl.Promise$DefaultPromise#751d3241
scala> val checkmail = allUser.map(userlist=>userlist.find(user=>user.email == "carl#test.com"))
checkmail: scala.concurrent.Future[Option[User]] = scala.concurrent.impl.Promise$DefaultPromise#1358b28e
scala> val rslt = checkmail.map(value => value match {case Some(x) =>println(x); x.email case None => println("None"); "nothing" })
rslt: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise#18324f97
User(carl#test.com)
scala> import scala.concurrent.duration._
import scala.concurrent.duration._
scala> import scala.concurrent._
import scala.concurrent._
scala> Await.result(rslt, 3 seconds)
warning: there was one feature warning; re-run with -feature for details
res8: String = carl#test.com
I am just new to learning Scala and the related technologies.I am coming across the problem where the loadUser should return a record but its coming empty.
I am getting the following error:
java.util.NoSuchElementException: None.get
I appreciate this is not ideal Scala, so feel free to suggest me improvements.
class MongoDataAccess extends Actor {
val message = "Hello message"
override def receive: Receive = {
case data: Payload => {
val user: Future[Option[User]] = MongoDataAccess.loadUser(data.deviceId)
val twillioApiAccess = context.actorOf(Props[TwillioApiAccess], "TwillioApiAccess")
user onComplete {
case Failure(exception) => println(exception)
case p: Try[Option[User]] => p match {
case Failure(exception) => println(exception)
case u: Try[Option[User]] => twillioApiAccess ! Action(data, u.get.get.phoneNumber, message)
}
}
}
case _ => println("received unknown message")
}
}
object MongoDataAccess extends MongoDataApi {
def connect(): Future[DefaultDB] = {
// gets an instance of the driver
val driver = new MongoDriver
val connection = driver.connection(List("192.168.99.100:32768"))
// Gets a reference to the database "sensor"
connection.database("sensor")
}
def props = Props(new MongoDataAccess)
def loadUser(deviceId: UUID): Future[Option[User]] = {
println(s"Loading user from the database with device id: $deviceId")
val query = BSONDocument("deviceId" -> deviceId.toString)
// By default, you get a Future[BSONCollection].
val collection: Future[BSONCollection] = connect().map(_.collection("profile"))
collection flatMap { x => x.find(query).one[User] }
}
}
Thanks
There is no guaranty the find-one (.one[T]) matches at least one document in your DB, so you get an Option[T].
Then it's up to you to consider (or not) that having found no document is a failure (or not); e.g.
val u: Future[User] = x.find(query).one[User].flatMap[User] {
case Some(matchingUser) => Future.successful(matchingUser)
case _ => Future.failed(new MySemanticException("No matching user found"))
}
Using .get on Option is a bad idea anyway.
When compiling this specification:
import org.specs.Specification
import org.specs.matcher.extension.ParserMatchers
class ParserSpec extends Specification with ParserMatchers {
type Elem = Char
"Vaadin DSL parser" should {
"parse attributes in parentheses" in {
DslParser.attributes must(
succeedOn(stringReader("""(attr1="val1")""")).
withResult(Map[String, AttrVal]("attr1" -> AttrVal("val1", "String"))))
}
}
}
I get the following error:
ParserSpec.scala:21
error: scala is not an enclosing class
withResult(Map[String, AttrVal]("attr1" -> AttrVal("val1", "String"))))
^
I don't understand the error message here at all. Why could it appear?
Scala version is 2.8.1, specs version is 1.6.7.2.
DslParser.attributes has type Parser[Map[String, AttrVal]] and the combinators succeedOn and withResult are defined as follows:
trait ParserMatchers extends Parsers with Matchers {
case class SucceedOn[T](str: Input,
resultMatcherOpt: Option[Matcher[T]]) extends Matcher[Parser[T]] {
def apply(parserBN: => Parser[T]) = {
val parser = parserBN
val parseResult = parser(str)
parseResult match {
case Success(result, remainingInput) =>
val succParseMsg = "Parser "+parser+" succeeded on input "+str+" with result "+result
val okMsgBuffer = new StringBuilder(succParseMsg)
val koMsgBuffer = new StringBuilder(succParseMsg)
val cond = resultMatcherOpt match {
case None =>
true
case Some(resultMatcher) =>
resultMatcher(result) match {
case (success, okMessage, koMessage) =>
okMsgBuffer.append(" and ").append(okMessage)
koMsgBuffer.append(" but ").append(koMessage)
success
}
}
(cond, okMsgBuffer.toString, koMsgBuffer.toString)
case _ =>
(false, "Parser succeeded", "Parser "+parser+": "+parseResult)
}
}
def resultMust(resultMatcher: Matcher[T]) = this.copy(resultMatcherOpt = Some(resultMatcher))
def withResult(expectedResult: T) = resultMust(beEqualTo(expectedResult))
def ignoringResult = this.copy(resultMatcherOpt = None)
}
def succeedOn[T](str: Input, expectedResultOpt: Option[Matcher[T]] = None) =
SucceedOn(str, expectedResultOpt)
implicit def stringReader(str: String): Reader[Char] = new CharSequenceReader(str)
}
This message can occur while the compiler is really trying to signal a type error or a type inference failure. It's a bug (or family of bugs) in scalac.
To locate the problem, progressively add in explicit types and type arguments; break complex expressions into smaller subexpressions.
For bonus points, produce a standalone example and file a bug.