Scala implicit class - mongodb

I am seeing some unexpected behavior with an implicit class. I have a case class that is retrieved from Mongo, and in certain cases wrapped into an implicit class with convenience lazy vals to retrieve data from other relevant collections. Code looks something like
case class MyClass(
_id: UUID, ...,
//some fields
)
and then
object MyImplicits {
implicit class MyRichClass(c: MyClass){
lazy val field1: Future[Iterable[T1]] = db.findAll[T1](bson)
lazy val field2: Future[Iterable[T2]] = db.findAll[T2](bson)
}
}
and the conversion is used like
import ...MyImplicits.MyRichClass
val c: MyClass...
c.field1.map(...)
My problem is Futures in field1 and field2 always contain an empty Iterable. I have spent best part of today debugging my queries, and they work correctly both in test code and Mongo cli.
Any tips would be greatly appreciated.
I have a wrapper around org.mongodb.scala.MongoClient, that has:
/**
* Finds all instances of T using raw mongo query
*
* #param bson search query
* #tparam T
* #return
*/
private[service] def findAll[T: ClassTag](bson: Bson): Future[Iterable[T]] = {
val colName = resolveCollection[T]
logger.info("colName = " + colName)
db.getCollection[T](colName).find(equal("ownerId", "user1")).toFuture().map(t => {
logger.info("t = " + t)
t
})
}
/**
* Returns collection name for given type
* #tparam T
* #return
*/
private def resolveCollection[T: ClassTag]: String = {
scala.reflect.classTag[T] match {
case `...tag` => "collectionName"
....
}
}
The
equal("ownerId", "user1")
is hardcoded for debugging purposes, as well as extra loggers.

There is actually no issues with the code. I was literally querying a wrong database, so, yes - all collections were in fact empty.

Related

Writing error code and its string message: Scala

I am storing error code and its string message in this way:
object Error {
def FileNotFound(filename: String) = Error("ERR01", s"${filename} not found")
def UserNotExist(userName: String) = Error("ERR02", s"${userName} not exist")
}
case class Error(code: String, value: String) {}
Benefit of keeping it like this is that I can pass string value to error message.
And I am creating it like
def validate(data: SomeType): List[Error] = {
var errors = ListBuffer[Error]()
if (validation1("abc") == false) {
errors+= Error.FileNotFound("abc")
}
if (validation2("lmn") == false) {
errors+= Error.UserNotExist("lmn")
}
errors.toList
}
I am new to Scala and functional programming.
Is it right way to write error code like this?
Is it following functional programming paradigm?
Scala: 2.11
Here are my two cents with a full example.
I assume you want to do this in nice-and-easy functional style, so let's remove the mutable ListBuffer and see how to deal with it. The description of the steps are after the code.
UPDATE: I want to note that, in your code, the usage of ListBuffer is acceptable because it doesn't break rerefential transparency. This is because you don't expose its mutable nature outside the function and the output of the function depends only on its inputs.
Nonetheless I usually prefer to avoid using mutable data if not for specific reasons, like compactness or performance.
object StackOverflowAnswer {
/* Base type with some common fields */
class MyError( val code: String, val msg: String )
/* FileNotFound type, subtype of MyError, with its specific fields */
case class FileNotFound( filename: String ) extends MyError(
"ERR01", s"$filename not found"
)
/* UserDoesntExist type, subtype of MyError, with its specific fields */
case class UserDoesntExist( userName: String ) extends MyError(
"ERR01", s"$userName doesn't exist"
)
/*
* Validates the file. If it finds an error it returns a Some(MyError) with
* the error that's been found
* */
def checkForBadFile( data: String ): Option[MyError] =
if( data.contains("bad_file.txt") )
Some(FileNotFound("bad_file.txt"))
else
None
/*
* Validates the user. If it finds an error it returns a Some(MyError) with
* the error that's been found
* */
def checkForMissingUser( data: String ): Option[MyError] =
if( data.contains("bad_username") )
Some(UserDoesntExist("bad_username"))
else
None
/*
* Performs all the validations and returns a list with the errors.
*/
def validate( data: String ): List[MyError] = {
val fileCheck = checkForBadFile( data )
val userCheck = checkForMissingUser( data )
List( fileCheck, userCheck ).flatten
}
/* Run and test! */
def main( args: Array[String] ): Unit = {
val goodData = "This is a text"
val goodDataResult = validate( goodData ).map( _.msg )
println(s"The checks for '$goodData' returned: $goodDataResult")
val badFile = "This is a text with bad_file.txt"
val badFileResult = validate( badFile ).map( _.msg )
println(s"The checks for '$badFile' returned: $badFileResult")
val badUser = "This is a text with bad_username"
val badUserResult = validate( badUser ).map( _.msg )
println(s"The checks for '$badUser' returned: $badUserResult")
val badBoth = "This is a text with bad_file.txt and bad_username"
val badBothResult = validate( badBoth ).map( _.msg )
println(s"The checks for '$badBoth' returned: $badBothResult")
}
}
I start defining the type structure for the error, similar to your.
Then I have two functions that performs validations for each check you want. When they find an error, they return it using the Option type of Scala. If you're not familiar with Option, you can have a look at this link or do some googling.
Then I have the validation function that calls and store each the above single checks. The last bit is the use of flatten (doc here) that "flattens" List[Option[MyError]] into List[MyError], removing the middle Option.
Then there is the actual code that shows you some examples.

