...
Utilizing these, the query returns results ordered by their keysare ordered based on the serialized byte[] of the keys, not the 'logical' key order.
Take IQv2
StoreIntegrationTest
as an example: we have two partitions with four key-value pairs:
...
Code Block |
---|
/** * Interactive query for issuing range queries and scans over KeyValue stores. * <p> * A range query retrieves a set of records, specified using an upper and/or lower bound on the keys. * <p> * A scan query retrieves all records contained in the store. * <p> */ @Evolving public final class RangeQuery<K, V> implements Query<KeyValueIterator<K, V>> { ... private final boolean isKeyAscending; private RangeQuery(final Optional<K> lower, final Optional<K> upper, final boolean isKeyAscending) { this.lower = lower; this.upper = upper; this.isKeyAscending = isKeyAscending; } /** * Interactive range query using a lower and upper bound to filter the keys returned. * @param lower The key that specifies the lower bound of the range * @param upper The key that specifies the upper bound of the range * @param <K> The key type * @param <V> The value type */ public static <K, V> RangeQuery<K, V> withRange(final K lower, final K upper) { return new RangeQuery<>(Optional.ofNullable(lower), Optional.ofNullable(upper), true); } /** * Determines if the query keys are in ascending order. * @return true if ascending, false otherwise. */ public boolean isKeyAscending() { return isKeyAscending; }... /** * Set the query to return keys in descending order. * @return a new RangeQuery instance with descending flag set. */ public RangeQuery<K, V> withDescendingKeys() { return new RangeQuery<>(this.lower, this.upper, false); } /** * Interactive range query using an upper bound to filter the keys returned. * If both <K,V> are null, RangQuery returns a full range scan. * @param upper The key that specifies the upper bound of the range * @param <K> The key type * @param <V> The value type */ public static <K, V> RangeQuery<K, V> withUpperBound(final K upper) { return new RangeQuery<>(Optional.empty(), Optional.of(upper), true); } /** * Interactive range query using a lower bound to filter the keys returned. * @param lower The key that specifies the lower bound of the range * @param <K> The key type * @param <V> The value type */ public static <K, V> RangeQuery<K, V> withLowerBound(final K lower) { return new RangeQuery<>(Optional.of(lower), Optional.empty(), true); } /** * Interactive scan query that returns all records in the store. * @param <K> The key type * @param <V> The value type */ public static <K, V> RangeQuery<K, V> withNoBounds() { return new RangeQuery<>(Optional.empty(), Optional.empty(), true); } ... } |
Test Plan
This time, our goal is to implement reverseRange
and reverseAll
functionalities. While these terms are used for clarity, in practice, they correspond to RangeQuery.withRange().withDescendingKeys()
and RangeQuery.withNoBounds().withDescendingKeys()
, respectively. To ensure the accurate retrieval of results for both functionalities, adjustments to IQv2StoreIntegrationTest
are required. In our previous approach, we stored query results in a set, which doesn't maintain order. I've transitioned to using a list for storing query results, enabling us to distinguish between rangeQuery
and reverseQuery
. Here, rangeQuery
refers to standard queries (those not using withDescendingKeys()
) such as withRange()
, withLowerBound()
, withUpperBound()
, and withNoBounds()
. In contrast, reverseQuery
denotes queries that employ the withDescendingKeys()
method.
...