Skip to content

Commit c8877ad

Browse files
committed
Polishing.
Add since tags, extract KeyUtil to replace duplicate calls to Arrays.copyOfRange(…) to split a concatenated list of elements into the first and remaining elements. See #3250
1 parent 100c7f0 commit c8877ad

File tree

6 files changed

+146
-38
lines changed

6 files changed

+146
-38
lines changed

src/main/java/org/springframework/data/redis/connection/RedisStringCommands.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,28 @@
3939
public interface RedisStringCommands {
4040

4141
enum BitOperation {
42-
AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR, ONE;
42+
43+
AND, OR, XOR, NOT,
44+
45+
/**
46+
* @since 4.1
47+
*/
48+
DIFF,
49+
50+
/**
51+
* @since 4.1
52+
*/
53+
DIFF1,
54+
55+
/**
56+
* @since 4.1
57+
*/
58+
ANDOR,
59+
60+
/**
61+
* @since 4.1
62+
*/
63+
ONE;
4364
}
4465

4566
/**

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.data.redis.core.ScanIteration;
3636
import org.springframework.data.redis.core.ScanOptions;
3737
import org.springframework.data.redis.util.ByteUtils;
38+
import org.springframework.data.redis.util.KeyUtils;
3839
import org.springframework.util.Assert;
3940

4041
/**
@@ -320,25 +321,25 @@ public Set<byte[]> sDiff(byte[]... keys) {
320321
}
321322
}
322323

323-
byte[] source = keys[0];
324-
byte[][] others = Arrays.copyOfRange(keys, 1, keys.length);
324+
return KeyUtils.splitKeys(keys, (source, others) -> {
325325

326-
ByteArraySet values = new ByteArraySet(sMembers(source));
327-
Collection<Set<byte[]>> resultList = connection.getClusterCommandExecutor()
328-
.executeMultiKeyCommand(
329-
(JedisMultiKeyClusterCommandCallback<Set<byte[]>>) (client, key) -> client.smembers(key),
330-
Arrays.asList(others))
331-
.resultsAsList();
326+
ByteArraySet values = new ByteArraySet(sMembers(source));
327+
Collection<Set<byte[]>> resultList = connection.getClusterCommandExecutor()
328+
.executeMultiKeyCommand(
329+
(JedisMultiKeyClusterCommandCallback<Set<byte[]>>) (client, key) -> client.smembers(key),
330+
Arrays.asList(others))
331+
.resultsAsList();
332332

333-
if (values.isEmpty()) {
334-
return Collections.emptySet();
335-
}
333+
if (values.isEmpty()) {
334+
return Collections.emptySet();
335+
}
336336

337-
for (Set<byte[]> singleNodeValue : resultList) {
338-
values.removeAll(singleNodeValue);
339-
}
337+
for (Set<byte[]> singleNodeValue : resultList) {
338+
values.removeAll(singleNodeValue);
339+
}
340340

341-
return values.asRawSet();
341+
return values.asRawSet();
342+
});
342343
}
343344

344345
@Override

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterSetCommands.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.data.redis.connection.lettuce.LettuceClusterConnection.LettuceMultiKeyClusterCommandCallback;
2727
import org.springframework.data.redis.connection.util.ByteArraySet;
2828
import org.springframework.data.redis.util.ByteUtils;
29+
import org.springframework.data.redis.util.KeyUtils;
2930
import org.springframework.util.Assert;
3031

3132
/**
@@ -191,24 +192,24 @@ public Set<byte[]> sDiff(byte[]... keys) {
191192
return super.sDiff(keys);
192193
}
193194

194-
byte[] source = keys[0];
195-
byte[][] others = Arrays.copyOfRange(keys, 1, keys.length);
195+
return KeyUtils.splitKeys(keys, (source, others) -> {
196196

197-
ByteArraySet values = new ByteArraySet(sMembers(source));
198-
Collection<Set<byte[]>> nodeResult = connection.getClusterCommandExecutor()
199-
.executeMultiKeyCommand((LettuceMultiKeyClusterCommandCallback<Set<byte[]>>) RedisSetCommands::smembers,
200-
Arrays.asList(others))
201-
.resultsAsList();
197+
ByteArraySet values = new ByteArraySet(sMembers(source));
198+
Collection<Set<byte[]>> nodeResult = connection.getClusterCommandExecutor()
199+
.executeMultiKeyCommand((LettuceMultiKeyClusterCommandCallback<Set<byte[]>>) RedisSetCommands::smembers,
200+
Arrays.asList(others))
201+
.resultsAsList();
202202

203-
if (values.isEmpty()) {
204-
return Collections.emptySet();
205-
}
203+
if (values.isEmpty()) {
204+
return Collections.emptySet();
205+
}
206206

207-
for (Set<byte[]> toSubstract : nodeResult) {
208-
values.removeAll(toSubstract);
209-
}
207+
for (Set<byte[]> toSubstract : nodeResult) {
208+
values.removeAll(toSubstract);
209+
}
210210

211-
return values.asRawSet();
211+
return values.asRawSet();
212+
});
212213
}
213214

214215
@Override

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStringCommands.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import reactor.core.publisher.Mono;
2323

2424
import java.nio.ByteBuffer;
25-
import java.util.Arrays;
2625
import java.util.List;
2726
import java.util.stream.Collectors;
2827

@@ -39,6 +38,7 @@
3938
import org.springframework.data.redis.connection.ReactiveStringCommands;
4039
import org.springframework.data.redis.connection.RedisStringCommands;
4140
import org.springframework.data.redis.core.types.Expiration;
41+
import org.springframework.data.redis.util.KeyUtils;
4242
import org.springframework.util.Assert;
4343

4444
/**
@@ -370,9 +370,12 @@ public Flux<NumericResponse<BitOpCommand, Long>> bitOp(Publisher<BitOpCommand> c
370370
Assert.isTrue(sourceKeys.length == 1, "BITOP NOT does not allow more than 1 source key.");
371371
yield reactiveCommands.bitopNot(destinationKey, sourceKeys[0]);
372372
}
373-
case DIFF -> reactiveCommands.bitopDiff(destinationKey, sourceKeys[0], Arrays.copyOfRange(sourceKeys, 1, sourceKeys.length));
374-
case DIFF1 -> reactiveCommands.bitopDiff1(destinationKey, sourceKeys[0], Arrays.copyOfRange(sourceKeys, 1, sourceKeys.length));
375-
case ANDOR -> reactiveCommands.bitopAndor(destinationKey, sourceKeys[0], Arrays.copyOfRange(sourceKeys, 1, sourceKeys.length));
373+
case DIFF -> KeyUtils.splitKeys(sourceKeys,
374+
(first, remaining) -> reactiveCommands.bitopDiff(destinationKey, first, remaining));
375+
case DIFF1 -> KeyUtils.splitKeys(sourceKeys,
376+
(first, remaining) -> reactiveCommands.bitopDiff1(destinationKey, first, remaining));
377+
case ANDOR -> KeyUtils.splitKeys(sourceKeys,
378+
(first, remaining) -> reactiveCommands.bitopAndor(destinationKey, first, remaining));
376379
case ONE -> reactiveCommands.bitopOne(destinationKey, sourceKeys);
377380
};
378381

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceStringCommands.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,19 @@
1818
import io.lettuce.core.BitFieldArgs;
1919
import io.lettuce.core.api.async.RedisStringAsyncCommands;
2020

21-
import java.util.Arrays;
2221
import java.util.List;
2322
import java.util.Map;
2423

2524
import org.jspecify.annotations.NonNull;
2625
import org.jspecify.annotations.NullUnmarked;
2726
import org.jspecify.annotations.Nullable;
27+
2828
import org.springframework.data.domain.Range;
2929
import org.springframework.data.redis.connection.BitFieldSubCommands;
3030
import org.springframework.data.redis.connection.RedisStringCommands;
3131
import org.springframework.data.redis.connection.convert.Converters;
3232
import org.springframework.data.redis.core.types.Expiration;
33+
import org.springframework.data.redis.util.KeyUtils;
3334
import org.springframework.util.Assert;
3435

3536
/**
@@ -309,9 +310,9 @@ public Long bitOp(@NonNull BitOperation op, byte @NonNull [] destination, byte @
309310
}
310311
yield it.bitopNot(destination, keys[0]);
311312
}
312-
case DIFF -> it.bitopDiff(destination, keys[0], Arrays.copyOfRange(keys, 1, keys.length));
313-
case DIFF1 -> it.bitopDiff1(destination, keys[0], Arrays.copyOfRange(keys, 1, keys.length));
314-
case ANDOR -> it.bitopAndor(destination, keys[0], Arrays.copyOfRange(keys, 1, keys.length));
313+
case DIFF -> KeyUtils.splitKeys(keys, (first, remaining) -> it.bitopDiff(destination, first, remaining));
314+
case DIFF1 -> KeyUtils.splitKeys(keys, (first, remaining) -> it.bitopDiff1(destination, first, remaining));
315+
case ANDOR -> KeyUtils.splitKeys(keys, (first, remaining) -> it.bitopAndor(destination, first, remaining));
315316
case ONE -> it.bitopOne(destination, keys);
316317
});
317318
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.util;
17+
18+
import java.util.Arrays;
19+
20+
/**
21+
* Utility class for Redis keys.
22+
*
23+
* @author Mark Paluch
24+
* @since 4.1
25+
*/
26+
public abstract class KeyUtils {
27+
28+
// ---------------------------------------------------------------------
29+
// General convenience methods for working with keys
30+
// ---------------------------------------------------------------------
31+
32+
/**
33+
* Utility method to split an array concatenated of keys into the first key and the remaining keys and invoke the
34+
* given function.
35+
*
36+
* @param keys array of keys to be separated into the first one and the remaining ones.
37+
* @param function function to be invoked with the first and remaining keys as input arguments.
38+
* @return result of the {@link SourceKeysFunction}.
39+
*/
40+
public static <T, R> R splitKeys(T[] keys, SourceKeysFunction<T, R> function) {
41+
42+
if (keys.length == 0) {
43+
throw new IllegalArgumentException("Keys array must contain at least one element");
44+
}
45+
46+
T firstKey = keys[0];
47+
T[] otherKeys = Arrays.copyOfRange(keys, 1, keys.length);
48+
return function.apply(firstKey, otherKeys);
49+
}
50+
51+
/**
52+
* Represents a function that accepts two arguments of the same base type and produces a result while the second
53+
* argument is an array of {@code T}. This is similar to a {@link java.util.function.BiFunction} but restricts both
54+
* arguments to be of the same type. Typically used in arrangements where a composite collection of keys is split into
55+
* the first key and the remaining keys.
56+
* <p>
57+
* This is a <a href="package-summary.html">functional interface</a> whose functional method is
58+
* {@link #apply(Object, Object)}.
59+
*
60+
* @param <T> the type of the first argument to the function.
61+
* @param <R> the type of the result of the function.
62+
*/
63+
public interface SourceKeysFunction<T, R> {
64+
65+
/**
66+
* Applies this function to the given arguments.
67+
*
68+
* @param firstKey the first key argument.
69+
* @param otherKeys the other keys function argument.
70+
* @return the function result.
71+
*/
72+
R apply(T firstKey, T[] otherKeys);
73+
74+
}
75+
76+
// utility constructor
77+
private KeyUtils() {
78+
79+
}
80+
81+
}

0 commit comments

Comments
 (0)