DATAREDIS-562 - Add reactive BitField support.

Original pull request: #227.
This commit is contained in:
Mark Paluch
2018-05-03 14:52:16 +02:00
parent 10f8553c5a
commit d2871ec032
6 changed files with 208 additions and 5 deletions

View File

@@ -902,6 +902,86 @@ public interface ReactiveStringCommands {
*/
Flux<NumericResponse<BitCountCommand, Long>> bitCount(Publisher<BitCountCommand> commands);
/**
* {@code BITFIELD} command parameters.
*
* @author Mark Paluch
* @see <a href="http://redis.io/commands/bitfield">Redis Documentation: BITFIELD</a>
* @since 2.1
*/
class BitFieldCommand extends KeyCommand {
private @Nullable BitFieldSubCommands subcommands;
private BitFieldCommand(ByteBuffer key, @Nullable BitFieldSubCommands subcommands) {
super(key);
this.subcommands = subcommands;
}
/**
* Creates a new {@link BitFieldCommand} given a {@literal key}.
*
* @param key must not be {@literal null}.
* @return a new {@link BitFieldCommand} for a {@literal key}.
*/
public static BitFieldCommand bitField(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new BitFieldCommand(key, null);
}
/**
* Applies the {@link BitFieldSubCommands}. Constructs a new command instance with all previously configured
* properties.
*
* @param commands must not be {@literal null}.
* @return a new {@link BitFieldSubCommands} with {@link BitFieldSubCommands} applied.
*/
public BitFieldCommand commands(BitFieldSubCommands commands) {
Assert.notNull(commands, "BitFieldCommands must not be null!");
return new BitFieldCommand(getKey(), commands);
}
public BitFieldSubCommands getSubCommands() {
return subcommands;
}
}
/**
* Get / Manipulate specific integer fields of varying bit widths and arbitrary non (necessary) aligned offset stored
* at a given {@code key}.
*
* @param key must not be {@literal null}.
* @param subCommands
* @return
* @see <a href="http://redis.io/commands/bitfield">Redis Documentation: BITFIELD</a>
* @since 2.1
*/
default Mono<List<Long>> bitField(ByteBuffer key, BitFieldSubCommands subCommands) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(subCommands, "BitFieldSubCommands must not be null!");
return bitField(Mono.just(BitFieldCommand.bitField(key).commands(subCommands))).map(CommandResponse::getOutput)
.next();
}
/**
* Get / Manipulate specific integer fields of varying bit widths and arbitrary non (necessary) aligned offset stored
* at a given {@code key}.
*
* @param commands must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/bitfield">Redis Documentation: BITFIELD</a>
* @since 2.1
*/
Flux<MultiValueResponse<BitFieldCommand, Long>> bitField(Publisher<BitFieldCommand> commands);
/**
* {@code BITOP} command parameters.
*

View File

@@ -15,12 +15,14 @@
*/
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.BitFieldArgs;
import io.lettuce.core.SetArgs;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.stream.Collectors;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.Range;
@@ -328,11 +330,20 @@ class LettuceReactiveStringCommands implements ReactiveStringCommands {
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#bitCount(org.reactivestreams.Publisher)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#bitField(org.reactivestreams.Publisher)
*/
@Override
public Flux<NumericResponse<BitFieldCommand, Long>> bitField(Publisher<BitFieldCommand> commands) {
return null;
public Flux<MultiValueResponse<BitFieldCommand, Long>> bitField(Publisher<BitFieldCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
Assert.notNull(command.getKey(), "Key must not be null!");
BitFieldArgs args = LettuceConverters.toBitFieldArgs(command.getSubCommands());
return cmd.bitfield(command.getKey(), args).collectList().map(value -> new MultiValueResponse<>(command,
value.stream().map(v -> v.getValueOrElse(null)).collect(Collectors.toList())));
}));
}
/*

View File

@@ -29,6 +29,7 @@ import java.util.Map;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.connection.ReactiveStringCommands;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.core.types.Expiration;
@@ -318,6 +319,18 @@ class DefaultReactiveValueOperations<K, V> implements ReactiveValueOperations<K,
return createMono(connection -> connection.getBit(rawKey(key), offset));
}
/* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveValueOperations#bitField(java.lang.Object, org.springframework.data.redis.connection.BitFieldSubCommands)
*/
@Override
public Mono<List<Long>> bitField(K key, BitFieldSubCommands subCommands) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(subCommands, "BitFieldSubCommands must not be null!");
return createMono(connection -> connection.bitField(rawKey(key), subCommands));
}
/* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveValueOperations#delete(java.lang.Object)
*/