Update and delete queries not working in play-slick

Iam trying to write a simple CRUD application for play-slick. I can Insert into the database and display all users, but I cannot update or delete users. My entire code is below
package models
import javax.inject.{ Inject, Singleton }
import play.api.db.slick.DatabaseConfigProvider
import slick.jdbc.JdbcProfile
import scala.concurrent.{ Future, ExecutionContext }
/**
* A repository for people.
*
* #param dbConfigProvider The Play db config provider. Play will inject this for you.
*/
#Singleton
class PersonRepository #Inject() (dbConfigProvider: DatabaseConfigProvider)(implicit ec: ExecutionContext) {
// We want the JdbcProfile for this provider
private val dbConfig = dbConfigProvider.get[JdbcProfile]
// These imports are important, the first one brings db into scope, which will let you do the actual db operations.
// The second one brings the Slick DSL into scope, which lets you define the table and other queries.
import dbConfig._
import profile.api._
/**
* Here we define the table. It will have a name of people
*/
private class PeopleTable(tag: Tag) extends Table[Person](tag, "people") {
/** The ID column, which is the primary key, and auto incremented */
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
/** The name column */
def name = column[String]("name")
/** The age column */
def age = column[Int]("age")
/**
* This is the tables default "projection".
*
* It defines how the columns are converted to and from the Person object.
*
* In this case, we are simply passing the id, name and page parameters to the Person case classes
* apply and unapply methods.
*/
def * = (id, name, age) <> ((Person.apply _).tupled, Person.unapply)
}
/**
* The starting point for all queries on the people table.
*/
private val people = TableQuery[PeopleTable]
/**
* Create a person with the given name and age.
*
* This is an asynchronous operation, it will return a future of the created person, which can be used to obtain the
* id for that person.
*/
def create(name: String, age: Int): Future[Person] = db.run {
// We create a projection of just the name and age columns, since we're not inserting a value for the id column
(people.map(p => (p.name, p.age))
// Now define it to return the id, because we want to know what id was generated for the person
returning people.map(_.id)
// And we define a transformation for the returned value, which combines our original parameters with the
// returned id
into ((nameAge, id) => Person(id, nameAge._1, nameAge._2))
// And finally, insert the person into the database
) += (name, age)
}
/**
* List all the people in the database.
*/
def list(): Future[Seq[Person]] = db.run {
people.result
}
def del(id: Int) : Future[Seq[Person]] = db.run {
people.filter(_.id === id).delete
}
def update (id: Int, name: String) : Future[Seq[Person]] = {
db.run(people.filter(_.id === id).update("john"))
}
}
It might also be possible that I have excluded some imports, so please check to see if i need to import anything.
I also get this error and I don't know what it means:
type mismatch; found :
UserData.this.dbConfig.profile.ProfileAction[Int,slick.dbio.NoStream,slick.dbio.Effect.Write]
(which expands to)
slick.sql.FixedSqlAction[Int,slick.dbio.NoStream,slick.dbio.Effect.Write]
required:
slick.dbio.DBIOAction[Seq[models.User],slick.dbio.NoStream,Nothing]
I think you are having a problem with the return types when you delete or update.
AFAIK they return the number of rows that have been modified.
Try this:
def del(id: Int) : Future[Int] = db.run {
people.filter(_.id === id).delete
}
def update (id: Int, name: String) : Future[Int] = {
db.run(people.filter(_.id === id).update("john"))
}

Generating column mappings

