Search code examples
scalacassandraphantom-dsl

Phantom-DSL cassandra with frozen type


I'm trying to map a column which is a Map with frozen type

My column family has a field

batsmen_data map<text, frozen<bat_card>>

bat_card has two fields

 bat_id int,
 bat_name text,

Map the column field

object batsmenData extends MapColumn[ScoreCardData, ScoreCard, String ,Batting](this) {
    override lazy val name="batsmen_data"
}   

This is not an ideal way to do it. Because MapColumn Supports only primitive types. Can any one help me out on how to create a UDT Columns


Solution

  • I've figured out a way to map the UDT using phantom. But not sure this is right way.

    I have a column

    batsmen_data map<text, frozen<bat_card>>
    

    To map this, we write the below code

    object batsmenData extends  MapColumn[ScoreCardData, ScoreCard, String, Batting](this) with CustomPrimitives {
            override lazy val name="batsmen_data"
    }
    

    When you compile, error will be shown No Primitive found for class Batting

    This is because Phantom has defined Primitives for native types like String, Int etc. To avoid you have to define primitive for class Batting as shown below(You have to extend CustomPrimitives trait in the class, else you will get the same error).

    trait CustomPrimitives extends Config {
        implicit object BattingPrimitive extends Primitive[Batting]{
    
            override type PrimitiveType = Batting
    
            override def clz: Class[Batting] = classOf[Batting]
    
            override def cassandraType: String = {
                Connector.session.getCluster.getMetadata.getKeyspace(cassandraKeyspace).getUserType("bat_card").toString()
            }
             override def fromRow(column: String, row: dsl.Row): Try[Batting] = ???
            override def fromString(value: String): Batting = ???
            override def asCql(value: Batting): String = ???
        }
    

    After this one more error will be shown saying Codec not found for requested operation: [frozen <-> Batting]. This is because cassandra expects a codec for the custom UDT type to serialize and deserialze the data. To Avoid this you have to write a CodecClass which will help to deserialize(as i need only deserializing) the UDT value into custom object.

    public class BattingCodec extends TypeCodec<Batting>{
        protected BattingCodec(TypeCodec<UDTValue> innerCodec, Class<Batting> javaType)  {
            super(innerCodec.getCqlType(), javaType);
        }
    
        @Override
        public ByteBuffer serialize(Batting value, ProtocolVersion protocolVersion) throws InvalidTypeException {
            return null;
        }
    
        @Override
        public Batting deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) throws InvalidTypeException {
             return null;
        }
    
        @Override
        public Batting parse(String value) throws InvalidTypeException {
            return null;
        }
    
        @Override
        public String format(Batting value) throws InvalidTypeException {
            return null;
        }
    }
    

    Once the codec is defined last step is to register this codec into codec registry.

    val codecRegistry = CodecRegistry.DEFAULT_INSTANCE
    val bat_card = Connector.session.getCluster.getMetadata.getKeyspace(cassandraKeyspace).getUserType("bat_card")
                val batCodec = new BattingCodec(TypeCodec.userType(bat_card), classOf[Batting])
                codecRegistry.register(batCodec)
    

    Now using deserialize function in BattingCodec, we can map the bytes to required object.

    This method is working fine. But i'm not sure this is the ideal way to achieve the UDT functionality using Phantom