Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the NOVALUES option of HSCAN #3741

Merged
merged 1 commit into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/main/java/redis/clients/jedis/CommandObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,10 @@ public final CommandObject<ScanResult<Map.Entry<String, String>>> hscan(String k
return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params), BuilderFactory.HSCAN_RESPONSE);
}

public final CommandObject<ScanResult<String>> hscanNoValues(String key, String cursor, ScanParams params) {
return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params).add(NOVALUES), BuilderFactory.SCAN_RESPONSE);
}

public final CommandObject<Long> hstrlen(String key, String field) {
return new CommandObject<>(commandArguments(HSTRLEN).key(key).add(field), BuilderFactory.LONG);
}
Expand All @@ -1136,6 +1140,10 @@ public final CommandObject<ScanResult<Map.Entry<byte[], byte[]>>> hscan(byte[] k
return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params), BuilderFactory.HSCAN_BINARY_RESPONSE);
}

public final CommandObject<ScanResult<byte[]>> hscanNoValues(byte[] key, byte[] cursor, ScanParams params) {
return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params).add(NOVALUES), BuilderFactory.SCAN_BINARY_RESPONSE);
}

public final CommandObject<Long> hstrlen(byte[] key, byte[] field) {
return new CommandObject<>(commandArguments(HSTRLEN).key(key).add(field), BuilderFactory.LONG);
}
Expand Down
17 changes: 12 additions & 5 deletions src/main/java/redis/clients/jedis/Jedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -4413,18 +4413,19 @@ public ScanResult<byte[]> scan(final byte[] cursor, final ScanParams params, fin
return connection.executeCommand(commandObjects.scan(cursor, params, type));
}

@Override
public ScanResult<Map.Entry<byte[], byte[]>> hscan(final byte[] key, final byte[] cursor) {
return hscan(key, cursor, new ScanParams());
}

@Override
public ScanResult<Map.Entry<byte[], byte[]>> hscan(final byte[] key, final byte[] cursor,
final ScanParams params) {
checkIsInMultiOrPipeline();
return connection.executeCommand(commandObjects.hscan(key, cursor, params));
}

@Override
public ScanResult<byte[]> hscanNoValues(final byte[] key, final byte[] cursor, final ScanParams params) {
checkIsInMultiOrPipeline();
return connection.executeCommand(commandObjects.hscanNoValues(key, cursor, params));
}