I'm using the play-slick plugin 3.0.1 with Play! 2.6.6 and seem to have run into a problem.
What I'm trying to do is to implement "grant" based permission system from a DB table containing several columns. Since this table can be broad, I wanted to find a way to programmatically generate column names and read bits from each column, and evaluate to the presence or absence of a grant.
For example, the table might look like:
granted_for grantee read write
id1 id2 1 0
Next I set things up like so:
import centralapp.api.string.CamelToUnderscore
import models.permissions.grants.Grant
import slick.ast.{BaseTypedType, TypedType}
import slick.jdbc
import slick.jdbc.JdbcType
import slick.jdbc.MySQLProfile.api._
import slick.lifted.ProvenShape
trait HasName {
/**
* #note "HasName"
*/
def simpleName: String = getClass.getSimpleName.replaceAll("[^a-zA-Z0-9_]", "")
/**
* #note "has_name"
*/
def classNameLower: String = simpleName.toUnderscoreLower
/**
* #note "HAS_NAME"
*/
def classNameUpper: String = simpleName.toUnderscoreUpper
}
And then a trait that defines a DB column abstraction:
trait DBColumn[C, T <: Table[_]] extends HasName {
/**
* the name of the column
*/
def columnName: String = classNameLower
/**
* get the column representation
*
* #param c the TypedType representation of this column's type
* #param table the table for which the column is being generated
* #return a Rep[C] (representation of type C)
*/
def col(implicit c: TypedType[C], table: T): Rep[C] = table.column[C](columnName)
}
And then use that to generate "Grant" column mappings:
package object db {
/**
* creates a grant column mapping from a boolean column for a given grant
*
* #param g the grant to obtain if the column is set to "true"
* #tparam G the subtype of the grant
* #return the JDBC type for the desired value
*/
def optGrantFromBoolCol[G <: Grant](g: G): JdbcType[Option[G]] with BaseTypedType[Option[G]] = MappedColumnType.base[Option[G], Boolean](
_.isDefined,
{
case true => Some(g)
case false => None
}
)
abstract class GrantCol[G <: Grant, T <: Table[_]](grant: G) extends DBColumn[Option[G], T] {
// class Grant also extends HasName; here I want to ensure the column name is exactly the same as the string representation of the grant.
override def columnName: String = grant.classNameLower
/**
* get the column representation
*
* #param table the table for which the column is being generated
* #return a Rep[C] (representation of type C)
*/
def grantCol(implicit table: T): jdbc.MySQLProfile.api.Rep[Option[G]] = col(optGrantFromBoolCol(grant), table)
}
}
And then I tried to use this like so:
object Columns {
case object GrantedFor extends DBColumn[String, MyTable]
case object Grantee extends DBColumn[String, MyTable]
case object Read extends GrantCol[UserGrant, MyTable](usergrants.Read)
}
// and then used it in a table:
class MyTable(tag: Tag) extends Table[SomeClass](tag, "my_table") {
implicit def table = this
val grantedFor = Columns.GrantedFor.col
val grantee = Columns.Grantee.col
val read = Columns.Read.grantCol
def * : ProvenShape[SomeClass] =
(grantedFor, grantee, read) <> // <- this is the problem: Cannot resolve symbol "<>"
(
???, // valid constructor from tuple
??? // valid unapply
)
def other : ProvenShape[OtherClass] =
(grantedFor, grantee) <> (
???,
???
)
}
As described in the comments, I see that the moment there's a "Grant" column in the tuple, the resolution for the <> symbol starts failing. I cannot seem to see what the problem here could be.
Any pointers?

Scala: class type required but T found

I've found similar issues of this particular problem, however the problem was due to someone trying to instantiate T directly. Here I'm trying to create a trait that is a general interface to extend classes and store them automatically in a database such as Riak using classOf[T]. Using Scala 2.10.
Here's my code:
trait RiakWriteable[T] {
/**
* bucket name of data in Riak holding class data
*/
def bucketName: String
/**
* determine whether secondary indices will be added
*/
def enable2i: Boolean
/**
* the actual bucket
*/
val bucket: Bucket = enable2i match {
case true => DB.client.createBucket(bucketName).enableForSearch().execute()
case false => DB.client.createBucket(bucketName).disableSearch().execute()
}
/**
* register the scala module for Jackson
*/
val converter = {
val c = new JSONConverter[T](classOf[T], bucketName)
JSONConverter.registerJacksonModule(DefaultScalaModule)
c
}
/**
* store operation
*/
def store = bucket.store(this).withConverter(converter).withRetrier(DB.retrier).execute()
/**
* fetch operation
*/
def fetch(id: String): Option[T] = {
val u = bucket.fetch(id, classOf[T]).withConverter(converter).withRetrier(DB.retrier).r(DB.N_READ).execute()
u match {
case null => None
case _ => Some(u)
}
}
}
Compiler error is class type required but T found.
Example usage (pseudo-code):
class Foo
object Foo extends RiakWriteable[Foo]
Foo.store(object)
So I'm guessing that a manifest of T is not being properly defined. Do I need to implicitly define this somewhere?
Thanks!
Here's an intermediary solution, though it leaves out the converter registration (which I may leave permanently for this use case, not sure yet).
/**
* trait for adding write methods to classes
*/
trait RiakWriteable[T] {
/**
* bucket name of data in Riak holding class data
*/
def bucketName: String
/**
* determine whether secondary indices will be added
*/
def enable2i: Boolean
/**
* the actual bucket
*/
val bucket: Bucket = enable2i match {
case true => DB.client.createBucket(bucketName).enableForSearch().execute()
case false => DB.client.createBucket(bucketName).disableSearch().execute()
}
/**
* store operation
*/
def store(o: T) = bucket.store(o).withRetrier(DB.retrier).execute()
/**
* fetch operation
*/
def fetch(id: String)(implicit m: ClassTag[T]) = {
val u = bucket.fetch(id, classTag[T].runtimeClass).withRetrier(DB.retrier).r(DB.N_READ).execute()
u match {
case null => None
case _ => Some(u)
}
}
}

