Skip to content

Commit 76cbe51

Browse files
christophstroblmp911de
authored andcommitted
DATAREDIS-1196 - Add support for LPOS command.
Original pull request: #563.
1 parent 1a025fe commit 76cbe51

22 files changed

+718
-11
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,15 @@ public byte[] lPop(byte[] key) {
702702
return convertAndReturn(delegate.lPop(key), identityConverter);
703703
}
704704

705+
/*
706+
* (non-Javadoc)
707+
* @see org.springframework.data.redis.connection.RedisListCommands#lPos(byte[], byte[], java.lang.Integer, java.lang.Integer)
708+
*/
709+
@Override
710+
public List<Long> lPos(byte[] key, byte[] element, @Nullable Integer rank, @Nullable Integer count) {
711+
return convertAndReturn(delegate.lPos(key, element, rank, count), identityConverter);
712+
}
713+
705714
/*
706715
* (non-Javadoc)
707716
* @see org.springframework.data.redis.connection.RedisListCommands#lPush(byte[], byte[][])
@@ -2138,6 +2147,15 @@ public String lPop(String key) {
21382147
return convertAndReturn(delegate.lPop(serialize(key)), bytesToString);
21392148
}
21402149

2150+
/*
2151+
* (non-Javadoc)
2152+
* @see org.springframework.data.redis.connection.StringRedisConnection#lPos(java.lang.String, java.lang.String, java.lang.Integer, java.lang.Integer)
2153+
*/
2154+
@Override
2155+
public List<Long> lPos(String key, String element, @Nullable Integer rank, @Nullable Integer count) {
2156+
return lPos(serialize(key), serialize(element), rank, count);
2157+
}
2158+
21412159
/*
21422160
* (non-Javadoc)
21432161
* @see org.springframework.data.redis.connection.StringRedisConnection#lPush(java.lang.String, java.lang.String[])

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,13 @@ default Long rPush(byte[] key, byte[]... values) {
621621
return listCommands().rPush(key, values);
622622
}
623623

624+
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
625+
@Override
626+
@Deprecated
627+
default List<Long> lPos(byte[] key, byte[] element, @Nullable Integer rank, @Nullable Integer count) {
628+
return listCommands().lPos(key, element, rank, count);
629+
}
630+
624631
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
625632
@Override
626633
@Deprecated

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

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,125 @@ default Mono<Boolean> lTrim(ByteBuffer key, long start, long end) {
312312
*/
313313
Flux<BooleanResponse<RangeCommand>> lTrim(Publisher<RangeCommand> commands);
314314

