Cast N1QLQuery response into custom object in Scala - scala

I have a simple case class:
case class Account(accountId: Long, login: DateTime)
Now I want to retrieve docs from Couchbase bucket by simple N1QL query (it should return a simple list of JSON documents contain two fields):
val query = "SELECT u.accountId, u.login FROM `accounts` u WHERE DATE_DIFF_STR(NOW_STR(), u.login, 'day') > 30"
bucket.query(N1qlQuery.simple(query)).map(rows => rows.map(row => row.value().asInstanceOf[Account]).seq)
but, I got an error there in postman:
java.lang.ClassCastException: com.couchbase.client.java.document.json.JsonObject cannot be cast to com.package.account
My question is - how I could cast docs from database into my, custom object? I also tried to cast it into RawJSONDocument first, but it did not help.
Can someone help me with that?

First, you may be interested to know that we're actively working on a native Couchbase Scala SDK at present for release this year, and it will support your use case of converting rows directly into a case class.
But in the here-and-now, no you cannot directly cast a JsonObject into a case class. You will need to use toString to pull out the raw JSON string, and then use a Scala JSON library to convert it. You've got several options here:
Jackson
val json = row.value().toString()
val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)
val account = mapper.readValue(json, classOf[Account])
uPickle
val account = upickle.default.read[Account](json)
Jsoniter
val account = com.github.plokhotnyuk.jsoniter_scala.core.readFromString[Account](json)
Plus there's Circe, Json4s, Play Json, Jawn etc..
FWIW, I found Jsoniter to be the fastest in my benchmarking.

Related

Nested JSON in scala, Circe, Slick

