Cassandra BoundStatement with multiple parameters and a partitioned query
After reading the article Asynchronous Queries with Java Driver on the datastax blog, I was trying to implement a solution similar to Example: Multi-Segment Query, aka Client Side SELECT ... IN.
I currently have some code that looks something like this:
public Future<List<ResultSet>> executeMultipleAsync(final BoundStatement statement, final Object... partitionKeys) {
List<Future<ResultSet>> futures = Lists.newArrayListWithExpectedSize(partitionKeys.length);
for (Object partitionKey : partitionKeys) {
Statement bs = statement.bind(partitionKey);
futures.add(executeWithRetry(bs));
}
return Futures.successfulAsList(futures);
}
But I would like to improve this. In the cql request this BoundStatement takes place, I would like to have something similar to this:
SELECT * FROM <column_family_name> WHERE <param1> = :p1_name AND param2 = :p2_name AND <partiotion_key_name> = ?;
I would like clients of this method to give me a BoundStatement with parameters already bound (in this case two parameters) and a list of section keys. In this case, all I have to do is bind the section keys and execute queries. Unfortunately, when I bind a key to this statement, I fail with the error - com.datastax.driver.core.exceptions.InvalidTypeException: Invalid type for value 0 of CQL type varchar, expecting class java.lang.String but class java.lang.Long provided
. The problem is I am trying to bind the key to the first parameter and not the last one. This is not a long line.
I can solve this by specifying the section parameter name, but then I would need to get the name through the method parameters or by specifying its index, which again will require an additional method parameter. Anyway, if I use a name or an index, I have to bind it to a specific type. For example: bs.setLong("<key_name>", partitionKey);
. For some reason, I cannot leave it in the BoundStatement to interpret the type of the last parameter.
I would like me to explicitly not omit the parameter name and work around the type issue. Is there something that can be done?
Thank!
source to share
I posted the same question in "Java DataStax Driver for Apache Cassandra User Mailing List" and got a response stating functionality that I am missing may be added in the next version (2.2) of the java datastax driver.
In JAVA-721 (which will be introduced in 2.2) we are tentatively planning to add the following signature methods to the BoundStatement:
public BoundStatement setObject (int i, V v) public BoundStatement setObject (string name, V v)
and
You can emulate setObject in 2.1:
void setObject(BoundStatement bs, int position, Object object,
ProtocolVersion protocolVersion) {
DataType type = bs.preparedStatement().getVariables().getType(position);
ByteBuffer buffer = type.serialize(object, protocolVersion);
bs.setBytesUnsafe(position, buffer);
}
To avoid passing the parameter name, one thing you could do is look for a position that is not already constrained:
int findUnsetPosition(BoundStatement bs) { int size = bs.preparedStatement().getVariables().size(); for (int i = 0; i < size; i++) if (!bs.isSet(i)) return i; throw new IllegalArgumentException("found no unset position"); }
I don't recommend it because it is ugly and unpredictable if the user forgot to bind one of the non-PK variables.
The way I would do it requires the user to pass in a callback that sets the PK:
interface PKBinder<T> { void bind(BoundStatement bs, T pk); } public <T> Future<List<ResultSet>> executeMultipleAsync(final BoundStatement statement, PKBinder<T> pkBinder, final T...
partitionKeys)
As a bonus, this will also work with compound partition keys.
source to share