315+
/**
316+
* {@code LPOS} command parameters.
317+
*
318+
* @author Christoph Strobl
319+
* @since 2.4
320+
* @see <a href="https://redis.io/commands/lpos">Redis Documentation: LPOS</a>
321+
*/
322+
class LPosCommand extends KeyCommand {
323+
324+
private final ByteBuffer element;
325+
private final @Nullable Integer count;
326+
private final @Nullable Integer rank;
327+
328+
private LPosCommand(@Nullable ByteBuffer key, ByteBuffer element, @Nullable Integer count, @Nullable Integer rank) {
329+
330+
super(key);
331+
this.element = element;
332+
this.count = count;
333+
this.rank = rank;
334+
}
335+
336+
/**
337+
* Creates a new {@link LPosCommand} for given {@literal element}.
338+
*
339+
* @param element
340+
* @return a new {@link LPosCommand} for {@literal element}.
341+
*/
342+
public static LPosCommand lPosOf(ByteBuffer element) {
343+
return new LPosCommand(null, element, null, null);
344+
}
345+
346+
/**
347+
* Applies the {@literal key}. Constructs a new command instance with all previously configured properties.
348+
*
349+
* @param key must not be {@literal null}.
350+
* @return a new {@link LPosCommand} with {@literal key} applied.
351+
*/
352+
public LPosCommand from(ByteBuffer key) {
353+
354+
Assert.notNull(key, "Key must not be null!");
355+
return new LPosCommand(key, element, count, rank);
356+
}
357+
358+
/**
359+
* Applies the {@literal count} parameter specifying the number of matches to return. Constructs a new command
360+
* instance with all previously configured properties.
361+
*
362+
* @param count can be {@literal null}.
363+
* @return a new {@link LPosCommand} with {@literal count} applied.
364+
*/
365+
public LPosCommand count(Integer count) {
366+
return new LPosCommand(getKey(), element, count, rank);
367+
}
368+
369+
/**
370+
* Applies the {@literal rank} parameter specifying the "rank" of the first element to return. Constructs a new
371+
* command instance with all previously configured properties.
372+
*
373+
* @param rank can be {@literal null}.
374+
* @return a new {@link LPosCommand} with {@literal count} applied.
375+
*/
376+
public LPosCommand rank(Integer rank) {
377+
return new LPosCommand(getKey(), element, count, rank);
378+
}
379+
380+
@Nullable
381+
public Integer getCount() {
382+
return count;
383+
}
384+
385+
@Nullable
386+
public Integer getRank() {
387+
return rank;
388+
}
389+
390+
@Nullable
391+
public ByteBuffer getElement() {
392+
return element;
393+
}
394+
}
395+
396+
/**
397+
* Get first index of the {@literal element} from list at {@literal key}.
398+
*
399+
* @param key must not be {@literal null}.
400+
* @param element
401+
* @return a {@link Mono} emitting the elements index.
402+
* @since 2.4
403+
* @see <a href="https://redis.io/commands/lindex">Redis Documentation: LINDEX</a>
404+
*/
405+
default Mono<Long> lPos(ByteBuffer key, ByteBuffer element) {
406+
407+
Assert.notNull(key, "Key must not be null!");
408+
409+
return lPos(LPosCommand.lPosOf(element).from(key)).next();
410+
}
411+
412+
/**
413+
* Get indices of the {@literal element} from list at {@link LPosCommand#getKey()}.
414+
*
415+
* @param command must not be {@literal null}.
416+
* @return a {@link Flux} emitting the elements indices one by one.
417+
* @since 2.4
418+
* @see <a href="https://redis.io/commands/lindex">Redis Documentation: LINDEX</a>
419+
*/
420+
default Flux<Long> lPos(LPosCommand command) {
421+
return lPos(Mono.just(command)).map(NumericResponse::getOutput);
422+
}
423+
424+
/**
425+
* Get indices of the {@literal element} from list at {@link LPosCommand#getKey()}.
426+
*
427+
* @param commands must not be {@literal null}.
428+
* @return a {@link Flux} emitting the elements indices one by one.
429+
* @since 2.4
430+
* @see <a href="https://redis.io/commands/lindex">Redis Documentation: LINDEX</a>
431+
*/
432+
Flux<NumericResponse<LPosCommand, Long>> lPos(Publisher<LPosCommand> commands);
433+
315434
/**
316435
* {@code LINDEX} command parameters.
317436
*

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.List;
1919

2020
import org.springframework.lang.Nullable;
21+
import org.springframework.util.CollectionUtils;
2122

2223
/**
2324
* List-specific commands supported by Redis.
@@ -46,6 +47,36 @@ enum Position {
4647
@Nullable
4748
Long rPush(byte[] key, byte[]... values);
4849

50+
/**
51+
* Returns the index of matching elements inside the list stored at given {@literal key}. <br />
52+
* Requires Redis 6.0.6.
53+
*
54+
* @param key must not be {@literal null}.
55+
* @param element must not be {@literal null}.
56+
* @return {@literal null} when used in pipeline / transaction.
57+
* @see <a href="https://redis.io/commands/lpos">Redis Documentation: LPOS</a>
58+
* @since 2.4
59+
*/
60+
@Nullable
61+
default Long lPos(byte[] key, byte[] element) {
62+
return CollectionUtils.firstElement(lPos(key, element, null, null));
63+
}
64+
65+
/**
66+
* Returns the index of matching elements inside the list stored at given {@literal key}. <br />
67+
* Requires Redis 6.0.6.
68+
*
69+
* @param key must not be {@literal null}.
70+
* @param element must not be {@literal null}.
71+
* @param rank specifies the "rank" of the first element to return, in case there are multiple matches. A rank of 1
72+
* means to return the first match, 2 to return the second match, and so forth.
73+
* @param count number of matches to return.
74+
* @return {@literal null} when used in pipeline / transaction.
75+
* @see <a href="https://redis.io/commands/lpos">Redis Documentation: LPOS</a>
76+
* @since 2.4
77+
*/
78+
List<Long> lPos(byte[] key, byte[] element, @Nullable Integer rank, @Nullable Integer count);
79+
4980
/**
5081
* Prepend {@code values} to {@code key}.
5182
*

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.data.redis.core.types.RedisClientInfo;
5050
import org.springframework.data.redis.serializer.RedisSerializer;
5151
import org.springframework.lang.Nullable;
52+
import org.springframework.util.CollectionUtils;
5253

5354
/**
5455
* Convenience extension of {@link RedisConnection} that accepts and returns {@link String}s instead of byte arrays.
@@ -685,6 +686,36 @@ default Long bitPos(String key, boolean bit) {
685686
*/
686687
Long rPush(String key, String... values);
687688