I have a nested JSON in my database. I have figured out the case class for the same. I am using circe, slick and Akka HTTP in my Web api application.
My case class is :
case class Sen
(
sentences: Array[File]
)
case class File
(
content: String,
)
I have written GetResult for the same nesting. I have problems with the array in the case class.
implicit lazy val getFile = GetResult(r => Array[File](r.<<))
implicit lazy val SenObj = GetResult(r => Sen(getFile(r)))
Can anyone tell me how to solve this?
Following is the error I get while compiling
Error:diverging implicit expansion for type slick.jdbc.GetResult[T]
starting with method createGetTuple22 in object GetResult
implicit lazy val getFile = GetResult(r => Array[File](r.<<))
Your definition of getFile is manually constructing an Array, and specifically you're asking for an Array[File]. There's no GetResult[File], meaning that r.<< won't be able to convert a column value into a File.
Can anyone tell me how to solve this?
You'll at least need a GetResult[File] defined.
However, it's not clear from the question how the JSON part is intended to work:
Perhaps you have a column containing text which your application treats as JSON. If that's the case, I suggest doing JSON array conversion outside of your Slick code layer. That will keep the mapping to and from the database straightforward.
Or perhaps you have a JSON-type in your database and you're using database-specific operations. In that case, I guess it'll depend on what control you have there, and it probably does make sense to try to do JSON-array operations at the Slick layer. (That's the case for Postgress, for example, via the pg-slick library)
But that's a different question.
As a general note, I suggest always being explicit about the types of GetResult you are defining:
implicit lazy val getFile: GetResult[Array[File]]=
GetResult(r => Array[File](r.<<))
implicit lazy val SenObj: GetResult[Sen] =
GetResult(r => Sen(getFile(r)))
...to be clear about what instances you have available. I find that helps in debugging these situations.

Sorting by DateTime in Slick

I'm currently going through a rough point in Slick. I'm trying to sort the query of a table with a timestamp:
TableName.filter(tableAttribute === 1).sortBy(_.tableTimestamp)
The timestamp is of type joda.DateTime within slick. When I try to sort, I'm getting the following error:
No implicit view available from dao.Tables.profile.api.Rep[org.joda.time.DateTime] => slick.lifted.Ordered.
I'm assuming that this isn't built into Slick. Is there a quick and clean way to add an implicit view and solve this?
Thanks!
You might be looking for an implicit conversion using Ordering.fromLessThan like below:
import org.joda.time.DateTime
implicit def datetimeOrdering: Ordering[DateTime] = Ordering.fromLessThan(_ isBefore _)
In case you want to reverse the ordering, simply replace isBefore with isAfter.

Converting a nested List in using Gson.tojson

I'm using Scala, but this applies to Java as well - it appears Gson is having issues converting a List nested within a case class/within another object:
case class Candy(name:String, price:Double)
case class PersonCandy(name:String, age:Int, candyList:List[Candy])
val candies = List(Candy("M&M's", 1.99), Candy("Snickers", 1.59), Candy("Butterfinger", 2.33))
val personC = PersonCandy("Mark", 19, candies)
val pollAsJson = new Gson().toJson(personC)
The REPL shows the resulting pollAsJson as follows:
pollAsJson: String = {"name":"Mark","age":19,"candyList":{}}
My workaround could be to convert the nested candyList, convert the personC and then since both are just Strings, manually hack them toether, but this is less than ideal. Reading blogs and usages and the like seems that Gson can extract and convert nested inner classes just fine, but when a Collection is the nested class, it seems to have issues. Any idea?
The problem is not related with case classes or nesting, but rather with Scala collection types which are not supported properly in Gson.
To prove that, let's change PersonCandy to
case class PersonCandy(name:String, age:Int, candyList: java.util.List[Candy])
And convert candies to a Java List:
import scala.collection.JavaConverters._
val candies = List(/* same items */).asJava
And the JSON is OK:
{"name":"Mark","age":19,"candyList":[{"name":"M\u0026M\u0027s","price":1.99},{"name":"Snickers","price":1.59},{"name":"Butterfinger","price":2.33}]}
And if you try to produce a JSON for the original candies list, it will produce:
{"head":{"name":"M\u0026M\u0027s","price":1.99},"tl":{}}
Which reflects the head :: tail structure of the list.
Gson is a lib primarily used with Java code.
For Scala, there is a variety of choices. I'd suggest to try some from the answers to this question.
You can use lift-json module to render json strings from case classes. It is a nice easy to use library with a really good DSL to create JSON strings without the need of case classes. You can find more about it at Lift-Json Github page

How to handle JsValue in Scala/Play

I'm trying to learn Scala/Play so I created a sample api that uses WSRequest to connect to GitHub and returns some info based on the user's id. I can convert this response to JsValue by doing:
val response: JsValeu = result.json
Ok(json)
I am having trouble when trying to manipulate the JsValue, for example filter values based on some criteria, etc? Do I need to convert it to a JsObject? I've looked on the Play documentation but I can't figure out how to do this.
What is the approach when handling JsValue?
Thanks
JsValue signifies any sort of JSON data entity, including objects, numbers, strings, etc.
If you want to filter values in a JsObject, then you'll have to "cast" your JsValue into a JsObject. like:
val jsonObject: JsObject = response.as[JsObject]
Then you can mutate the object how you like.
Read the documentation on JsObject and JsValue to find out how to do the rest of what you're trying to do.
https://www.playframework.com/documentation/2.5.x/api/scala/index.html#play.api.libs.json.JsObject
https://www.playframework.com/documentation/2.5.x/api/scala/index.html#play.api.libs.json.JsValue

Scala Slick: MappedColumnType cannot find implicit value for BaseColumlnType[String]

I'm trying to set up database columns in Slick with non-primitive objects. I've spent the past day researching MappedColumnType for mapping custom objects to columns, and as far as I can tell I'm implementing them as people recommend. Unfortunately, the following code produces an error:
implicit val localDateMapper = MappedColumnType.base[LocalDate, String]
(
//map date to String
d => d.toString,
//map String to date
s => LocalDate.parse(s)
)
And here is the error:
could not find implicit value for evidence parameter of type slick.driver.H2Driver.BaseColumnType[String]
I've seen multiple examples where people map custom objects to and from Strings. I figure there must be something I'm missing?
For reference, I'm using Play Slick 1.1.1 and Scala 2.11.6. The former supports Slick 3.1.
You can import a BaseColumnType[String] with:
import slick.driver.H2Driver.api.stringColumnType