Add support for SMISMEMBER.
Allows querying with a single command whether multiple elements are members of a set. Also, refine DefaultRedisSet.containsAll(…) implementation. Closes #2037 Original Pull Request: #2105
This commit is contained in:
committed by
Christoph Strobl
parent
280d9541da
commit
90095d1c89
@@ -1169,6 +1169,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
|
||||
return convertAndReturn(delegate.sIsMember(key, value), Converters.identityConverter());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sIsMember(byte[], byte[]...)
|
||||
*/
|
||||
@Override
|
||||
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
|
||||
return convertAndReturn(delegate.sMIsMember(key, values), Converters.identityConverter());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sMembers(byte[])
|
||||
@@ -2853,6 +2862,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
|
||||
return sIsMember(serialize(key), serialize(value));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.StringRedisConnection#sMIsMember(java.lang.String, java.lang.String...)
|
||||
*/
|
||||
@Override
|
||||
public List<Boolean> sMIsMember(String key, String... values) {
|
||||
return sMIsMember(serialize(key), serializeMulti(values));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.StringRedisConnection#sMembers(java.lang.String)
|
||||
|
||||
@@ -835,6 +835,13 @@ public interface DefaultedRedisConnection extends RedisConnection {
|
||||
return setCommands().sIsMember(key, value);
|
||||
}
|
||||
|
||||
/** @deprecated in favor of {@link RedisConnection#setCommands()}}. */
|
||||
@Override
|
||||
@Deprecated
|
||||
default List<Boolean> sMIsMember(byte[] key, byte[]... value) {
|
||||
return setCommands().sMIsMember(key, value);
|
||||
}
|
||||
|
||||
/** @deprecated in favor of {@link RedisConnection#setCommands()}}. */
|
||||
@Override
|
||||
@Deprecated
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.Command
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyScanCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -546,7 +547,7 @@ public interface ReactiveSetCommands {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if set at {@link SIsMemberCommand#getKey()} contains {@link SIsMemberCommand#getKey()}.
|
||||
* Check if set at {@link SIsMemberCommand#getKey()} contains {@link SIsMemberCommand#getValue()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
@@ -554,6 +555,86 @@ public interface ReactiveSetCommands {
|
||||
*/
|
||||
Flux<BooleanResponse<SIsMemberCommand>> sIsMember(Publisher<SIsMemberCommand> commands);
|
||||
|
||||
/**
|
||||
* {@code SMISMEMBER} command parameters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
*/
|
||||
class SMIsMemberCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> values;
|
||||
|
||||
private SMIsMemberCommand(@Nullable ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
super(key);
|
||||
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SMIsMemberCommand} given one or more {@literal values}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return a new {@link SMIsMemberCommand} for a {@literal value}.
|
||||
*/
|
||||
public static SMIsMemberCommand values(List<ByteBuffer> values) {
|
||||
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.notEmpty(values, "Values must not be empty!");
|
||||
|
||||
return new SMIsMemberCommand(null, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the {@literal set} key. Constructs a new command instance with all previously configured properties.
|
||||
*
|
||||
* @param set must not be {@literal null}.
|
||||
* @return a new {@link SMIsMemberCommand} with {@literal set} applied.
|
||||
*/
|
||||
public SMIsMemberCommand of(ByteBuffer set) {
|
||||
|
||||
Assert.notNull(set, "Set key must not be null!");
|
||||
|
||||
return new SMIsMemberCommand(set, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public List<ByteBuffer> getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if set at {@code key} contains one or more {@code values}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
*/
|
||||
default Mono<List<Boolean>> sMIsMember(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Value must not be null!");
|
||||
|
||||
return sMIsMember(Mono.just(SMIsMemberCommand.values(values).of(key))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if set at {@link SMIsMemberCommand#getKey()} contains {@link SMIsMemberCommand#getValues()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
*/
|
||||
Flux<MultiValueResponse<SMIsMemberCommand, Boolean>> sMIsMember(Publisher<SMIsMemberCommand> commands);
|
||||
|
||||
/**
|
||||
* {@code SINTER} command parameters.
|
||||
*
|
||||
|
||||
@@ -108,6 +108,18 @@ public interface RedisSetCommands {
|
||||
@Nullable
|
||||
Boolean sIsMember(byte[] key, byte[] value);
|
||||
|
||||
/**
|
||||
* Check if set at {@code key} contains one or more {@code values}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
*/
|
||||
@Nullable
|
||||
List<Boolean> sMIsMember(byte[] key, byte[]... values);
|
||||
|
||||
/**
|
||||
* Diff all sets for given {@code keys}.
|
||||
*
|
||||
|
||||
@@ -1043,6 +1043,19 @@ public interface StringRedisConnection extends RedisConnection {
|
||||
*/
|
||||
Boolean sIsMember(String key, String value);
|
||||
|
||||
/**
|
||||
* Check if set at {@code key} contains one or more {@code values}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
* @see RedisSetCommands#sMIsMember(byte[], byte[]...)
|
||||
*/
|
||||
@Nullable
|
||||
List<Boolean> sMIsMember(String key, String... values);
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@code keys}.
|
||||
*
|
||||
|
||||
@@ -177,6 +177,24 @@ class JedisClusterSetCommands implements RedisSetCommands {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sMIsMember(byte[], byte[]...)
|
||||
*/
|
||||
@Override
|
||||
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Value must not be null!");
|
||||
Assert.noNullElements(values, "Values must not contain null elements!");
|
||||
|
||||
try {
|
||||
return connection.getCluster().smismember(key, values);
|
||||
} catch (Exception ex) {
|
||||
throw convertJedisAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sInter(byte[][])
|
||||
|
||||
@@ -136,6 +136,20 @@ class JedisSetCommands implements RedisSetCommands {
|
||||
return connection.invoke().just(BinaryJedis::sismember, MultiKeyPipelineBase::sismember, key, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sMIsMember(byte[], byte[]...)
|
||||
*/
|
||||
@Override
|
||||
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.noNullElements(values, "Values must not contain null elements!");
|
||||
|
||||
return connection.invoke().just(BinaryJedis::smismember, MultiKeyPipelineBase::smismember, key, values);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sMembers(byte[])
|
||||
|
||||
@@ -22,11 +22,13 @@ import reactor.core.publisher.Mono;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyScanCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveSetCommands;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -163,6 +165,23 @@ class LettuceReactiveSetCommands implements ReactiveSetCommands {
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sMIsMember(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<SMIsMemberCommand, Boolean>> sMIsMember(Publisher<SMIsMemberCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValues(), "Values must not be null!");
|
||||
|
||||
return cmd.smismember(command.getKey(), command.getValues().toArray(new ByteBuffer[0])).collectList()
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInter(org.reactivestreams.Publisher)
|
||||
|
||||
@@ -137,6 +137,20 @@ class LettuceSetCommands implements RedisSetCommands {
|
||||
return connection.invoke().just(RedisSetAsyncCommands::sismember, key, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sMIsMember(byte[], byte[]...)
|
||||
*/
|
||||
@Override
|
||||
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
Assert.noNullElements(values, "Values must not contain null elements!");
|
||||
|
||||
return connection.invoke().just(RedisSetAsyncCommands::smismember, key, values);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisSetCommands#sMembers(byte[])
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -88,6 +89,18 @@ public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
|
||||
@Nullable
|
||||
Boolean isMember(Object o);
|
||||
|
||||
/**
|
||||
* Check if set at at the bound key contains one or more {@code values}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param objects
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
*/
|
||||
@Nullable
|
||||
Map<Object, Boolean> isMember(Object... objects);
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at the bound key and {@code key}.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
@@ -143,6 +144,15 @@ class DefaultBoundSetOperations<K, V> extends DefaultBoundKeyOperations<K> imple
|
||||
return ops.isMember(getKey(), o);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.BoundSetOperations#isMember(java.lang.Object...)
|
||||
*/
|
||||
@Override
|
||||
public Map<Object, Boolean> isMember(Object... objects) {
|
||||
return ops.isMember(getKey(), objects);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.BoundSetOperations#members()
|
||||
|
||||
@@ -22,7 +22,9 @@ import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -151,6 +153,34 @@ class DefaultReactiveSetOperations<K, V> implements ReactiveSetOperations<K, V>
|
||||
return createMono(connection -> connection.sIsMember(rawKey(key), rawValue((V) o)));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#isMember(java.lang.Object, java.lang.Object...)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Map<Object, Boolean>> isMember(K key, Object... objects) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return createMono(connection -> {
|
||||
|
||||
return Flux.fromArray((V[]) objects) //
|
||||
.map(this::rawValue) //
|
||||
.collectList() //
|
||||
.flatMap(rawValues -> connection.sMIsMember(rawKey(key), rawValues)) //
|
||||
.map(result -> {
|
||||
|
||||
Map<Object, Boolean> isMember = new LinkedHashMap<>(result.size());
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
isMember.put(objects[i], result.get(i));
|
||||
}
|
||||
|
||||
return isMember;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveSetOperations#intersect(java.lang.Object, java.lang.Object)
|
||||
|
||||
@@ -18,7 +18,9 @@ package org.springframework.data.redis.core;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
@@ -203,6 +205,34 @@ class DefaultSetOperations<K, V> extends AbstractOperations<K, V> implements Set
|
||||
return execute(connection -> connection.sIsMember(rawKey, rawValue), true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.SetOperations#isMember(java.lang.Object, java.lang.Object...)
|
||||
*/
|
||||
@Override
|
||||
public Map<Object, Boolean> isMember(K key, Object... objects) {
|
||||
|
||||
byte[] rawKey = rawKey(key);
|
||||
byte[][] rawValues = rawValues(objects);
|
||||
|
||||
return execute(connection -> {
|
||||
|
||||
List<Boolean> result = connection.sMIsMember(rawKey, rawValues);
|
||||
|
||||
if (result == null || result.size() != objects.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<Object, Boolean> isMember = new LinkedHashMap<>(result.size());
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
isMember.put(objects[i], result.get(i));
|
||||
}
|
||||
|
||||
return isMember;
|
||||
}, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.SetOperations#members(java.lang.Object)
|
||||
|
||||
@@ -19,6 +19,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Redis set specific operations.
|
||||
@@ -100,6 +101,17 @@ public interface ReactiveSetOperations<K, V> {
|
||||
*/
|
||||
Mono<Boolean> isMember(K key, Object o);
|
||||
|
||||
/**
|
||||
* Check if set at {@code key} contains one or more {@code values}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
*/
|
||||
Mono<Map<Object, Boolean>> isMember(K key, Object... objects);
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@code key} and {@code otherKey}.
|
||||
*
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.redis.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -108,6 +109,18 @@ public interface SetOperations<K, V> {
|
||||
@Nullable
|
||||
Boolean isMember(K key, Object o);
|
||||
|
||||
/**
|
||||
* Check if set at {@code key} contains one or more {@code values}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param objects
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
|
||||
*/
|
||||
@Nullable
|
||||
Map<Object, Boolean> isMember(K key, Object... objects);
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@code key} and {@code otherKey}.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.redis.support.collections;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -33,6 +34,7 @@ import org.springframework.data.redis.core.ScanOptions;
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DefaultRedisSet<E> extends AbstractRedisCollection<E> implements RedisSet<E> {
|
||||
|
||||
@@ -231,6 +233,23 @@ public class DefaultRedisSet<E> extends AbstractRedisCollection<E> implements Re
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.AbstractCollection#containsAll(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
|
||||
if (c.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<Object, Boolean> member = boundSetOps.isMember(c.toArray());
|
||||
checkResult(member);
|
||||
|
||||
return member.values().stream().reduce(true, (left, right) -> left && right);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.AbstractCollection#iterator()
|
||||
|
||||
Reference in New Issue
Block a user