cassandradatastaxdatastax-java-driver

Invalid ordering value for annotation @ClusteringColumn of column?


I am using Cassandra 2.2.1. and I have a table job_status with the following key:

PRIMARY KEY (job_id, is_complete, last_run_at) WITH CLUSTERING ORDER BY (is_complete ASC, last_run_at DESC)

I have the following java class:

@Table(keyspace = "storakle", name = "import_job_status")
public class JobStatus
{
    @PartitionKey
    @Column(name = "job_id")
    private String jobId;   

    @ClusteringColumn
    @Column(name = "is_complete")
    private boolean isComplete;

    @ClusteringColumn
    @Column(name = "last_run_at")
    private Date lastRunAt;

    @Column(name = "run_number_of_times")
    private int runNumberOfTimes;
}

I would like to query my job_status table via the Mapper class in the Cassandra Java driver like this:

public JobStatus getIncompleteJobStatusById(String jobId)
{
        Mapper<JobStatus> mapper = new   MappingManager(_cassandraDatabaseManager.getSession()).mapper(JobStatus.class);

        boolean isComplete = false;
        JobStatus jobStatus = mapper.get(jobId, isComplete);

        return jobStatus;
}

However I get the following error:

"Invalid ordering value 0 for annotation @ClusteringColumn of column lastRunAt, was expecting 1"

I can see why this is happening. The mapper gets all the primary and clustering keys that are annotated in the JobStatus class and checks upon the call to get whether the supplied keys in the mapper.get method are the same number as the keys in the annotated class.

But I thought in Cassandra it was possible to query a table without specifying all the clustering keys as long as the ones you omit are at the end?

Should I not use the Mapper in this case?


Solution

  • If you check what get on mapper does:

    /** * Fetch an entity based on its primary key. *

    * This method is basically equivalent to: {@code map(getManager().getSession().execute(getQuery(primaryKey))).one()}.

    It is meant to get single entity for mapped class from database, therefore full primary key must be specified.

    As I can understand from your example you would like to see list of job runs based on job id and status sorted by last_run_at which is range query. You can create Datastax accessor interface, add range query and use that from your code.

    @Accessor
    public interface JobStatusAccessor {
    
        @Query("SELECT * FROM import_job_status WHERE job_id = :jobId AND "
            + "is_complete = :isComplete;")
        Statement getJobRunsByStatus(@Param("jobId") String jobId,
            @Param("isComplete") boolean isComplete);
    
    }
    

    And than in your code you can do:

    public JobStatus getIncompleteJobStatusById(String jobId)
    {
            JobStatusAccessor jobStatusAccessor = new   MappingManager(_cassandraDatabaseManager.getSession()).createAccessor(JobStatusAccessor.class);
    
            boolean isComplete = false;
            List<JobStatus> jobRunsByStatus = jobStatusAccessor.getJobRunsByStatus(jobId, isComplete);
    
            return jobStatus.get(0); //here I suppose you need last one
    }
    

    By looking at your model you are storing list of job runs clustered first by status, then by run timestamp. So when you provide job id and status you will get list of job runs so maybe your model is not right (name of mapped class should be JobRun, not JobStatus). If you need only last status of job run, you can remove lastRunAt from clustering key and do upsert which will keep only latest run with status, so you will have for each job id and each status only one entry.