Search code examples
scalacassandra-2.0phantom-dsl

How to Implement Cassandra Counter Columns with phantom-dsl?


This is an extension to this question:

How to increment Cassandra Counter Column with phantom-dsl?

This question has also been asked here.

In Thiagos example the two tables; 'songs' & 'songs_by_artist' both have the same rows but with different partitions (primary keys / clustering columns)

CREATE TABLE test.songs (
    song_id timeuuid PRIMARY KEY,
    album text,
    artist text,
    title text
);

CREATE TABLE test.songs_by_artist (
    artist text,
    song_id timeuuid,
    album text,
    title text,
    PRIMARY KEY (artist, song_id)
) WITH CLUSTERING ORDER BY (song_id ASC);

This means inserting, updating and deleting across both tables within the SongsService works with the same base data / rows.

How would you for example have a table such as 'artist_songs_counts', with columns 'song_id' (K) and 'num_songs' (++) and ensure that 'SongsService' adds corresponding row to each table; 'songs' & 'songs_by_artist' & 'artist_songs_counts' (where there are different numbers of row but information should be linked, such as the artist info).

CREATE TABLE test.artist_songs_counts (
    artist text PRIMARY KEY,
    num_songs counter);

Solution

  • SongsService extends ProductionDatabaseProvider that gives to you an object called database where you have access to tables under a certain database:

    /**
        * Find songs by Id
        * @param id
        * @return
        */
      def getSongById(id: UUID): Future[Option[Song]] = {
        database.songsModel.getBySongId(id)
      }
    

    Or even better, handling two tables at the same time:

      /**
       * Save a song in both tables
       *
       * @param songs
       * @return
       */
      def saveOrUpdate(songs: Song): Future[ResultSet] = {
        for {
          byId <- database.songsModel.store(songs)
          byArtist <- database.songsByArtistsModel.store(songs)
        } yield byArtist
      }
    

    Since through a database object you can access all tables that belongs to a specific Database, I would implement a counter for artist the following way:

    def increment(artist: String): Future[ResultSet] = {
      update
        .where(_.artist eqs artist)
        .modify(_.numSongs += 1)
        .future()
    }
    

    Then the saveOrUpdate method could be written as below:

    def saveOrUpdate(song: Song): Future[ResultSet] = {
        for {
          byId <- database.songsModel.store(songs)
          byArtist <- database.songsByArtistsModel.store(songs)
          counter <- database.artistSongsCounter.increment(song.artist)
        } yield byArtist
      }