Search code examples
scalaphantom-dsl

ERROR: 'Slice not a member' when implementing Asynchronous-iterators in Phantom-DSL


I am trying to follow the Asynchronous iterators example from the wiki

Am getting the following error:

value slice is not a member of play.api.libs.iteratee.Enumerator

Any input into what the issue is greatly appreciated.


This is so that I can paginate results from a large collection


libraryDependencies ++= Seq(
  "com.websudos" %% "phantom-dsl" % 1.22.0,
  "com.websudos" %% "phantom-reactivestreams" % 1.22.0 
)

import com.datastax.driver.core.{ResultSet, Row}
import com.websudos.phantom.CassandraTable
import com.websudos.phantom.dsl._
import com.websudos.phantom.iteratee.Iteratee
import org.dyne.danielsan.openblockchain.data.entity.Block

import scala.concurrent.Future
import com.websudos.phantom.reactivestreams._
import scala.concurrent.Await
import scala.concurrent.duration._

sealed class BlocksModel extends CassandraTable[BlocksModel, Block] {

  override def fromRow(row: Row): Block = {
    Block(
      hash(row),
      height(row)
}

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

  object height extends IntColumn(this) with ClusteringOrder[Int] with Descending

  object order_id extends LongColumn(this) with ClusteringOrder[Long] with Descending

abstract class ConcreteBlocksModel extends BlocksModel with RootConnector {

  override val tableName = "blocks"

  def getBlocks(start: Int, limit: Int): Future[Set[Block]] = {
    select.fetchEnumerator.slice(start, limit).collect
  }
}

Solution

  • Slightly wrong syntax, when you want to use methods on enumerator here's what you need:

    abstract class ConcreteBlocksModel extends BlocksModel with RootConnector {
    
      override val tableName = "blocks"
    
      def getBlocks(start: Int, limit: Int): Future[Iterator[Block]] = {
        select.fetchEnumerator run Iterator.slice(start, limit)
      }
    }
    

    If you want to paginate your records, there's a different way to do that in Cassandra with automated pagination though.

    def getPage(limit: Int, paging: Option[PagingState] = None): Future[ListResult[JodaRow]] = {
      select.limit(limit).fetchRecord(paging)
    }
    

    And basically you need to provide a paging state for the next query to run. The Future now being returned will have a ListResult item inside of it, which means you get 2 methods:

    def records: List[R] // the list of records from the db, just like fetch()
    def pagingState: PagingState // the state you care about.
    

    Basically pagingState has a toString method which will give you back a token you will need to store on the client side. When the user wants to get "the next page", you need to provide the pagingState string from the previous page, think of the paging state as a pointer to a particular location in the Cassandra table, so that way Cassandra knows how to "jump" or "skip pages".

    So your next API call, assuming you start from page 0, should include a pagingState as a string.

    You can then you PagingState.fromString(pagingState) and pass the result of this to get the "next page".

    I will add an example of this in phantom, but this should basically solve your current problem.