689+
/**
690+
* Returns the index of matching elements inside the list stored at given {@literal key}. <br />
691+
* Requires Redis 6.0.6.
692+
*
693+
* @param key must not be {@literal null}.
694+
* @param element must not be {@literal null}.
695+
* @return {@literal null} when used in pipeline / transaction.
696+
* @see <a href="https://redis.io/commands/lpos">Redis Documentation: LPOS</a>
697+
* @since 2.4
698+
*/
699+
@Nullable
700+
default Long lPos(String key, String element) {
701+
return CollectionUtils.firstElement(lPos(key, element, null, null));
702+
}
703+
704+
/**
705+
* Returns the index of matching elements inside the list stored at given {@literal key}. <br />
706+
* Requires Redis 6.0.6.
707+
*
708+
* @param key must not be {@literal null}.
709+
* @param element must not be {@literal null}.
710+
* @param rank specifies the "rank" of the first element to return, in case there are multiple matches. A rank of 1
711+
* means to return the first match, 2 to return the second match, and so forth.
712+
* @param count number of matches to return.
713+
* @return {@literal null} when used in pipeline / transaction.
714+
* @see <a href="https://redis.io/commands/lpos">Redis Documentation: LPOS</a>
715+
* @since 2.4
716+
*/
717+
List<Long> lPos(String key, String element, @Nullable Integer rank, @Nullable Integer count);
718+
688719
/**
689720
* Prepend {@code values} to {@code key}.
690721
*

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
import java.util.List;
2121

2222
import org.springframework.dao.DataAccessException;
23+
import org.springframework.dao.InvalidDataAccessApiUsageException;
2324
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
2425
import org.springframework.data.redis.connection.RedisListCommands;
2526
import org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisMultiKeyClusterCommandCallback;
27+
import org.springframework.lang.Nullable;
2628
import org.springframework.util.Assert;
2729
import org.springframework.util.CollectionUtils;
2830

@@ -55,6 +57,19 @@ public Long rPush(byte[] key, byte[]... values) {
5557
}
5658
}
5759

60+
/*
61+
* (non-Javadoc)
62+
* @see org.springframework.data.redis.connection.RedisListCommands#l{lPos(byte[], byte[], java.lang.Integer, java.lang.Integer)
63+
*/
64+
@Override
65+
public List<Long> lPos(byte[] key, byte[] element, @Nullable Integer rank, @Nullable Integer count) {
66+
67+
Assert.notNull(key, "Key must not be null!");
68+
Assert.notNull(element, "Element must not be null!");
69+
70+
throw new InvalidDataAccessApiUsageException("LPOS is not supported by jedis.");
71+
}
72+
5873
/*
5974
* (non-Javadoc)
6075
* @see org.springframework.data.redis.connection.RedisListCommands#lPush(byte[], byte[][])

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import java.util.ArrayList;
2121
import java.util.List;
2222

23+
import org.springframework.dao.InvalidDataAccessApiUsageException;
2324
import org.springframework.data.redis.connection.RedisListCommands;
25+
import org.springframework.lang.Nullable;
2426
import org.springframework.util.Assert;
2527

2628
/**
@@ -60,6 +62,19 @@ public Long rPush(byte[] key, byte[]... values) {
6062
}
6163
}
6264

65+
/*
66+
* (non-Javadoc)
67+
* @see org.springframework.data.redis.connection.RedisListCommands#l{lPos(byte[], byte[], java.lang.Integer, java.lang.Integer)
68+
*/
69+
@Override
70+
public List<Long> lPos(byte[] key, byte[] element, @Nullable Integer rank, @Nullable Integer count) {
71+
72+
Assert.notNull(key, "Key must not be null!");
73+
Assert.notNull(element, "Element must not be null!");
74+
75+
throw new InvalidDataAccessApiUsageException("LPOS is not supported by jedis.");
76+
}
77+
6378
/*
6479
* (non-Javadoc)
6580
* @see org.springframework.data.redis.connection.RedisListCommands#lPush(byte[], byte[][])

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,7 @@ static class TypeHints {
11361136
COMMAND_OUTPUT_TYPE_MAPPING.put(LINSERT, IntegerOutput.class);
11371137
COMMAND_OUTPUT_TYPE_MAPPING.put(LLEN, IntegerOutput.class);
11381138
COMMAND_OUTPUT_TYPE_MAPPING.put(LPUSH, IntegerOutput.class);
1139+
COMMAND_OUTPUT_TYPE_MAPPING.put(LPOS, IntegerOutput.class);
11391140
COMMAND_OUTPUT_TYPE_MAPPING.put(LPUSHX, IntegerOutput.class);
11401141
COMMAND_OUTPUT_TYPE_MAPPING.put(LREM, IntegerOutput.class);
11411142
COMMAND_OUTPUT_TYPE_MAPPING.put(PTTL, IntegerOutput.class);

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
*/
1616
package org.springframework.data.redis.connection.lettuce;
1717