@Override
public ScanResult<byte[]> sscan(final byte[] key, final byte[] cursor) {
return sscan(key, cursor, new ScanParams());
Expand Down Expand Up @@ -8617,6 +8618,12 @@ public ScanResult<Map.Entry<String, String>> hscan(final String key, final Strin
return connection.executeCommand(commandObjects.hscan(key, cursor, params));
}

@Override
public ScanResult<String> hscanNoValues(final String key, final String cursor, final ScanParams params) {
checkIsInMultiOrPipeline();
return connection.executeCommand(commandObjects.hscanNoValues(key, cursor, params));
}

@Override
public ScanResult<String> sscan(final String key, final String cursor, final ScanParams params) {
checkIsInMultiOrPipeline();
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/redis/clients/jedis/Protocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ public static enum Keyword implements Rawable {
DELETE, LIBRARYNAME, WITHCODE, DESCRIPTION, GETKEYS, GETKEYSANDFLAGS, DOCS, FILTERBY, DUMP,
MODULE, ACLCAT, PATTERN, DOCTOR, LATEST, HISTORY, USAGE, SAMPLES, PURGE, STATS, LOADEX, CONFIG, ARGS, RANK,
NOW, VERSION, ADDR, SKIPME, USER, LADDR,
CHANNELS, NUMPAT, NUMSUB, SHARDCHANNELS, SHARDNUMSUB;
CHANNELS, NUMPAT, NUMSUB, SHARDCHANNELS, SHARDNUMSUB, NOVALUES;

private final byte[] raw;

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/redis/clients/jedis/UnifiedJedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,11 @@ public ScanResult<Map.Entry<String, String>> hscan(String key, String cursor, Sc
return executeCommand(commandObjects.hscan(key, cursor, params));
}

@Override
public ScanResult<String> hscanNoValues(String key, String cursor, ScanParams params) {
return executeCommand(commandObjects.hscanNoValues(key, cursor, params));
}

@Override
public long hstrlen(String key, String field) {
return executeCommand(commandObjects.hstrlen(key, field));
Expand All @@ -1574,6 +1579,11 @@ public ScanResult<Map.Entry<byte[], byte[]>> hscan(byte[] key, byte[] cursor, Sc
return executeCommand(commandObjects.hscan(key, cursor, params));
}

@Override
public ScanResult<byte[]> hscanNoValues(byte[] key, byte[] cursor, ScanParams params) {
return executeCommand(commandObjects.hscanNoValues(key, cursor, params));
}

@Override
public long hstrlen(byte[] key, byte[] field) {
return executeCommand(commandObjects.hstrlen(key, field));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@ default ScanResult<Map.Entry<byte[], byte[]>> hscan(byte[] key, byte[] cursor) {
return hscan(key, cursor, new ScanParams());
}

default ScanResult<byte[]> hscanNoValues(byte[] key, byte[] cursor) {
return hscanNoValues(key, cursor, new ScanParams());
}

ScanResult<Map.Entry<byte[], byte[]>> hscan(byte[] key, byte[] cursor, ScanParams params);

ScanResult<byte[]> hscanNoValues(byte[] key, byte[] cursor, ScanParams params);

long hstrlen(byte[] key, byte[] field);

}
6 changes: 6 additions & 0 deletions src/main/java/redis/clients/jedis/commands/HashCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ default ScanResult<Map.Entry<String, String>> hscan(String key, String cursor) {
return hscan(key, cursor, new ScanParams());
}

default ScanResult<String> hscanNoValues(String key, String cursor) {
return hscanNoValues(key, cursor, new ScanParams());
}

ScanResult<Map.Entry<String, String>> hscan(String key, String cursor, ScanParams params);

ScanResult<String> hscanNoValues(String key, String cursor, ScanParams params);

long hstrlen(String key, String field);
}
164 changes: 151 additions & 13 deletions src/test/java/redis/clients/jedis/commands/jedis/HashesCommandsTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package redis.clients.jedis.commands.jedis;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
Expand All @@ -10,13 +12,15 @@
import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.Test;

Expand Down Expand Up @@ -339,35 +343,56 @@ public void hgetAllPipeline() {

@Test
public void hscan() {
jedis.hset("foo", "b", "b");
jedis.hset("foo", "a", "a");
jedis.hset("foo", "b", "y");
jedis.hset("foo", "a", "x");

ScanResult<Map.Entry<String, String>> result = jedis.hscan("foo", SCAN_POINTER_START);

assertEquals(SCAN_POINTER_START, result.getCursor());
assertFalse(result.getResult().isEmpty());
assertEquals(2, result.getResult().size());

assertThat(
result.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()),
containsInAnyOrder("a", "b"));
assertThat(
result.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()),
containsInAnyOrder("x", "y"));

// binary
jedis.hset(bfoo, bbar, bcar);

ScanResult<Map.Entry<byte[], byte[]>> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY);

assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes());
assertFalse(bResult.getResult().isEmpty());
assertEquals(1, bResult.getResult().size());

assertThat(
bResult.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()),
containsInAnyOrder(bbar));
assertThat(
bResult.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()),
containsInAnyOrder(bcar));
}

@Test
public void hscanMatch() {
ScanParams params = new ScanParams();
params.match("a*");

jedis.hset("foo", "b", "b");
jedis.hset("foo", "a", "a");
jedis.hset("foo", "aa", "aa");
jedis.hset("foo", "b", "y");
jedis.hset("foo", "a", "x");
jedis.hset("foo", "aa", "xx");
ScanResult<Map.Entry<String, String>> result = jedis.hscan("foo", SCAN_POINTER_START, params);

assertEquals(SCAN_POINTER_START, result.getCursor());
assertFalse(result.getResult().isEmpty());
assertEquals(2, result.getResult().size());

assertThat(
result.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()),
containsInAnyOrder("a", "aa"));
assertThat(
result.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()),
containsInAnyOrder("x", "xx"));

// binary
params = new ScanParams();
Expand All @@ -379,10 +404,17 @@ public void hscanMatch() {
jedis.hset(bfoo, bbar3, bcar);

ScanResult<Map.Entry<byte[], byte[]>> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY,
params);
params);

assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes());
assertFalse(bResult.getResult().isEmpty());
assertEquals(4, bResult.getResult().size());

