Cassandra BoundStatement with Multiple Parameters and Multi-Partition Query

730 views
Skip to first unread message

Anatoly Rabinovich

unread,
Jul 20, 2015, 10:17:05 AM7/20/15
to java-dri...@lists.datastax.com
Hi,

I've run into a problem when when binding parameters to a BoundStatement. I've posted this question on stackoverflow but didn't get any response. So, I'm trying here now.
After reading "Asynchronous queries with the Java driver" article in the datastax blog, I was trying to implement a solution similar to the one in the section called - 'Case study: multi-partition query, a.k.a. “client-side SELECT...IN“'.

I currently have 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'd like to improve on that. In the cql query this BoundStatement holds, I'd like to have something that looks like this:
SELECT * FROM <column_family_name> WHERE <param1> = :p1_name AND param2 = :p2_name AND <partiotion_key_name> = ?;

I'd like the clients of this method to give me a BoundStatement with an already bound parameters (two parameters in this case) and a list of partition keys. In this case, all I need to do, is bind the partition keys and execute the queries. Unfortunately, when I bind the key to this statement I fail with an 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, that I try to bind the key to the first parameter and not the last. Which is a string and not a long.
I can solve this by either giving the partition parameter a name but then I'd have to get the name via method parameters, or by specifying it's index which again will require an additional method parameter. Either way, if I use the name or the index I have to bind it with a specific type. For instance: `bs.setLong("<key_name>", partitionKey);`. For some reason, I can't leave it to the BoundStatement to interpret the type of the last parameter.
I'd like to avoid passing the parameter name explicitly and bypass the type problem. Is there anything that can be done?

Thanks!

Andrew Tolbert

unread,
Jul 20, 2015, 12:38:19 PM7/20/15
to java-dri...@lists.datastax.com
Hi Anatoly,

In JAVA-721 (to be introduced in 2.2) we are tentatively planning on adding the following methods with the signature to BoundStatement:

public <V> BoundStatement setObject(int i, V v)
public <V> BoundStatement setObject(String name, V v)

Which I believe would solve your particular problem as you could simply provide the index/name of the column you wish to set without having to know the type information at the time.

However, I don't think I am completely understanding your question.  Are you providing statements that map to different tables?  Is this why you are not sure what 'type' you are looking to set for a column?  Is the goal to be able to execute a bunch of different queries that follow some similar query structure (but not the exact same query) concurrently?  In that case it might be better to have your the callers of the method create and bind the statements previously and call the method, so the method is only responsible for executing the queries and binding them together, i.e.

public Future<List<ResultSet>> executeMultipleAsync(final BoundStatement... statements)

However when you do this and get your list of results back, it will not be trivial to correlate which results map to which query which is problematic.

Regards,
Andy

Anatoly Rabinovich

unread,
Jul 21, 2015, 2:02:51 AM7/21/15
to java-dri...@lists.datastax.com
Hello Andrew,

Thank you for the prompt answer. It seems that indeed the methods that your going to introduce in version 2.2 are going to solve my problem.
You are also right regarding your second assumption, I am trying to give the user the ability to create different statements that map to different tables that follow similar query structure. But I'd like to abstract away from the client the need to create these multiple queries and the way the partition keys (which, from the point of view of the client, are just ids of the data it's trying to retrieve) are bound to the statement. What I'm saying is, that I'm trying give the user the 'feeling' of executing a regular 'IN' statement.

Olivier Michallat

unread,
Jul 21, 2015, 6:49:01 AM7/21/15
to java-dri...@lists.datastax.com
Hi,

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 isn't bound yet:

    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 though, because it's ugly and unpredictable if the user forgot to bind one of the non-PK variables.

The way I would do it is require the user to pass 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 composite partition keys.


--

Olivier Michallat

Driver & tools engineer, DataStax


To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-us...@lists.datastax.com.

Anatoly Rabinovich

unread,
Jul 26, 2015, 8:35:49 AM7/26/15
to DataStax Java Driver for Apache Cassandra User Mailing List, olivier....@datastax.com
Thanks for the help. But I think I prefer waiting for version 2.2 of the driver. Thanks!

Kevin Gallardo

unread,
Jul 27, 2015, 5:16:49 AM7/27/15
to java-dri...@lists.datastax.com
Hi, 

Please note that the final API for this feature will be :

public <V> BoundStatement set(int i, V v)

Instead of :

public <V> BoundStatement setObject(int i, V v)

Thanks.
--
Kevin Gallardo, 
Drivers and Tools Team
DataStax.

Anatoly Rabinovich

unread,
Aug 3, 2015, 8:50:02 AM8/3/15
to DataStax Java Driver for Apache Cassandra User Mailing List
Thank you!

Anatoly Rabinovich

unread,
Aug 31, 2015, 10:02:59 AM8/31/15
to DataStax Java Driver for Apache Cassandra User Mailing List
I see that version 2.2.0-rc3 is available. Is it stable enough for production usage? When is version 2.2 coming out?

Thanks!

On Monday, July 27, 2015 at 12:16:49 PM UTC+3, Kevin Gallardo wrote:
Reply all
Reply to author
Forward
0 new messages