How to parse this "pretty" BSON in Scala?

In our project we are using Scala and Reactivemongo. (I'm very new to both)
When you print to the console a "pretty" Bson, it looks like this:
{ _id: BSONObjectID("52b006fe0100000100d47242"),
desc: BSONString({"_id:"BSONObjectID(52af03a5010000010036194d),"desc:"BSONString(www.foo.com"hits),"domains:"["0:"BSONString(www.foo.com)"],"func:"BSONString(Count),"maxr:"BSONInteger(5),"props:"["]"} hits),
domains: [
0: BSONString(jsonString)
],
func: BSONString(Count),
maxr: BSONInteger(5),
props: [
]
}
I need to be able to parse it back from the console into a corosponding Case Class.
Any help please?
taken from typesafe's activator template, you can simply use Json.format as an implicit val in the case class's companion object (ReactiveMongo 0.9, scala 2.10.2). example:
package models
import play.api.libs.json.Json
import reactivemongo.bson.BSONObjectID
import play.modules.reactivemongo.json.BSONFormats._
/**
* A message class
*
* #param _id The BSON object id of the message
* #param message The message
*/
case class Message(_id: BSONObjectID, message: String)
object Message {
/**
* Format for the message.
*
* Used both by JSON library and reactive mongo to serialise/deserialise a message.
*/
implicit val messageFormat = Json.format[Message]
}
i am using it, and you can use more parameters as long as JSON knows how to format them, or, if you have members that are case classes you created, if the have the same:
package models
import play.api.libs.json.Json
import reactivemongo.bson.BSONObjectID
import play.modules.reactivemongo.json.BSONFormats._
/**
* A message class
*
* #param _id The BSON object id of the message
* #param message The message
*/
case class Name(fName: String, lName: String, mInitial: String)
object Name {
implicit val nameFormat = Json.format[Name]
}
case class Message(_id: BSONObjectID, message: String, name: Name)
object Message {
/**
* Format for the message.
*
* Used both by JSON library and reactive mongo to serialise/deserialise a message.
*/
implicit val messageFormat = Json.format[Message]
}
i still have not figured a way to do it if you have auxiliary constructors, or if you implement apply(...) in the companion. however, the compiler will alert you to that.
You can check how they did it at this link in Reactivemongo documentation: http://stephane.godbillon.com/2012/10/18/writing-a-simple-app-with-reactivemongo-and-play-framework-pt-1.html
implicit object ArticleBSONReader extends BSONReader[Article] {
def fromBSON(document: BSONDocument) :Article = {
val doc = document.toTraversable
Article(
doc.getAs[BSONObjectID]("_id"),
doc.getAs[BSONString]("title").get.value,
doc.getAs[BSONString]("content").get.value,
doc.getAs[BSONString]("publisher").get.value,
doc.getAs[BSONDateTime]("creationDate").map(dt => new DateTime(dt.value)),
doc.getAs[BSONDateTime]("updateDate").map(dt => new DateTime(dt.value)))
}
}
Here a solution I'm using. I hope it helps ->
def bsonToString(bson: BSONDocument): String = {
val bsonAsKeyValueList = bson.toMap.toList.map {
case (key, document: BSONDocument) =>
(key, bsonToString(document))
case (key, value: BSONValue) =>
(key, value.toString)
}
val bsonAsKeyValueStrings = bsonAsKeyValueList.map {
case (key, value) =>
s"""("${key}" -> ${value})"""
}
bsonAsKeyValueStrings.mkString("BSONDocument", ", ", "")
}