Skip to content
Keyston edited this page May 12, 2015 · 3 revisions

Rethinkscala tries to retain type saftey while you are writing your quries, so that there is no guess work/casting needed after the returns are returned.

By default all table row results will be wrapped in a special class called Document , which is located under com.rethinkscala.Document. This this class has methods that will allow you to manual extract values from the return set and cast as needed, for example take the giving row stored in our table named "foos".

{
  "id":1,
  "foo":"Bar",
  "index":10
}

One could extract the following results as so:

r.table.get(1).run match{
case Left(e:RethinkError)=>
case Right(d:Document) => println((d \ "index").as[Int]) // outputs 10
}

There are also other methods such as:

// returns your json as a Map[String,Any]
def toMap:Map[String,Any] 

//returns the raw json
def asRaw:String 

//returns either a List[(String,Any)] (in the case the json returned an object) or List[Any] (in the case the query returned an array)
def toList:List[Any]

Casting

Currently rethinkscala doesn't support type-safety for nested objects. So you may need to cast values when working with Var inside functions like map/filter or when accessing nested field values via \ or field(name:String) calls.

For example if you wanted to reproduce the following query

r.table('marvel').get('IronMan').getField('firstAppearance').run(conn)

where "firstAppearance" is of type String , you can do the following

val result:Either[RethinkError,String] = r.table("marvel").get("Ironman").string("firstApperance").run
val result:Either[RethinkError,String] = r.table("marvel").get("Ironman").("firstApperance").mkString.run
val result:Either[RethinkError,String] = r.table("marvel").get("Ironman").field("firstApperance").mkString.run
val result:Either[RethinkError,String] = (r.table("marvel").get("Ironman") \ "firstApperance").mkString.run

Other methods are as follows: When you already have a field reference call one of the following methods to cast

field.toInt
field.toDouble
field.mkString
field.toAnySeq
field.toSeq[T]
field.asMap
field.toMap[T]

When you don't have a field reference already but want to fetch field and cast all in one operation

object.int("field")
object.double("field")
object.float("field")
object.string("field")
object.seq[T]("field")
object.anySeq("field")
object.mapOf[T]("field")
object.anyMap("field")

Casting support doesn't stop at documents, you can also cast the table.

case class Foo(value:String)
var table  = r.table("foo") // Table[Document]
var foos = table.to[Foo] // Table[Foo]
var foos2 = r.tableAs[Foo]("foo") // Table[Foo]

val results:Either[RethinkError,Seq[Foo]] = foos.run 

#Nested Values

Sometimes you may need to cast a value to its apporapite types to prove to scala that a giving condition is valid or to provide type hints prior to executing the query. This normally happens when working with nested results as the driver can't infer its return type as of yet.

Say you have this query in the javascript driver, to rewrite it for Scala you would do the following query.

r.db("notifications").table("conversations").get("962c7fc3-0ed1-4fd6-b4fd-2956e8f0c8aa").update(
  {"messages":r.row("messages").filter(function(msg) {return msg("created").ne(1428641056382)}).insertAt(
    r.row("messages").map(function(msg) {return msg("created")}).indexesOf(1428641056382)(0), 
    r.row("messages").filter({"created": 1428641056382})(0).merge(
      function(msg) { return {"unread": msg("unread").filter(
        function(id) {return id.ne("5465402d49c1ca1800003a40")}
      )}}
    )
  )}
)
val convId ="962c7fc3-0ed1-4fd6-b4fd-2956e8f0c8aa"
val message = 1428641056382L;
var partId = "5465402d49c1ca1800003a40";
convos.get(convId)
  .update(doc => {


  Map("messages" -> doc("messages").filter(msg => msg("created").ne(message)).insertAt(
    doc("messages").toSeq[Message].map(v => v("created").toLong).indexesOf(message)(0).index,

    doc("messages").filter(msg => msg("created").eq(message))(0).merge(
      Map("unread" -> doc("messages").filter(msg =>
        msg("created").eq(message))(0)("unread").filter(id => id.ne(partId))
      )
    )
  ))
})

You will notice that we had to cast to Message to prove our map results. Rethinkscala comes with a class named CanMap[-From, -To <: Typed, Out] that tries to mimics CanBuildFrom[-From, -Elem, +To] . Some common conversion are added to Implictis.scala that are brought into scope when you import Blocking._ ,Blocking.functional._ or Async._.

In this case the implicit def mapDocumentToDouble[T <: AnyRef] = CanMap[T, Numeric, Double] is used to prove the return type. The use of this implicit allows for the compiler to know that the return value is of type Numeric (Int,Double,Long,Float all fall under this) and allows the use of the indexesOf function after the map.

While this class is powerfully not all cases are included by default and if you need to add a custom one its pretty simple to do so

Clone this wiki locally