Skip to content

Automated schema generation

Flavian Alexandru edited this page Aug 22, 2016 · 5 revisions

Build Status Coverage Status Maven Central Bintray

Keyspace level generation and setting properties for keyspaces is provided separately and we are in the process of porting that to the main DSL, but CQL 3 Table schemas are automatically generated from the Scala code. To create a schema in Cassandra from a table definition:

import scala.concurrent.Await
import scala.concurrent.duration._
import com.websudos.phantom.dsl._

Await.result(ExampleRecord.create().future(), 5000 millis)

Of course, you don't have to block unless you want to. Phantom is capable of handling more advanced scenarios as well, including and not limited to things like creating secondary indexes and initialising them in due time and so on.

More advanced indexing scenarios

Secondary indexes are a non scalable flavour of Cassandra indexing that allows us to query certain columns without needing to duplicate data. They do not scale very well at well, but they remain useful for tables where the predicted cardinality for such records is very small.

That aside, it's worth noting phantom is capable of auto-generating your CQL schema and initialising all your indexes automatically, and this functionality is exposed through the exact same table.create.future().

sealed class IndexedCollectionsTable extends CassandraTable[ConcreteIndexedCollectionsTable, TestRow] {

  object key extends StringColumn(this) with PartitionKey[String]

  object list extends ListColumn[String](this)

  object setText extends SetColumn[String](this) with Index[Set[String]]

  object mapTextToText extends MapColumn[String, String](this) with Index[Map[String, String]]

  object setInt extends SetColumn[Int](this)

  object mapIntToText extends MapColumn[Int, String](this) with Index[Map[Int, String]] with Keys

  object mapIntToInt extends MapColumn[Int, Int](this) with Index[Map[Int, Int]] with Entries

  def fromRow(r: Row): TestRow = {
    TestRow(
      key = key(r),
      list = list(r),
      setText = setText(r),
      mapTextToText = mapTextToText(r),
      setInt = setInt(r),
      mapIntToText = mapIntToText(r),
      mapIntToInt = mapIntToInt(r)
    )
  }
}

Automated initialisation of an entire database

Using Database objects and injecting them into your controllers and other parts where they need to exist is not something that we have designed just for application layering purposes. It is indeed a very powerful feature that you can perfectly encapsulate the scope where a session exists using a Database object, but another very powerful feature is the ability to auto-generate and sync the schema for entire databases with a single method.

Let's have a look at this example taken from the dsl module inside phantom. We use this for testing purposes, and all phantom tests are written to run against this database, which is then injected into all the test suites using a provider trait, namely DatabaseProvider.

To initialise the entire database in a single call, you can use a single call to the autocreate().future() method. If you are using the Twitter API provided via the phantom-finagle module, you can also call autocreate.execute() and get a Twitter future back, provided you have imported the right implicits.

Await.result(database.autocreate.future(), 10.seconds)
class TestDatabase(override val connector: KeySpaceDef) extends DatabaseImpl(connector) {
  object articles extends ConcreteArticles with connector.Connector
  object articlesByAuthor extends ConcreteArticlesByAuthor with connector.Connector

  object basicTable extends ConcreteBasicTable with connector.Connector
  object enumTable extends ConcreteEnumTable with connector.Connector
  object namedEnumTable extends ConcreteNamedEnumTable with connector.Connector
  object clusteringTable extends ConcreteClusteringTable with connector.Connector
  object complexClusteringTable extends ConcreteComplexClusteringTable with connector.Connector
  object brokenClusteringTable extends ConcreteBrokenClusteringTable with connector.Connector
  // ..
}

Specifying custom table creation options during auto-generation

In some instances you may want to override the default query used to create a table, and that could be for the purpose of specifying more advanced options at query creation time.

The default query used by phantom during auto-generation is: database.myTable.create.ifNotExists()

Which will produce the following CQL equivalent: CREATE TABLE IF NOT EXISTS mytable, with no options specified.