Search code examples
scalacassandraphantom-dsl

Partial inserts with Cassandra and Phantom DSL


I'm building a simple Scala Play app which stores data in a Cassandra DB using the Phantom DSL driver for Scala. One of the nice features of Cassandra is that you can do partial updates i.e. so long as you provide the key columns, you do not have to provide values for all the other columns in the table. Cassandra will merge the data into your existing record based on the key.

Unfortunately, it seems this doesn't work with Phantom DSL. I have a table with several columns, and I want to be able to do an update, specifying values just for the key and one of the data columns, and let Cassandra merge this into the record as usual, while leaving all the other data columns for that record unchanged.

But Phantom DSL overwrites existing columns with null if you don't specify values in your insert/update statement.

Does anybody know of a work-around for this? I don't want to have to read/write all the data columns every time, as eventually the data columns will be quite large.

FYI I'm using the same approach to my Phantom coding as in these examples:

https://github.com/thiagoandrade6/cassandra-phantom/blob/master/src/main/scala/com/cassandra/phantom/modeling/model/GenericSongsModel.scala


Solution

  • It would be great to see some code, but partial updates are possible with phantom. Phantom is an immutable builder, it will not override anything with null by default. If you don't specify a value it won't do anything about it.

    database.table.update.where(_.id eqs id).update(_.bla setTo "newValue")
    

    will produce a query where only the values you've explicitly set to something will be set to null. Please provide some code examples, your problem seems really strange as queries don't keep track of table columns to automatically add in what's missing.

    Update

    If you would like to delete column values, e.g set them to null inside Cassandra basically, phantom offers a different syntax which does the same thing:

    database.table.delete(_.col1, _.col2).where(_.id eqs id)`
    

    Furthermore, you can even delete map entries in the same fashion:

    database.table.delete(_.props("test"), _.props("test2").where(_.id eqs id)
    

    This assumes props is a MapColumn[Table, Record, String, _], as the props.apply(key: T) is typesafe, so it will respect the keytype you define for the map column.