DATACASS-56 - Cassandra paging support.

We now support forward-only paging with Cassandra through the Template API and Repositories. Results in Cassandra are paged by navigating forward-only through pages described by a binary paging state encapsulated by CassandraPageRequest and accessible via the returned Slice. Spring Data Page's do not fit to Cassandra's paging concept because Cassandra paging is not based on limit/offset.

Page requests are applicable to a Query and as parameter of query methods.

Query query = Query.empty().pageRequest(CassandraPageRequest.first(10));
Slice<User> slice = template.slice(query, User.class);

do {

    // consume slice

    if (slice.hasNext()) {
        slice = template.select(query, slice.nextPageable(), User.class);
    } else {
        break;
    }
} while (!slice.getContent().isEmpty());

assertThat(ids).hasSize(100);
assertThat(iterations).isEqualTo(10);

interface UserRepository implements Repository<User, String> {

    Slice<User> findAllByName(String name, Pageable pageRequest);
}
This commit is contained in:
Mark Paluch
2017-09-01 13:00:18 +02:00
committed by John Blum
parent 62e6d33314
commit 90bfa5317b
23 changed files with 990 additions and 176 deletions

View File

@@ -111,7 +111,7 @@ class ApplicationConfig extends AbstractCassandraConfiguration {
As our domain Repository extends `CrudRepository` it provides you with basic CRUD operations.
Working with the Repository instance is just a matter of injecting the Repository as a dependency into a client.
.Paging access to Person entities
.Basic access to Person entities
====
[source,java]
----
@@ -131,6 +131,35 @@ public class PersonRepositoryTests {
----
====
Cassandra repositories support paging and sorting for paginated and sorted access to the entities. Cassandra paging requires a paging state to forward-only navigate through pages. A `Slice` keeps track of the current paging state and allows creation of a `Pageable` to request the next page.
.Paging access to Person entities
====
[source,java]
----
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class PersonRepositoryTests {
@Autowired PersonRepository repository;
@Test
public void readsPagesCorrectly() {
Slice<Person> firstBatch = repository.findAll(CassandraPageRequest.first(10));
assertThat(firstBatch).hasSize(10);
Page<Person> nextBatch = repository.findAll(firstBatch.nextPageable());
// …
}
}
----
====
NOTE: Cassandra repositories do not extend `PagingAndSortingRepository` because classic paging patterns using limit/offset are not applicable to Cassandra.
The sample creates an application context with Spring's unit test support, which will perform annotation-based
dependency injection into the test class. Inside the test cases (test methods) we simply use the Repository to query
the data store. We invoke the Repository query method that requests the all `Person` instances.
@@ -147,26 +176,30 @@ the Apache Cassandra database. Defining such a query is just a matter of declari
----
public interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByLastname(String lastname); <1>
List<Person> findByLastname(String lastname); <1>
List<Person> findByFirstname(String firstname, Sort sort); <2>
Slice<Person> findByFirstname(String firstname, Pageable pageRequest); <2>
List<Person> findByFirstname(String firstname, QueryOptions opts); <3>
List<Person> findByFirstname(String firstname, QueryOptions opts); <3>
Person findByShippingAddress(Address address); <4>
List<Person> findByFirstname(String firstname, Sort sort); <4>
Stream<Person> findAllBy(); <5>
Person findByShippingAddress(Address address); <5>
Stream<Person> findAllBy(); <6>
>>>>>>> 18c314d... DATACASS-56 - Cassandra paging support.
}
----
<1> The method shows a query for all people with the given `lastname`. The query will be derived from parsing
the method name for constraints which can be concatenated with `And`. Thus the method name will result in
a query expression of `SELECT * from person WHERE lastname = 'lastname'`.
<2> Applies dynamic sorting to a query. Just add a `Sort` parameter to your method signature and Spring Data.
will automatically apply ordering to the query accordingly.
<2> Applies pagination to a query. Just equip your method signature with a `Pageable` parameter and let the method return a `Slice` instance and we will automatically page the query accordingly.
<3> Passing a `QueryOptions` object will apply the query options to the resulting query before it's execution.
<4> Shows that you can query based on properties which are not a primitive type using registered `Converter`'s.
<4> Applies dynamic sorting to a query. Just add a `Sort` parameter to your method signature and Spring Data
will automatically apply ordering to the query accordingly.
<5> Shows that you can query based on properties which are not a primitive type using registered `Converter`'s
in `CustomConversions`.
<5> Uses a Java 8 `Stream` which reads and converts individual elements while iterating the stream.
<6> Uses a Java 8 `Stream` which reads and converts individual elements while iterating the stream.
====
NOTE: Querying non-primary key properties requires secondary indexes.

View File

@@ -1137,6 +1137,7 @@ The `Query` class has some additional methods used to provide options for the qu
* `Query` *and* `(CriteriaDefinition criteria)` used to add additional criteria to the query.
* `Query` *columns* `(Columns columns)` used to define columns to be included in the query results.
* `Query` *limit* `(long limit)` used to limit the size of the returned results to the provided limit (used for paging).
* `Query` *pageRequest* `(Pageable pageRequest)` used to associate `Sort`, `PagingState` and `fetchSize` with the query (used for paging).
* `Query` *pagingState* `(PagingState pagingState)` used to associate a `PagingState` with the query (used for paging).
* `Query` *queryOptions* `(QueryOptions queryOptions)` used to associate `QueryOptions` with the query.
* `Query` *sort* `(Sort sort)` used to provide sort definition for the results.
@@ -1151,8 +1152,9 @@ The query methods need to specify the target type T that will be returned.
* `List<T>` *select* `(Query query, Class<T> entityClass)` Query for a list of objects of type T from the table.
* `T` *selectOneById* `(Query query, Class<T> entityClass)` Query for a single object of type T from the table.
* `Slice<T>` *slice* `(Query query, Class<T> entityClass)` Start or continue paging by querying for a `Slice` of objects of type T from the table.
* `T` *selectOne* `(Query query, Class<T> entityClass)` Query for a single object of type T from the table.
* `Stream<T>` *stream* `(Query query, Class<T> entityClass)` Query for a stream of objects of type T from the table.
* `List<T>` *select* `(String cql, Class<T> entityClass)` Ad-hoc query for a list of objects of type T from the table providing a CQL statement.
* `T` *selectOneById* `(String cql, Class<T> entityClass)` Ad-hoc query for a single object of type T from the table providing a CQL statement.
* `Stream<T>` *stream* `(String cql, Class<T> entityClass)` Ad-hoc query for a stream of objects of type T from the table providing a CQL statement.