com.datastax.oss.driver.api.core.PagingIterable Maven / Gradle / Ivy
/*
* Copyright DataStax, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.oss.driver.api.core;
import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.internal.core.PagingIterableWrapper;
import com.datastax.oss.driver.internal.core.cql.PagingIterableSpliterator;
import com.datastax.oss.driver.shaded.guava.common.collect.Iterables;
import com.datastax.oss.driver.shaded.guava.common.collect.Lists;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Function;
/**
* An iterable of elements which are fetched synchronously by the driver, possibly in multiple
* requests.
*
* It uses asynchronous calls internally, but blocks on the results in order to provide a
* synchronous API to its clients. If the query is paged, only the first page will be fetched
* initially, and iteration will trigger background fetches of the next pages when necessary.
*
*
Note that this object can only be iterated once: elements are "consumed" as they are read,
* subsequent calls to {@code iterator()} will return the same iterator instance.
*
*
Implementations of this type are not thread-safe. They can only be iterated by the
* thread that invoked {@code session.execute}.
*
*
This is a generalization of {@link ResultSet}, replacing rows by an arbitrary element type.
*/
public interface PagingIterable extends Iterable {
/** Metadata about the columns returned by the CQL request that was used to build this result. */
@NonNull
ColumnDefinitions getColumnDefinitions();
/**
* The execution information for the last query performed for this iterable.
*
* This is a shortcut for:
*
*
* getExecutionInfos().get(getExecutionInfos().size() - 1)
*
*
* @see #getExecutionInfos()
*/
@NonNull
default ExecutionInfo getExecutionInfo() {
List infos = getExecutionInfos();
return infos.get(infos.size() - 1);
}
/**
* The execution information for all the queries that have been performed so far to assemble this
* iterable.
*
* This will have multiple elements if the query is paged, since the driver performs blocking
* background queries to fetch additional pages transparently as the result set is being iterated.
*/
@NonNull
List getExecutionInfos();
/**
* Returns the next element, or {@code null} if the iterable is exhausted.
*
* This is convenient for queries that are known to return exactly one row, for example count
* queries.
*/
@Nullable
default ElementT one() {
Iterator iterator = iterator();
return iterator.hasNext() ? iterator.next() : null;
}
/**
* Returns all the remaining elements as a list; not recommended for queries that return a
* large number of elements.
*
* Contrary to {@link #iterator()} or successive calls to {@link #one()}, this method forces
* fetching the full contents at once; in particular, this means that a large number of
* background queries might have to be run, and that all the data will be held in memory locally.
* Therefore it is crucial to only call this method for queries that are known to return a
* reasonable number of results.
*/
@NonNull
@SuppressWarnings("MixedMutabilityReturnType")
default List all() {
if (!iterator().hasNext()) {
return Collections.emptyList();
}
// We can't know the actual size in advance since more pages could be fetched, but we can at
// least allocate for what we already have.
List result = Lists.newArrayListWithExpectedSize(getAvailableWithoutFetching());
Iterables.addAll(result, this);
return result;
}
/**
* Whether all pages have been fetched from the database.
*
* If this is {@code false}, it means that more blocking background queries will be triggered
* as iteration continues.
*/
boolean isFullyFetched();
/**
* The number of elements that can be returned from this result set before a blocking background
* query needs to be performed to retrieve more results. In other words, this is the number of
* elements remaining in the current page.
*
*
This is useful if you use the paging state to pause the iteration and resume it later: after
* you've retrieved the state ({@link ExecutionInfo#getPagingState()
* getExecutionInfo().getPagingState()}), call this method and iterate the remaining elements;
* that way you're not leaving a gap between the last element and the position you'll restart from
* when you reinject the state in a new query.
*/
int getAvailableWithoutFetching();
/**
* If the query that produced this result was a CQL conditional update, indicate whether it was
* successfully applied.
*
*
For consistency, this method always returns {@code true} for non-conditional queries
* (although there is no reason to call the method in that case). This is also the case for
* conditional DDL statements ({@code CREATE KEYSPACE... IF NOT EXISTS}, {@code CREATE TABLE... IF
* NOT EXISTS}), for which Cassandra doesn't return an {@code [applied]} column.
*
*
Note that, for versions of Cassandra strictly lower than 2.1.0-rc2, a server-side bug (CASSANDRA-7337) causes this
* method to always return {@code true} for batches containing conditional queries.
*/
boolean wasApplied();
/**
* Creates a new instance by transforming each element of this iterable with the provided
* function.
*
*
Note that both instances share the same underlying data: consuming elements from the
* transformed iterable will also consume them from this object, and vice-versa.
*/
@NonNull
default PagingIterable map(
Function elementMapper) {
return new PagingIterableWrapper<>(this, elementMapper);
}
/**
* {@inheritDoc}
*
* Default spliterators created by the driver will report the following characteristics: {@link
* Spliterator#ORDERED}, {@link Spliterator#IMMUTABLE}, {@link Spliterator#NONNULL}. Single-page
* result sets will also report {@link Spliterator#SIZED} and {@link Spliterator#SUBSIZED}, since
* the result set size is known.
*
*
This method should be called at most once. Spliterators share the same underlying data but
* do not support concurrent consumption; once a spliterator for this iterable is obtained, the
* iterable should not be consumed through calls to other methods such as {@link
* #iterator()}, {@link #one()} or {@link #all()}; doing so will result in unpredictable results.
*/
@NonNull
@Override
default Spliterator spliterator() {
return new PagingIterableSpliterator<>(this);
}
}