Skip to content

Collection columns

Flavian Alexandru edited this page Jul 5, 2016 · 8 revisions

Build Status Coverage Status Maven Central Bintray

Cassandra collections do not allow custom data types. Storing JSON as a string is possible, but it's still a text column as far as Cassandra is concerned. The type in the below example is always a default C* type.

Examples on how to use JSON columns can be found in JsonColumnTest.scala

phantom columns Cassandra columns
ListColumn.<type> list<type>
SetColumn.<type> set<type>
MapColumn.<type, type> map<type, type>
JsonColumn.<type> text
JsonListColumn.<type> list<text>
JsonSetColumn.<type> set<type>

JSON columns require you to define a toJson and fromJson method, telling phantom how to go from a String to the type you need. It makes no assumptions as to what library you are using, although we have tested with lift-json and play-json.

An example of a fully working Cassandra table using the whole variety of JSON columns can be found below. It's worth noting that before phantom 1.25.x, any collection column in phantom would need 3 arguments when being defined, which looked something like the below, where you needed to provide both the table type and the record type of the host table, as well as the type of the actual collection:

object listColumn extends ListColumn[TableClass, RecordClass, String](this)

In more recent versions of phantom, collection types consumed at DSL levels have been moved, so now they are type alises nested within Cassandra table. This means we can now the type of the host table and the record it holds without it needing to be provided explicitly.

import com.websudos.phantom.dsl._
import net.liftweb.json.{DefaultFormats, Extraction, JsonParser, compactRender}

case class JsonTest(prop1: String, prop2: String)

case class JsonClass(
  id: UUID,
  name: String,
  json: JsonTest,
  jsonList: List[JsonTest],
  jsonSet: Set[JsonTest]
)


class JsonTable extends CassandraTable[ConcreteJsonTable, JsonClass] {

  implicit val formats = DefaultFormats

  object id extends UUIDColumn(this) with PartitionKey[UUID]

  object name extends StringColumn(this)

  object json extends JsonColumn[JsonTest](this) {
    override def fromJson(obj: String): JsonTest = {
      JsonParser.parse(obj).extract[JsonTest]
    }

    override def toJson(obj: JsonTest): String = {
      compactRender(Extraction.decompose(obj))
    }
  }

  object jsonList extends JsonListColumn[JsonTest](this) {
    override def fromJson(obj: String): JsonTest = {
      JsonParser.parse(obj).extract[JsonTest]
    }

    override def toJson(obj: JsonTest): String = {
      compactRender(Extraction.decompose(obj))
    }
  }

  object jsonSet extends JsonSetColumn[JsonTest](this) {
    override def fromJson(obj: String): JsonTest = {
      JsonParser.parse(obj).extract[JsonTest]
    }

    override def toJson(obj: JsonTest): String = {
      compactRender(Extraction.decompose(obj))
    }
  }

  def fromRow(row: Row): JsonClass = {
    JsonClass(
      id(row),
      name(row),
      json(row),
      jsonList(row),
      jsonSet(row)
    )
  }
}

abstract class ConcreteJsonTable extends JsonTable with RootConnector {
  def store(sample: JsonClass): InsertQuery.Default[ConcreteJsonTable, JsonClass] = {
    insert
      .value(_.id, sample.id)
      .value(_.name, sample.name)
      .value(_.json, sample.json)
      .value(_.jsonList, sample.jsonList)
      .value(_.jsonSet, sample.jsonSet)
  }
}