View File

@@ -22,6 +22,8 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.data.redis.connection.BitFieldSubCommands;
/**
* Reactive Redis operations for simple (or in Redis terminology 'string') values.
*
@@ -235,6 +237,17 @@ public interface ReactiveValueOperations<K, V> {
*/
Mono<Boolean> getBit(K key, long offset);
/**
* Get / Manipulate specific integer fields of varying bit widths and arbitrary non (necessary) aligned offset stored
* at a given {@code key}.
*
* @param key must not be {@literal null}.
* @param command must not be {@literal null}.
* @return
* @since 2.1
*/
Mono<List<Long>> bitField(K key, BitFieldSubCommands command);
/**
* Removes the given {@literal key}.
*

View File

@@ -16,13 +16,17 @@
package org.springframework.data.redis.connection.lettuce;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.Offset.*;
import org.springframework.data.redis.util.ByteUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoOperator;
import reactor.test.StepVerifier;
import java.nio.ByteBuffer;
@@ -46,6 +50,7 @@ import org.springframework.data.redis.connection.ReactiveStringCommands.SetComma
import org.springframework.data.redis.connection.RedisStringCommands.BitOperation;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.test.util.HexStringUtils;
import org.springframework.data.redis.util.ByteUtils;
/**
* @author Christoph Strobl
@@ -346,6 +351,63 @@ public class LettuceReactiveStringCommandsTests extends LettuceReactiveCommandsT
.verifyComplete();
}
@Test // DATAREDIS-562
public void bitFieldSetShouldWorkCorrectly() {
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER, create().set(INT_8).valueAt(offset(0L)).to(10L)))
.expectNext(Collections.singletonList(0L)).verifyComplete();
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER, create().set(INT_8).valueAt(offset(0L)).to(20L)))
.expectNext(Collections.singletonList(10L)).verifyComplete();
}
@Test // DATAREDIS-562
public void bitFieldGetShouldWorkCorrectly() {
StepVerifier.create(connection.stringCommands().bitField(KEY_1_BBUFFER, create().get(INT_8).valueAt(offset(0L))))
.expectNext(Collections.singletonList(0L)).verifyComplete();
}
@Test // DATAREDIS-562
public void bitFieldIncrByShouldWorkCorrectly() {
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER, create().incr(INT_8).valueAt(offset(100L)).by(1L)))
.expectNext(Collections.singletonList(1L)).verifyComplete();
}
@Test // DATAREDIS-562
public void bitFieldIncrByWithOverflowShouldWorkCorrectly() {
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER,
create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(1L)).verifyComplete();
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER,
create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(2L)).verifyComplete();
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER,
create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(3L)).verifyComplete();
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER,
create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(null)).verifyComplete();
}
@Test // DATAREDIS-562
public void bitfieldShouldAllowMultipleSubcommands() {
StepVerifier
.create(connection.stringCommands().bitField(KEY_1_BBUFFER,
create().incr(signed(5)).valueAt(offset(100L)).by(1L).get(unsigned(4)).valueAt(0L)))
.expectNext(Arrays.asList(1L, 0L)).verifyComplete();
}
@Test // DATAREDIS-525
public void bitOpAndShouldWorkAsExpected() {

View File

@@ -17,6 +17,10 @@ package org.springframework.data.redis.core;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldIncrBy.Overflow.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldType.*;
import static org.springframework.data.redis.connection.BitFieldSubCommands.Offset.offset;
import reactor.test.StepVerifier;
@@ -24,6 +28,7 @@ import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -372,6 +377,25 @@ public class DefaultReactiveValueOperationsIntegrationTests<K, V> {
StepVerifier.create(valueOperations.getBit(key, 1)).expectNext(false).expectComplete();
}
@Test // DATAREDIS-562
public void bitField() {
K key = keyFactory.instance();
StepVerifier
.create(valueOperations.bitField(key, create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(1L)).verifyComplete();
StepVerifier
.create(valueOperations.bitField(key, create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(2L)).verifyComplete();
StepVerifier
.create(valueOperations.bitField(key, create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(3L)).verifyComplete();
StepVerifier
.create(valueOperations.bitField(key, create().incr(unsigned(2)).valueAt(offset(102L)).overflow(FAIL).by(1L)))
.expectNext(Collections.singletonList(null)).verifyComplete();
}
@Test // DATAREDIS-602
public void delete() {