All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.lettuce.core.ScanIterator Maven / Gradle / Ivy

Go to download

Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and much more.

The newest version!
package io.lettuce.core;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import io.lettuce.core.api.sync.RedisHashCommands;
import io.lettuce.core.api.sync.RedisKeyCommands;
import io.lettuce.core.api.sync.RedisSetCommands;
import io.lettuce.core.api.sync.RedisSortedSetCommands;
import io.lettuce.core.internal.LettuceAssert;

/**
 * Scan command support exposed through {@link Iterator}.
 * 

* {@link ScanIterator} uses synchronous command interfaces to scan over keys ({@code SCAN}), sets ({@code SSCAN}), sorted sets * ({@code ZSCAN}), and hashes ({@code HSCAN}). A {@link ScanIterator} is stateful and not thread-safe. Instances can be used * only once to iterate over results. *

* Use {@link ScanArgs#limit(long)} to set the batch size. *

* Data structure scanning is progressive and stateful and demand-aware. It supports full iterations (until all received cursors * are exhausted) and premature termination. Subsequent scan commands to fetch the cursor data get only issued if the caller * signals demand by consuming the {@link ScanIterator}. * * @param Element type * @author Mark Paluch * @since 4.4 */ public abstract class ScanIterator implements Iterator { private ScanIterator() { } /** * Sequentially iterate over keys in the keyspace. This method uses {@code SCAN} to perform an iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator scan(RedisKeyCommands commands) { return scan(commands, Optional.empty()); } /** * Sequentially iterate over keys in the keyspace. This method uses {@code SCAN} to perform an iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param scanArgs the scan arguments, must not be {@code null}. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator scan(RedisKeyCommands commands, ScanArgs scanArgs) { LettuceAssert.notNull(scanArgs, "ScanArgs must not be null"); return scan(commands, Optional.of(scanArgs)); } private static ScanIterator scan(RedisKeyCommands commands, Optional scanArgs) { LettuceAssert.notNull(commands, "RedisKeyCommands must not be null"); return new SyncScanIterator() { @Override protected ScanCursor nextScanCursor(ScanCursor scanCursor) { KeyScanCursor cursor = getNextScanCursor(scanCursor); chunk = cursor.getKeys().iterator(); return cursor; } private KeyScanCursor getNextScanCursor(ScanCursor scanCursor) { if (scanCursor == null) { return scanArgs.map(commands::scan).orElseGet(commands::scan); } return scanArgs.map((scanArgs) -> commands.scan(scanCursor, scanArgs)) .orElseGet(() -> commands.scan(scanCursor)); } }; } /** * Sequentially iterate over entries in a hash identified by {@code key}. This method uses {@code HSCAN} to perform an * iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the hash to scan. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator> hscan(RedisHashCommands commands, K key) { return hscan(commands, key, Optional.empty()); } /** * Sequentially iterate over keys in a hash identified by {@code key}. This method uses {@code HSCAN NOVALUES} to perform an * iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the hash to scan. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. * @since 6.4 */ public static ScanIterator hscanNovalues(RedisHashCommands commands, K key) { return hscanNovalues(commands, key, Optional.empty()); } /** * Sequentially iterate over entries in a hash identified by {@code key}. This method uses {@code HSCAN} to perform an * iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the hash to scan. * @param scanArgs the scan arguments, must not be {@code null}. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator> hscan(RedisHashCommands commands, K key, ScanArgs scanArgs) { LettuceAssert.notNull(scanArgs, "ScanArgs must not be null"); return hscan(commands, key, Optional.of(scanArgs)); } /** * Sequentially iterate over keys in a hash identified by {@code key}. This method uses {@code HSCAN NOVALUES} to perform an * iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the hash to scan. * @param scanArgs the scan arguments, must not be {@code null}. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. * @since 6.4 */ public static ScanIterator hscanNovalues(RedisHashCommands commands, K key, ScanArgs scanArgs) { LettuceAssert.notNull(scanArgs, "ScanArgs must not be null"); return hscanNovalues(commands, key, Optional.of(scanArgs)); } private static ScanIterator> hscan(RedisHashCommands commands, K key, Optional scanArgs) { LettuceAssert.notNull(commands, "RedisKeyCommands must not be null"); LettuceAssert.notNull(key, "Key must not be null"); return new SyncScanIterator>() { @Override protected ScanCursor nextScanCursor(ScanCursor scanCursor) { MapScanCursor cursor = getNextScanCursor(scanCursor); chunk = cursor.getMap().keySet().stream().map(k -> KeyValue.fromNullable(k, cursor.getMap().get(k))).iterator(); return cursor; } private MapScanCursor getNextScanCursor(ScanCursor scanCursor) { if (scanCursor == null) { return scanArgs.map(scanArgs -> commands.hscan(key, scanArgs)).orElseGet(() -> commands.hscan(key)); } return scanArgs.map((scanArgs) -> commands.hscan(key, scanCursor, scanArgs)) .orElseGet(() -> commands.hscan(key, scanCursor)); } }; } private static ScanIterator hscanNovalues(RedisHashCommands commands, K key, Optional scanArgs) { LettuceAssert.notNull(commands, "RedisKeyCommands must not be null"); LettuceAssert.notNull(key, "Key must not be null"); return new SyncScanIterator() { @Override protected ScanCursor nextScanCursor(ScanCursor scanCursor) { KeyScanCursor cursor = getNextScanCursor(scanCursor); chunk = cursor.getKeys().iterator(); return cursor; } private KeyScanCursor getNextScanCursor(ScanCursor scanCursor) { if (scanCursor == null) { return scanArgs.map(scanArgs -> commands.hscanNovalues(key, scanArgs)) .orElseGet(() -> commands.hscanNovalues(key)); } return scanArgs.map((scanArgs) -> commands.hscanNovalues(key, scanCursor, scanArgs)) .orElseGet(() -> commands.hscanNovalues(key, scanCursor)); } }; } /** * Sequentially iterate over elements in a set identified by {@code key}. This method uses {@code SSCAN} to perform an * iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the set to scan. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator sscan(RedisSetCommands commands, K key) { return sscan(commands, key, Optional.empty()); } /** * Sequentially iterate over elements in a set identified by {@code key}. This method uses {@code SSCAN} to perform an * iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the set to scan. * @param scanArgs the scan arguments, must not be {@code null}. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator sscan(RedisSetCommands commands, K key, ScanArgs scanArgs) { LettuceAssert.notNull(scanArgs, "ScanArgs must not be null"); return sscan(commands, key, Optional.of(scanArgs)); } private static ScanIterator sscan(RedisSetCommands commands, K key, Optional scanArgs) { LettuceAssert.notNull(commands, "RedisKeyCommands must not be null"); LettuceAssert.notNull(key, "Key must not be null"); return new SyncScanIterator() { @Override protected ScanCursor nextScanCursor(ScanCursor scanCursor) { ValueScanCursor cursor = getNextScanCursor(scanCursor); chunk = cursor.getValues().iterator(); return cursor; } private ValueScanCursor getNextScanCursor(ScanCursor scanCursor) { if (scanCursor == null) { return scanArgs.map(scanArgs -> commands.sscan(key, scanArgs)).orElseGet(() -> commands.sscan(key)); } return scanArgs.map((scanArgs) -> commands.sscan(key, scanCursor, scanArgs)) .orElseGet(() -> commands.sscan(key, scanCursor)); } }; } /** * Sequentially iterate over scored values in a sorted set identified by {@code key}. This method uses {@code ZSCAN} to * perform an iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the sorted set to scan. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator> zscan(RedisSortedSetCommands commands, K key) { return zscan(commands, key, Optional.empty()); } /** * Sequentially iterate over scored values in a sorted set identified by {@code key}. This method uses {@code ZSCAN} to * perform an iterative scan. * * @param commands the commands interface, must not be {@code null}. * @param key the sorted set to scan. * @param scanArgs the scan arguments, must not be {@code null}. * @param Key type. * @param Value type. * @return a new {@link ScanIterator}. */ public static ScanIterator> zscan(RedisSortedSetCommands commands, K key, ScanArgs scanArgs) { LettuceAssert.notNull(scanArgs, "ScanArgs must not be null"); return zscan(commands, key, Optional.of(scanArgs)); } private static ScanIterator> zscan(RedisSortedSetCommands commands, K key, Optional scanArgs) { LettuceAssert.notNull(commands, "RedisKeyCommands must not be null"); LettuceAssert.notNull(key, "Key must not be null"); return new SyncScanIterator>() { @Override protected ScanCursor nextScanCursor(ScanCursor scanCursor) { ScoredValueScanCursor cursor = getNextScanCursor(scanCursor); chunk = cursor.getValues().iterator(); return cursor; } private ScoredValueScanCursor getNextScanCursor(ScanCursor scanCursor) { if (scanCursor == null) { return scanArgs.map(scanArgs -> commands.zscan(key, scanArgs)).orElseGet(() -> commands.zscan(key)); } return scanArgs.map((scanArgs) -> commands.zscan(key, scanCursor, scanArgs)) .orElseGet(() -> commands.zscan(key, scanCursor)); } }; } /** * Returns a sequential {@code Stream} with this {@link ScanIterator} as its source. * * @return a {@link Stream} for this {@link ScanIterator}. */ public Stream stream() { return StreamSupport.stream(Spliterators.spliterator(this, -1, 0), false); } /** * Synchronous {@link ScanIterator} implementation. * * @param */ private static abstract class SyncScanIterator extends ScanIterator { private ScanCursor scanCursor; protected Iterator chunk = null; @Override public boolean hasNext() { while (scanCursor == null || !scanCursor.isFinished()) { if (scanCursor == null || !hasChunkElements()) { scanCursor = nextScanCursor(scanCursor); } if (hasChunkElements()) { return true; } } return hasChunkElements(); } private boolean hasChunkElements() { return chunk.hasNext(); } @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } return chunk.next(); } protected abstract ScanCursor nextScanCursor(ScanCursor scanCursor); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy