Row Extraction

Cassandra’s Row holds the response from a statement. Using the driver, conversion into a useful Scala data type is cumbersome both in extracting a value from the Row, and converting it from the Java type. Scala-Cass handles all of that under the hood.

As an example, start with a Row retrieved from Cassandra table. Let’s say this table has a definition of

scala> case class MyRow(s: String, i: Int, l: List[Long]) // s varchar, i int, l bigint
defined class MyRow

Then we select a row using a ScalaSession (see more of select here)

scala> case class Select(s: String)
defined class Select

scala> val rowRes = sSession.selectOneStar("mytable", Select("a str")).execute()
rowRes: com.weather.scalacass.Result[Option[com.datastax.driver.core.Row]] = Right(Some(Row[a str, 1234, [5678]]))

scala> val row: Row = rowRes.toOption.flatten.get 
row: com.datastax.driver.core.Row = Row[a str, 1234, [5678]]

First, let’s extract into MyRow using the regular driver

scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._

scala> val driverDerivedRow = MyRow(row.getString("s"), row.getInt("i"), row.getList("l", classOf[java.lang.Long]).asScala.toList.map(Long.unbox))
driverDerivedRow: MyRow = MyRow(a str,1234,List(5678))

Especially for the List[Long], it is unnecessarily verbose, since we already have the necessary type information. In addition, we don’t know if any of these values came back as null, so in truth, we would need null checks as well

We can hide all of this boilerplate using the ScalaCass library. First, we need to import the syntax, com.weather.scalacass.syntax. Then let’s mirror that extraction from above

scala> import com.weather.scalacass.syntax._
import com.weather.scalacass.syntax._

scala> val scalaCassDerivedRow = MyRow(row.as[String]("s"), row.as[Int]("i"), row.as[List[Long]]("l"))
scalaCassDerivedRow: MyRow = MyRow(a str,1234,List(5678))

All we need to specify is the type that we want*, and the library handles the rest. If one of these values came back null, the driver will throw an exception since we do not want to introduce null values into our code.

If you do need to handle null types, use the Option type to extract values, as this will return None instead of null

scala> row.as[Option[String]]("s")
res3: Option[String] = Some(a str)

There are 2 convenience functions around this, getAs and getOrElse, that retrieves an Optional response of the type, and, in the case of getOrElse, provides a default in the case it gets back a None

scala> row.getAs[Int]("i")
res5: Option[Int] = Some(1234)

scala> row.getOrElse[Int]("i", -12345)
res6: Int = 1234

If you want to handle the exception yourself, there is a way to simply attempt the extraction and return either the success or the failure

scala> row.attemptAs[Int]("i")
res8: com.weather.scalacass.Result[Int] = Right(1234)

Case Classes

We already have a conveniently defined MyRow with all of the type information we want, so the Scala-Cass library (with the help of the Shapeless library) can automatically use the case class directly for extraction

scala> row.as[MyRow]
res9: MyRow = MyRow(a str,1234,List(5678))

scala> row.getAs[MyRow]
res10: Option[MyRow] = Some(MyRow(a str,1234,List(5678)))

scala> row.getOrElse[MyRow](MyRow("default row", -12345, List(-5678L)))
res11: MyRow = MyRow(a str,1234,List(5678))

scala> row.attemptAs[MyRow]
res12: com.weather.scalacass.Result[MyRow] = Right(MyRow(a str,1234,List(5678)))

Note that no arguments (aside from the type parameter) are passed when extracting to a case class because you are acting on the entire row.

For performance characteristics of these extractions, see the performance page
For a full list of type mappings between the Cassandra types and Scala types, see the type mappings page