Search code examples
scalacassandraphantom-dsl

Phantom 1.25.4 with Cassandra


I need help with implementing a model of a notification using phantom and cassandra. What I have done till now:

import java.util.UUID

import com.websudos.phantom.dsl._
import com.websudos.phantom.connectors.Connector
import org.joda.time.DateTime

import scala.concurrent.Future

case class Notification(
  id:           UUID,
  userId:       UUID,
  timestamp:    DateTime,
  read:         Boolean,
  actionUser:   List[String],
  verb:         String,
  itemId:       UUID,
  collectionId: String
)

sealed class NotificationTable extends CassandraTable[NotificationTable, Notification] {

  object id extends UUIDColumn(this) with ClusteringOrder[UUID] with Ascending

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

  object timestamp extends DateTimeColumn(this) with ClusteringOrder[DateTime] with Descending

  object read extends BooleanColumn(this)

  object actionUser extends ListColumn[NotificationTable, Notification, String](this)

  object verb extends StringColumn(this)

  object itemId extends UUIDColumn(this)

  object collectionId extends StringColumn(this)

  def fromRow(row: Row): Notification =
    Notification(
      id(row),
      userId(row),
      timestamp(row),
      read(row),
      actionUser(row),
      verb(row),
      itemId(row),
      collectionId(row)
    )
}

object NotificationTable extends NotificationTable with Connector {

  override def keySpace: String = "test"
  implicit val keyspace: com.websudos.phantom.connectors.KeySpace = com.websudos.phantom.connectors.KeySpace("test")
  def insertItem(item: Notification): Future[ResultSet] =
    insert
      .value(_.id, item.id)
      .value(_.userId, item.userId)
      .value(_.timestamp, item.timestamp)
      .value(_.read, item.read)
      .value(_.actionUser, item.actionUser) 
      .value(_.verb, item.verb)
      .value(_.itemId, item.itemId)
      .value(_.collectionId, item.collectionId)
      .future()
}

Somehow, I have to define two keyspaces, one for RootConnector and one for the insert statement. This is close enough to: this example,. Yet, my code does not compile. I know that they are using an abstract class there and hence it compiles.

My question is how would I go about using that abstract class?? I want to just call the insert statement from another scala source.


Solution

  • You are missing the fact that you are meant to use RootConnector instead of a random Connector trait there. The reason why that class is abstract is because it should only be instantiated inside of a Database object.

    Have a look at this tutorial for more details, but in short, notice the RootConnector mixin here:

    abstract class ConcreteNotificationTable extends
      NotificationTable with RootConnector
    

    And then:

    class MyDatabase(val connector: KeySpaceDef) extends Database(connector) {
      // And here you inject the real session and keyspace in the table
      object notifications extends ConcreteNotificationsTable with connector.Connector
    }
    

    Then you do something like this:

    object MyDatabase extends MyDatabase(ContactPoint.local.keySpace("my_app"))
    

    And from every other source file:

    val notification = Notification(id, //etc...)
    MyDatabase.notifications.insertItem(someNotification)
    

    And even better seperation of concern, as available in the tutorial:

    trait DbProvider extends DatabaseProvider {
      def database: MyDatabase
    }
    trait ProductionDbProvider extends DbProvider {
      // this would now point to your object
      override val database = MyDatabase
    }
    

    Then every single place which needs a database would need to mixin either DbProvider or directly ProductionDbProvider. Read the tutorial for more details, this is not a super trivial topic and all the details are already there.