18+
import io.lettuce.core.LPosArgs;
1819
import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands;
1920
import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
2021

22+
import java.util.Collections;
2123
import java.util.List;
2224

2325
import org.springframework.dao.DataAccessException;
2426
import org.springframework.data.redis.connection.RedisListCommands;
27+
import org.springframework.lang.Nullable;
2528
import org.springframework.util.Assert;
2629

2730
/**
@@ -61,6 +64,47 @@ public Long rPush(byte[] key, byte[]... values) {
6164
}
6265
}
6366

67+
/*
68+
* (non-Javadoc)
69+
* @see org.springframework.data.redis.connection.RedisListCommands#l{lPos(byte[], byte[], java.lang.Integer, java.lang.Integer)
70+
*/
71+
@Override
72+
public List<Long> lPos(byte[] key, byte[] element, @Nullable Integer rank, @Nullable Integer count) {
73+
74+
Assert.notNull(key, "Key must not be null!");
75+
Assert.notNull(element, "Element must not be null!");
76+
77+
LPosArgs args = new LPosArgs();
78+
if (rank != null) {
79+
args.rank(rank);
80+
}
81+
try {
82+
if (isPipelined()) {
83+
if (count != null) {
84+
pipeline(connection.newLettuceResult(getAsyncConnection().lpos(key, element, count, args)));
85+
} else {
86+
pipeline(connection.newLettuceResult(getAsyncConnection().lpos(key, element, args)));
87+
}
88+
return null;
89+
}
90+
if (isQueueing()) {
91+
if (count != null) {
92+
transaction(connection.newLettuceResult(getAsyncConnection().lpos(key, element, count, args)));
93+
} else {
94+
transaction(connection.newLettuceResult(getAsyncConnection().lpos(key, element, args)));
95+
}
96+
return null;
97+
}
98+
if (count != null) {
99+
return getConnection().lpos(key, element, count, args);
100+
}
101+
102+
return Collections.singletonList(getConnection().lpos(key, element, args));
103+
} catch (Exception ex) {
104+
throw convertLettuceAccessException(ex);
105+
}
106+
}
107+
64108
/*
65109
* (non-Javadoc)
66110
* @see org.springframework.data.redis.connection.RedisListCommands#lPush(byte[], byte[][])

0 commit comments

Comments
 (0)