/*
* Copyright 2016-2017 the original author or authors.
*
* 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 org.springframework.data.redis.connection;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import org.reactivestreams.Publisher;
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
import org.springframework.util.Assert;
/**
* Redis Key commands executed using reactive infrastructure.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.0
*/
public interface ReactiveKeyCommands {
/**
* Determine if given {@literal key} exists.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/exists">Redis Documentation: EXISTS</a>
*/
default Mono<Boolean> exists(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return exists(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput);
}
/**
* Determine if given {@literal key} exists.
*
* @param keys must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/exists">Redis Documentation: EXISTS</a>
*/
Flux<BooleanResponse<KeyCommand>> exists(Publisher<KeyCommand> keys);
/**
* Determine the type stored at {@literal key}.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/type">Redis Documentation: TYPE</a>
*/
default Mono<DataType> type(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return type(Mono.just(new KeyCommand(key))).next().map(CommandResponse::getOutput);
}
/**
* Determine the type stored at {@literal key}.
*
* @param keys must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/type">Redis Documentation: TYPE</a>
*/
Flux<CommandResponse<KeyCommand, DataType>> type(Publisher<KeyCommand> keys);
/**
* Find all keys matching the given {@literal pattern}.
*
* @param pattern must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/keys">Redis Documentation: KEYS</a>
*/
default Mono<List<ByteBuffer>> keys(ByteBuffer pattern) {
Assert.notNull(pattern, "Pattern must not be null!");
return keys(Mono.just(pattern)).next().map(MultiValueResponse::getOutput);
}
/**
* Find all keys matching the given {@literal pattern}.
*
* @param patterns must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/keys">Redis Documentation: KEYS</a>
*/
Flux<MultiValueResponse<ByteBuffer, ByteBuffer>> keys(Publisher<ByteBuffer> patterns);
/**
* Return a random key from the keyspace.
*
* @return
* @see <a href="http://redis.io/commands/randomkey">Redis Documentation: RANDOMKEY</a>
*/
Mono<ByteBuffer> randomKey();
/**
* {@code RENAME} command parameters.
*
* @author Christoph Strobl
* @see <a href="http://redis.io/commands/rename">Redis Documentation: RENAME</a>
*/
class RenameCommand extends KeyCommand {
private ByteBuffer newName;
private RenameCommand(ByteBuffer key, ByteBuffer newName) {
super(key);
this.newName = newName;
}
/**
* Creates a new {@link RenameCommand} given a {@link ByteBuffer key}.
*
* @param key must not be {@literal null}.
* @return a new {@link RenameCommand} for {@link ByteBuffer key}.
*/
public static RenameCommand key(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new RenameCommand(key, null);
}
/**
* Applies the {@literal newName}. Constructs a new command instance with all previously configured properties.
*
* @param newName must not be {@literal null}.
* @return a new {@link RenameCommand} with {@literal newName} applied.
*/
public RenameCommand to(ByteBuffer newName) {
Assert.notNull(newName, "New name must not be null!");
return new RenameCommand(getKey(), newName);
}
/**
* @return
*/
public ByteBuffer getNewName() {
return newName;
}
}
/**
* Rename key {@literal oleName} to {@literal newName}.
*
* @param key must not be {@literal null}.
* @param newName must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/rename">Redis Documentation: RENAME</a>
*/
default Mono<Boolean> rename(ByteBuffer key, ByteBuffer newName) {
Assert.notNull(key, "Key must not be null!");
return rename(Mono.just(RenameCommand.key(key).to(newName))).next().map(BooleanResponse::getOutput);
}
/**
* Rename key {@literal oleName} to {@literal newName}.
*
* @param command must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/rename">Redis Documentation: RENAME</a>
*/
Flux<BooleanResponse<RenameCommand>> rename(Publisher<RenameCommand> command);
/**
* Rename key {@literal oleName} to {@literal newName} only if {@literal newName} does not exist.
*
* @param key must not be {@literal null}.
* @param newName must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/renamenx">Redis Documentation: RENAMENX</a>
*/
default Mono<Boolean> renameNX(ByteBuffer key, ByteBuffer newName) {
Assert.notNull(key, "Key must not be null!");
return renameNX(Mono.just(RenameCommand.key(key).to(newName))).next().map(BooleanResponse::getOutput);
}
/**
* Rename key {@literal oleName} to {@literal newName} only if {@literal newName} does not exist.
*
* @param command must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/renamenx">Redis Documentation: RENAMENX</a>
*/
Flux<BooleanResponse<RenameCommand>> renameNX(Publisher<RenameCommand> command);
/**
* Delete {@literal key}.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
*/
default Mono<Long> del(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return del(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
}
/**
* Delete {@literal keys} one by one.
*
* @param keys must not be {@literal null}.
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} removed along with the deletion result.
* @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
*/
Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> keys);
/**
* Delete multiple {@literal keys} one in one batch.
*
* @param keys must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
*/
default Mono<Long> mDel(List<ByteBuffer> keys) {
Assert.notEmpty(keys, "Keys must not be empty or null!");
return mDel(Mono.just(keys)).next().map(NumericResponse::getOutput);
}
/**
* Delete multiple {@literal keys} in batches.
*
* @param keys must not be {@literal null}.
* @return {@link Flux} of {@link NumericResponse} holding the {@literal keys} removed along with the deletion result.
* @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
*/
Flux<NumericResponse<List<ByteBuffer>, Long>> mDel(Publisher<List<ByteBuffer>> keys);
/**
* {@code EXPIRE}/{@code PEXPIRE} command parameters.
*
* @author Mark Paluch
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIRE</a>
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIRE</a>
*/
class ExpireCommand extends KeyCommand {
private Duration timeout;
private ExpireCommand(ByteBuffer key, Duration timeout) {
super(key);
this.timeout = timeout;
}
/**
* Creates a new {@link ExpireCommand} given a {@link ByteBuffer key}.
*
* @param key must not be {@literal null}.
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
*/
public static ExpireCommand key(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new ExpireCommand(key, null);
}
/**
* Applies the {@literal timeout}. Constructs a new command instance with all previously configured properties.
*
* @param timeout must not be {@literal null}.
* @return a new {@link ExpireCommand} with {@literal timeout} applied.
*/
public ExpireCommand timeout(Duration timeout) {
Assert.notNull(timeout, "Timeout must not be null!");
return new ExpireCommand(getKey(), timeout);
}
/**
* @return
*/
public Duration getTimeout() {
return timeout;
}
}
/**
* Set time to live for given {@code key} in seconds.
*
* @param key must not be {@literal null}.
* @param timeout must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIRE</a>
*/
default Mono<Boolean> expire(ByteBuffer key, Duration timeout) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(timeout, "Timeout must not be null!");
return expire(Mono.just(new ExpireCommand(key, timeout))).next().map(BooleanResponse::getOutput);
}
/**
* Expire {@literal keys} one by one.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
* result.
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIRE</a>
*/
Flux<BooleanResponse<ExpireCommand>> expire(Publisher<ExpireCommand> commands);
/**
* Set time to live for given {@code key} in milliseconds.
*
* @param key must not be {@literal null}.
* @param timeout must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIRE</a>
*/
default Mono<Boolean> pExpire(ByteBuffer key, Duration timeout) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(timeout, "Timeout must not be null!");
return expire(Mono.just(new ExpireCommand(key, timeout))).next().map(BooleanResponse::getOutput);
}
/**
* Expire {@literal keys} one by one.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
* result.
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIRE</a>
*/
Flux<BooleanResponse<ExpireCommand>> pExpire(Publisher<ExpireCommand> commands);
/**
* {@code EXPIREAT}/{@code PEXPIREAT} command parameters.
*
* @author Mark Paluch
* @see <a href="http://redis.io/commands/expire">Redis Documentation: EXPIREAT</a>
* @see <a href="http://redis.io/commands/pexpire">Redis Documentation: PEXPIREAT</a>
*/
class ExpireAtCommand extends KeyCommand {
private Instant expireAt;
private ExpireAtCommand(ByteBuffer key, Instant expireAt) {
super(key);
this.expireAt = expireAt;
}
/**
* Creates a new {@link ExpireAtCommand} given a {@link ByteBuffer key}.
*
* @param key must not be {@literal null}.
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
*/
public static ExpireAtCommand key(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new ExpireAtCommand(key, null);
}
/**
* Applies the {@literal expireAt}. Constructs a new command instance with all previously configured properties.
*
* @param expireAt must not be {@literal null}.
* @return a new {@link ExpireAtCommand} with {@literal expireAt} applied.
*/
public ExpireAtCommand timeout(Instant expireAt) {
Assert.notNull(expireAt, "Expire at must not be null!");
return new ExpireAtCommand(getKey(), expireAt);
}
/**
* @return
*/
public Instant getExpireAt() {
return expireAt;
}
}
/**
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp.
*
* @param key must not be {@literal null}.
* @param expireAt must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/expireat">Redis Documentation: EXPIREAT</a>
*/
default Mono<Boolean> expireAt(ByteBuffer key, Instant expireAt) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(expireAt, "Expire at must not be null!");
return expireAt(Mono.just(new ExpireAtCommand(key, expireAt))).next().map(BooleanResponse::getOutput);
}
/**
* Set one-by-one the expiration for given {@code key} as a {@literal UNIX} timestamp.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
* result.
* @see <a href="http://redis.io/commands/expireat">Redis Documentation: EXPIREAT</a>
*/
Flux<BooleanResponse<ExpireAtCommand>> expireAt(Publisher<ExpireAtCommand> commands);
/**
* Set the expiration for given {@code key} as a {@literal UNIX} timestamp.
*
* @param key must not be {@literal null}.
* @param expireAt must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/pexpireat">Redis Documentation: PEXPIREAT</a>
*/
default Mono<Boolean> pExpireAt(ByteBuffer key, Instant expireAt) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(expireAt, "Expire at must not be null!");
return pExpireAt(Mono.just(new ExpireAtCommand(key, expireAt))).next().map(BooleanResponse::getOutput);
}
/**
* Set one-by-one the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} removed along with the expiration
* result.
* @see <a href="http://redis.io/commands/pexpireat">Redis Documentation: PEXPIREAT</a>
*/
Flux<BooleanResponse<ExpireAtCommand>> pExpireAt(Publisher<ExpireAtCommand> commands);
/**
* Remove the expiration from given {@code key}.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/persist">Redis Documentation: PERSIST</a>
*/
default Mono<Boolean> persist(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return persist(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput);
}
/**
* Remove one-by-one the expiration from given {@code key}.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} persisted along with the persist result.
* @see <a href="http://redis.io/commands/persist">Redis Documentation: PERSIST</a>
*/
Flux<BooleanResponse<KeyCommand>> persist(Publisher<KeyCommand> commands);
/**
* Get the time to live for {@code key} in seconds.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
*/
default Mono<Long> ttl(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return ttl(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
}
/**
* Get one-by-one the time to live for keys.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} along with the time to live result.
* @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
*/
Flux<NumericResponse<KeyCommand, Long>> ttl(Publisher<KeyCommand> commands);
/**
* Get the time to live for {@code key} in milliseconds.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
*/
default Mono<Long> pTtl(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return pTtl(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
}
/**
* Get one-by-one the time to live for keys.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} along with the time to live result.
* @see <a href="http://redis.io/commands/pttl">Redis Documentation: PTTL</a>
*/
Flux<NumericResponse<KeyCommand, Long>> pTtl(Publisher<KeyCommand> commands);
/**
* {@code MOVE} command parameters.
*
* @author Mark Paluch
* @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
*/
class MoveCommand extends KeyCommand {
private Integer database;
private MoveCommand(ByteBuffer key, Integer database) {
super(key);
this.database = database;
}
/**
* Creates a new {@link MoveCommand} given a {@link ByteBuffer key}.
*
* @param key must not be {@literal null}.
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
*/
public static MoveCommand key(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new MoveCommand(key, null);
}
/**
* Applies the {@literal database} index. Constructs a new command instance with all previously configured
* properties.
*
* @param database
* @return a new {@link MoveCommand} with {@literal database} applied.
*/
public MoveCommand timeout(int database) {
return new MoveCommand(getKey(), database);
}
/**
* @return
*/
public Integer getDatabase() {
return database;
}
}
/**
* Move given {@code key} to database with {@code index}.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
*/
default Mono<Boolean> move(ByteBuffer key, int database) {
Assert.notNull(key, "Key must not be null!");
return move(Mono.just(new MoveCommand(key, database))).next().map(BooleanResponse::getOutput);
}
/**
* Move keys one-by-one between databases.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} to move along with the move result.
* @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
*/
Flux<BooleanResponse<MoveCommand>> move(Publisher<MoveCommand> commands);
}