T
- the type of elements of the cursor@API(value=MAINTAINED) public class ChainedCursor<T> extends Object implements BaseCursor<T>
Optional.empty()
as
input, and from that produces an initial value. Upon subsequent iterations the function is given the
value it previously returned and the next value is computed. For example, the following generator would
produce the values from 1 to 10:
maybePreviousValue -> { if (maybePreviousValue.isPresent()) { if (maybePreviousValue.get() < 10) { return Optional.of(maybePreviousValue.get() + 1); } else { return Optional.empty(); } } return Optional.of(1); }Given this function, the
ChainedCursor
would iteratively generate the value from 1 to 10.
In order to support continuations, the cursor must additionally be provided a functions that can encode and decode the values it produces into binary continuation form. So the fully defined cursor to perform our iteration from 1 to 10, would look like:
new ChainedCursor( // Value generator maybePreviousValue -> { if (maybePreviousValue.isPresent()) { if (maybePreviousValue.get() < 10) { return Optional.of(maybePreviousValue.get() + 1); } else { return Optional.empty(); } } return Optional.of(1); }, // Continuation encoder previousValue -> Tuple.from(previousValue).pack(), // Continuation decoder encodedContinuation -> Tuple.fromBytes(encodedContinuation).getLong(0) executor )
ChainedCursor
is created with a set of ScanProperties
it will attempt to count each
iteration of the cursor against the limits that are indicated by the properties. This can lead to an interesting
behavior if the same set of ScanProperties
are also used by cursors that are present in the nextGenerator
that is provided and can lead to double-counting. For example, if the nextGenerator
is driven
by the read of a row key via the KeyValueCursor
, the cursor could end up counting that read, and the
ChainedCursor
would then, again, count that read. As such, the caller should take care when
specifying the ScanProperties
to the cursor and within the nextGenerator
.
In addition, while the ChainedCursor
can track and limit on scanned records and scan time,
it has no visibility into any bytes that may have been read by cursor managed by the nextGenerator
and, thus, cannot enforce any byte scan limits.
RecordCursor.NoNextReason
Constructor and Description |
---|
ChainedCursor(FDBRecordContext context,
Function<Optional<T>,CompletableFuture<Optional<T>>> nextGenerator,
Function<T,byte[]> continuationEncoder,
Function<byte[],T> continuationDecoder,
byte[] continuation,
ScanProperties scanProperties)
Creates a
ChainedCursor . |
ChainedCursor(Function<Optional<T>,CompletableFuture<Optional<T>>> nextGenerator,
Function<T,byte[]> continuationEncoder,
Function<byte[],T> continuationDecoder,
byte[] continuation,
Executor executor)
Creates a new
ChainedCursor . |
Modifier and Type | Method and Description |
---|---|
boolean |
accept(RecordCursorVisitor visitor)
Accept a visit from hierarchical visitor, which implements
RecordCursorVisitor . |
void |
close() |
byte[] |
getContinuation()
Deprecated.
|
Executor |
getExecutor() |
RecordCursor.NoNextReason |
getNoNextReason()
Deprecated.
|
T |
next()
Deprecated.
|
CompletableFuture<Boolean> |
onHasNext()
Deprecated.
|
CompletableFuture<RecordCursorResult<T>> |
onNext()
Asynchronously return the next result from this cursor.
|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
asIterator, asList, empty, empty, filter, filterAsync, filterAsyncInstrumented, filterAsyncInstrumented, filterInstrumented, filterInstrumented, first, flatMapPipelined, flatMapPipelined, flatMapPipelined, forEach, forEachAsync, forEachResult, forEachResultAsync, fromFuture, fromFuture, fromIterator, fromIterator, fromList, fromList, fromList, fromList, getCount, getNext, hasNext, limitRowsTo, limitTo, map, mapEffect, mapEffect, mapFuture, mapPipelined, orElse, reduce, skip, skipThenLimit
forEachRemaining, remove
public ChainedCursor(@Nonnull Function<Optional<T>,CompletableFuture<Optional<T>>> nextGenerator, @Nonnull Function<T,byte[]> continuationEncoder, @Nonnull Function<byte[],T> continuationDecoder, @Nullable byte[] continuation, @Nonnull Executor executor)
ChainedCursor
. When created in this fashion any resource limits that may be
specified in cursors that are used within the nextGenerator
will be honored.nextGenerator
- a function that that is called to perform iterationcontinuationEncoder
- a function that takes a value that was returned by nextGenerator
and
encodes it as a continuation valuecontinuationDecoder
- a function that takes a continuation that was produced by continuationEncoder
and decodes it back to its original value as returned by nextGenerator
continuation
- the initial continuation for the iterationexecutor
- executor that will be returned by getExecutor()
public ChainedCursor(@Nonnull FDBRecordContext context, @Nonnull Function<Optional<T>,CompletableFuture<Optional<T>>> nextGenerator, @Nonnull Function<T,byte[]> continuationEncoder, @Nonnull Function<byte[],T> continuationDecoder, @Nullable byte[] continuation, @Nonnull ScanProperties scanProperties)
ChainedCursor
.
The ScanProperties.isReverse()
} cannot be honored by the ChainedCursor
as the
direction of iteration is strictly controlled by the nextGenerator
.
context
- a record contextnextGenerator
- a function that that is called to perform iterationcontinuationEncoder
- a function that takes a value that was returned by nextGenerator
and
encodes it as a continuation valuecontinuationDecoder
- a function that takes a continuation that was produced by continuationEncoder
and decodes it back to its original value as returned by nextGenerator
continuation
- the initial continuation for the iterationscanProperties
- properties used to control the scanning behavior@Nonnull public CompletableFuture<RecordCursorResult<T>> onNext()
RecordCursor
RecordCursorResult
, which represents exactly one of the following:
T
produced by the cursor. In addition to the next record, this result
includes a RecordCursorContinuation
that can be used to continue the cursor after the last record
returned. The returned continuation is guaranteed not to be an "end continuation" representing the end of
the cursor: specifically, RecordCursorContinuation.isEnd()
is always false
on the returned
continuation.
RecordCursor.NoNextReason
that
explains why no record could be produced. The result include a continuation that can be used to continue
the cursor after the last record returned.
If the result's NoNextReason
is anything other than RecordCursor.NoNextReason.SOURCE_EXHAUSTED
, the
returned continuation must not be an end continuation. Conversely, if the result's NoNextReason
is SOURCE_EXHAUSTED
, then the returned continuation must be an an "end continuation".
RecordCursorContinuation
can be serialized to an opaque byte array using
RecordCursorContinuation.toBytes()
. This can be passed back into a new cursor of the same type, with all
other parameters remaining the same.onNext
in interface RecordCursor<T>
RecordCursorResult
,
RecordCursorContinuation
@Nonnull @Deprecated public CompletableFuture<Boolean> onHasNext()
RecordCursor
onHasNext
in interface RecordCursor<T>
true
if RecordCursor.next()
would return a record.AsyncIterator.onHasNext()
@Nullable @Deprecated public T next()
RecordCursor
@Nullable @Deprecated public byte[] getContinuation()
RecordCursor
getContinuation
in interface RecordCursor<T>
null
if the underlying source is completely exhausted, independent of any limit
passed to the cursor creator. Since such creators generally accept null
to mean no continuation,
that is, start from the beginning, one must check for null
from getContinuation
to
keep from starting over.
Result is not always defined if called before onHasNext
or before next
after
onHasNext
has returned true
. That is, a continuation is only guaranteed when called
"between" records from a while (hasNext) next
loop or after its end.@Nonnull @Deprecated public RecordCursor.NoNextReason getNoNextReason()
RecordCursor
false
for RecordCursor.hasNext()
.
If hasNext
was not called or returned true
last time, the result is undefined and
may be an exception.getNoNextReason
in interface RecordCursor<T>
public void close()
close
in interface RecordCursor<T>
close
in interface AutoCloseable
@Nonnull public Executor getExecutor()
getExecutor
in interface RecordCursor<T>
public boolean accept(@Nonnull RecordCursorVisitor visitor)
RecordCursor
RecordCursorVisitor
.
By contract, implementations of this method must return the value of visitor.visitLeave(this)
,
which determines whether or not subsequent siblings of this cursor should be visited.accept
in interface RecordCursor<T>
visitor
- a hierarchical visitortrue
if the subsequent siblings of the cursor
should be visited, and false
otherwise