assertThat(
bResult.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()),
containsInAnyOrder(bbar, bbar1, bbar2, bbar3));
assertThat(
bResult.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()),
containsInAnyOrder(bcar, bcar, bcar, bcar));
}

@Test
Expand All @@ -391,13 +423,20 @@ public void hscanCount() {
params.count(2);

for (int i = 0; i < 10; i++) {
jedis.hset("foo", "a" + i, "a" + i);
jedis.hset("foo", "a" + i, "x" + i);
}

ScanResult<Map.Entry<String, String>> result = jedis.hscan("foo", SCAN_POINTER_START, params);

assertFalse(result.getResult().isEmpty());

assertThat(
result.getResult().stream().map(Map.Entry::getKey).map(s -> s.substring(0, 1)).collect(Collectors.toSet()),
containsInAnyOrder("a"));
assertThat(
result.getResult().stream().map(Map.Entry::getValue).map(s -> s.substring(0, 1)).collect(Collectors.toSet()),
containsInAnyOrder("x"));

// binary
params = new ScanParams();
params.count(2);
Expand All @@ -407,10 +446,109 @@ public void hscanCount() {
jedis.hset(bfoo, bbar2, bcar);
jedis.hset(bfoo, bbar3, bcar);

ScanResult<Map.Entry<byte[], byte[]>> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY,
params);
ScanResult<Map.Entry<byte[], byte[]>> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params);

assertFalse(bResult.getResult().isEmpty());

assertThat(
bResult.getResult().stream().map(Map.Entry::getKey)
.map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()),
containsInAnyOrder(Arrays.toString(bbar)));
assertThat(
bResult.getResult().stream().map(Map.Entry::getValue)
.map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()),
containsInAnyOrder(Arrays.toString(bcar)));
}

@Test
public void hscanNoValues() {
jedis.hset("foo", "b", "y");
jedis.hset("foo", "a", "x");

ScanResult<String> result = jedis.hscanNoValues("foo", SCAN_POINTER_START);

assertEquals(SCAN_POINTER_START, result.getCursor());
assertEquals(2, result.getResult().size());

assertThat(result.getResult(), containsInAnyOrder("a", "b"));

// binary
jedis.hset(bfoo, bbar, bcar);

ScanResult<byte[]> bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY);

assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes());
assertEquals(1, bResult.getResult().size());

assertThat(bResult.getResult(), containsInAnyOrder(bbar));
}

@Test
public void hscanNoValuesMatch() {
ScanParams params = new ScanParams();
params.match("a*");

jedis.hset("foo", "b", "y");
jedis.hset("foo", "a", "x");
jedis.hset("foo", "aa", "xx");
ScanResult<String> result = jedis.hscanNoValues("foo", SCAN_POINTER_START, params);

assertEquals(SCAN_POINTER_START, result.getCursor());
assertEquals(2, result.getResult().size());

assertThat(result.getResult(), containsInAnyOrder("a", "aa"));

// binary
params = new ScanParams();
params.match(bbarstar);

jedis.hset(bfoo, bbar, bcar);
jedis.hset(bfoo, bbar1, bcar);
jedis.hset(bfoo, bbar2, bcar);
jedis.hset(bfoo, bbar3, bcar);

ScanResult<byte[]> bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY, params);

assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes());
assertEquals(4, bResult.getResult().size());

assertThat(bResult.getResult(), containsInAnyOrder(bbar, bbar1, bbar2, bbar3));
}

@Test
public void hscanNoValuesCount() {
ScanParams params = new ScanParams();
params.count(2);

for (int i = 0; i < 10; i++) {
jedis.hset("foo", "a" + i, "a" + i);
}

ScanResult<String> result = jedis.hscanNoValues("foo", SCAN_POINTER_START, params);

assertFalse(result.getResult().isEmpty());

assertThat(
result.getResult().stream().map(s -> s.substring(0, 1)).collect(Collectors.toSet()),
containsInAnyOrder("a"));

// binary
params = new ScanParams();
params.count(2);

jedis.hset(bfoo, bbar, bcar);
jedis.hset(bfoo, bbar1, bcar);
jedis.hset(bfoo, bbar2, bcar);
jedis.hset(bfoo, bbar3, bcar);

ScanResult<byte[]> bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY, params);

assertFalse(bResult.getResult().isEmpty());

assertThat(
bResult.getResult().stream()
.map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()),
containsInAnyOrder(Arrays.toString(bbar)));
}

@Test
Expand Down
Loading
Loading