I can't figure out how to make all the range queries i need using Astyanax. I've tried what's explained here but it does not seem to work.
FYI I'm coming from the Hector world where i got this all working nicely and for reasons i won't explain, we decided to switch to Astyanax.
I have a column family described as follow. Note the "ReversedType" that allows us to have the latest TimeUUID first. It shouldn't impact the rest, at leat it never did with Hector:
ColumnFamily: mycf
Key Validation Class: org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.UTF8Type)
Default column value validator: org.apache.cassandra.db.marshal.UTF8Type
Columns sorted by: org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UTF8Type,org.apache.cassandra.db.marshal.ReversedType(org.apache.cassandra.db.marshal.TimeUUIDType))
And here's it's definition as an Astyanax CF:
public static final CF = new ColumnFamily<Rowkey, Column>(
NAME,
new AnnotatedCompositeSerializer<Rowkey>(Rowkey.class),
new AnnotatedCompositeSerializer<Column>(Column.class),
StringSerializer.get()
);
public static class Column {
@Component(ordinal = 0)
public String colname = null;
@Component(ordinal = 1)
public UUID timeUUID = null;
}
public static class Rowkey {
@Component(ordinal = 0)
public String aid;
@Component(ordinal = 1)
public String cid;
}
I use to be able to do a query using partial composites as described here. What i really need is to be able to do the following range queries:
# Range 1
# Should get the 10 first elements of the rowkey
colstart: null
colend: null
limit: 10
# Range 2
# Should get the 10 first elements of the rowkey
# that has as first part of the composite the string "mycol"
colstart: Column("mycol", null)
colend: Column("mycol", null)
limit: 10
# Range 3
# Should get the 10 first elements of the rowkey
# that has as first part of the composite the string "mycol"
# and with a timeuuid created with a timstamp between 'timestampStart' and 'timestampEnd'
colstart: Column("mycol", TimeUUID(timestampStart))
colend: Column("mycol", TimeUUID(timestampEnd))
limit: 10
# Range 4, not actually a range
# Should get the 1 column composed of "mycol" and existingTimeUUID
colstart: Column("mycol", existingTimeUUID)
colend: Column("mycol", existingTimeUUID)
limit: 10
Here's the 3 ways i've tried:
# Code 1
keyspace.prepareQuery(columnFamily)
.getKey(rowkey)
.withColumnRange(columnStart, columnEnd, false, 10)
.execute()
.getResult();
# Code 2
RangeBuilder rangeBuilder = new RangeBuilder()
.setStart(columnStart, CF.getColumnSerializer())
.setEnd(columnEnd, CF.getColumnSerializer())
.setReversed(false)
.setLimit(10);
keyspace.prepareQuery(columnFamily)
.getKey(rowkey)
.withColumnRange(rangeBuilder.build())
.execute()
.getResult();
# Code 3
CompositeRangeBuilder rangeBuilder = ((AnnotatedCompositeSerializer<Column>) colSerializer)
.buildRange()
.withPrefix(columnStart.colname)
.greaterThanEquals(columnStart.timeUUID)
.lessThanEquals(columnEnd.timeUUID)
.limit(10);
keyspace.prepareQuery(columnFamily)
.getKey(rowkey)
.withColumnRange(rangeBuilder.build())
.execute()
.getResult();
Based on my Hector background, the composite i give (Annotated Classes) miss the EQUALITY parameter.
Code 3
wouldn't do exactly what i need as the prefix applies to both start and end and those may be different if i wanted, for intance, to range query from (col1, timUUID1) to (col2, TimeUUID2).
I managed to make Range 4
work with all three codes easely.
I managed to make Range 1
work with all three codes by doing so:
Code 1
i gave null as param for start and endCode 2
i avoided calling setStart and setEndCode 3
i avoided calling withPrefix or any equality methode.I managed to make Range 2
work only with Code 3
:
greaterThanEquals(columnStart.colname)
or lessThanEquals(columnStart.colname)
and no other equality/prefix methodI did not manage to make Range 3
at all.
Clearely, i would like to use only Code 1
as I actually don't know the column family i will be querying. Here i'm just using an example with a composite column but it may not be composite or have more than 2 fields.
In hector it's really as easy as this and i would like some equivalent:
MultigetSliceQuery<K, C, V> q = HFactory.createMultigetSliceQuery(connection.getKeyspace(), this.getRowKeySerializer(), this.getColumnNameSerializer(), this.getColumnValueSerializer());
q.setColumnFamily(this.getCfName());
q.setKeys(keys)
q.setRange(colStart, colEnd, reversed, count);
q.execute();
With colStart and colEnd beeing of any type and if composite, allowing partial composites with an Equality defined on each component.
Thanks for your help.
I wasn't able to do all i wanted using the above methods but i found a 4th one that even though it doesn't please me, works.
I'm using the ByteBuffer
version of withColumnRange
in Code 1
. The start and end buffer are built using the actual Composite class as follows:
public static <T> ByteBuffer toRangeStart(List<Object> list, boolean reversed) {
return toRangeValue(list, reversed ? ComponentEquality.GREATER_THAN_EQUAL : ComponentEquality.EQUAL);
}
public static <T> ByteBuffer toRangeEnd(List<Object> list, boolean reversed) {
return toRangeValue(list, reversed ? ComponentEquality.EQUAL : ComponentEquality.GREATER_THAN_EQUAL);
}
public static <T> ByteBuffer toRangeValue(List<Object> list, ComponentEquality eq) {
// We use a real composite as we didn't find any other way to build those ranges
Composite c = new Composite();
int i;
for (i = 0; i < list.size() - 1; i++) {
c.addComponent(list.get(i), SerializerTypeInferer.getSerializer(list.get(i)), ComponentEquality.EQUAL);
}
c.addComponent(list.get(i), SerializerTypeInferer.getSerializer(list.get(i)), eq);
return c.serialize();
}