package com.dekayd.astyanax.cassandra.widerow;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import com.dekayd.astyanax.cassandra.Cassandra;
import com.google.common.collect.ImmutableMap;
import com.netflix.astyanax.ColumnListMutation;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.Serializer;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.model.Column;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.ColumnList;
import com.netflix.astyanax.serializers.SerializerTypeInferer;
/**
* Extent this class to create a wide row table. Has convenience methods for saving and querying columns
* @param <Ky> Row key type (UUID, String, Long, etc)
* @param <Col> Column key type (UUID, String, Long, etc)
* @param <Val> Column value type (UUID, String, Long, etc)
*/
abstract public class BaseWideRow<Ky, Col, Val> implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(BaseWideRow.class);
@Autowired
protected Cassandra cassandra;
protected ColumnFamily<Ky, Col> columnFamily;
protected Class<Col> column;
protected Class<Ky> key;
protected Class<Val> value;
/**
* Constructor requires the classes of the types for you key, column key, and column value to workaround type erasure
* @param key Class of your row key type
* @param column Class of your column key type
* @param value Class of your column value type
*/
public BaseWideRow(Class<Ky> key,Class<Col> column,Class<Val> value) {
this.key = key;
this.column = column;
this.value = value;
}
public Keyspace getKeyspace() {
return cassandra.getKeyspace();
}
protected Serializer<Ky> getKeySerializer(){
return SerializerTypeInferer.getSerializer(key);
}
protected Serializer<Col> getColumnSerializer(){
return SerializerTypeInferer.getSerializer(column);
}
protected Serializer<Val> getValueSerializer(){
return SerializerTypeInferer.getSerializer(value);
}
protected String getColumnFamilyName() {
return this.getClass().getSimpleName().toLowerCase();
}
public void afterPropertiesSet() {
createColumnFamily();
createTable();
}
/**
* Convenience method for querying a single column of a single row
* @param rowKey The row key
* @param column The column key
* @return A WideRowValue containing the row key, column key, and column value
* @throws ConnectionException
*/
public WideRowValue<Ky, Col, Val> getSingleColumn(Ky rowKey, Col column) throws ConnectionException{
WideRowValue<Ky, Col, Val> wrv = new WideRowValue<Ky, Col, Val>().setKey(rowKey).setColumn(column);
Column<Col> result = getKeyspace().prepareQuery(getColumnFamily())
.getKey(rowKey)
.getColumn(column)
.execute().getResult();
wrv.setValue(getColumnValue(result));
return wrv;
}
protected Val getColumnValue(Column<Col> result) {
return result.getValue(getValueSerializer());
}
/**
*
* @param key The row key
* @return A Collection of of WideRowValues representing the entire row
* @throws ConnectionException
*/
public Collection<WideRowValue<Ky, Col, Val>> getEntireRow(Ky key) throws ConnectionException{
log.info("Getting row for key: {}", key);
Collection<WideRowValue<Ky, Col, Val>> values = new ArrayList<WideRowValue<Ky, Col, Val>>();
ColumnList<Col> result = getKeyspace().prepareQuery(getColumnFamily())
.getKey(key)
.execute().getResult();
if (!result.isEmpty()) {
Iterator<Column<Col>> it = result.iterator();
while(it.hasNext()) {
Column<Col> column = it.next();
WideRowValue<Ky, Col, Val> wrv = new WideRowValue<Ky, Col, Val>()
.setKey(key)
.setColumn(column.getName())
.setValue(getColumnValue(column));
values.add(wrv);
}
}
return values;
}
/**
* @param key
* @param column
* @param value
* @throws ConnectionException
*/
public void saveValue(Ky key, Col column, Val value) throws ConnectionException{
getKeyspace().prepareColumnMutation(getColumnFamily(), key, column)
.putValue(value, getValueSerializer(), null)
.execute();
}
/**
* @param wrv
* @throws ConnectionException
*/
public void saveValue(WideRowValue<Ky, Col, Val> wrv) throws ConnectionException {
saveValue(wrv.getKey(), wrv.getColumn(), wrv.getValue());
}
/**
* For saving batches of values.
* @param values
* @throws ConnectionException
*/
public void saveValues(Collection<WideRowValue<Ky, Col, Val>> values) throws ConnectionException {
Map<Ky, Collection<WideRowValue<Ky, Col, Val>>> grouped = WideRowUtil.groupWideRowValuesByKey(values);
saveValues(grouped);
}
/**
* For saving batches of values grouped by row key
* @param grouped
* @throws ConnectionException
*/
public void saveValues(Map<Ky, Collection<WideRowValue<Ky, Col, Val>>> grouped)throws ConnectionException {
MutationBatch m = getKeyspace().prepareMutationBatch();
for(Ky kee: grouped.keySet()) {
Iterable<WideRowValue<Ky, Col, Val>> itb = grouped.get(kee);
ColumnListMutation<Col> clm = m.withRow(getColumnFamily(), kee);
Iterator<WideRowValue<Ky, Col, Val>> it = itb.iterator();
while(it.hasNext()) {
WideRowValue<Ky, Col, Val> value = it.next();
putColumn(clm, value);
}
}
m.execute();
}
protected void putColumn(ColumnListMutation<Col> clm, WideRowValue<Ky, Col, Val> value) {
clm.putColumn(value.getColumn(), value.getValue(), getValueSerializer(), null);
}
public void saveValuesWithUniqueKeys(Map<Ky, WideRowValue<Ky, Col, Val>> values)throws ConnectionException {
MutationBatch m = getKeyspace().prepareMutationBatch();
for(Ky kee: values.keySet()) {
WideRowValue<Ky, Col, Val> value = values.get(kee);
ColumnListMutation<Col> clm = m.withRow(getColumnFamily(), kee);
putColumn(clm, value);
}
m.execute();
}
protected void createColumnFamily() {
columnFamily = new ColumnFamily<Ky, Col>(getColumnFamilyName(), getKeySerializer(), getColumnSerializer());
}
/**
* Creates the column family if it doesn't already exist
*/
protected void createTable() {
try {
getKeyspace().createColumnFamily(getColumnFamily(), getCFMetadata());
}catch(Exception e) {
log.info("Could not create wide row table for {} message: {}", getColumnFamilyName(), e.getMessage());
}
}
protected ImmutableMap<String, Object> getCFMetadata() {
return ImmutableMap.<String, Object>builder()
.put("default_validation_class", getValueSerializer().getComparatorType().getClassName())
.build();
}
public ColumnFamily<Ky, Col> getColumnFamily() {
return columnFamily;
}
public void setColumnFamily(ColumnFamily<Ky, Col> columnFamily) {
this.columnFamily = columnFamily;
}
public Class<Col> getColumn() {
return column;
}
public void setColumn(Class<Col> column) {
this.column = column;
}
public Class<Ky> getKey() {
return key;
}
public void setKey(Class<Ky> key) {
this.key = key;
}
public Class<Val> getValue() {
return value;
}
public void setValue(Class<Val> value) {
this.value = value;
}
}