Skip to content

Commit 803fd71

Browse files
junghoon-vansmp911de
authored andcommitted
Refactor Redis secondary-index key formatting.
Redis secondary-index keys are now formatted using helper methods instead of using chains of concatAll(toBytes(…)) sequences for more readability. Original pull request: #2795 Closes: #2794
1 parent 0f45851 commit 803fd71

File tree

3 files changed

+67
-21
lines changed

3 files changed

+67
-21
lines changed

src/main/java/org/springframework/data/redis/core/IndexWriter.java

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,26 @@
3333

3434
/**
3535
* {@link IndexWriter} takes care of writing <a href="https://redis.io/topics/indexes">secondary index</a> structures to
36-
* Redis. Depending on the type of {@link IndexedData} it uses eg. Sets with specific names to add actually referenced
37-
* keys to. While doing so {@link IndexWriter} also keeps track of all indexes associated with the root types key, which
36+
* Redis. Depending on the type of {@link IndexedData}, it uses Sets with specific names to add actually referenced keys
37+
* to. While doing so {@link IndexWriter} also keeps track of all indexes associated with the root types key, which
3838
* allows to remove the root key from all indexes in case of deletion.
3939
*
4040
* @author Christoph Strobl
4141
* @author Rob Winch
42+
* @author Mark Paluch
4243
* @since 1.7
4344
*/
4445
class IndexWriter {
4546

47+
private static final byte[] SEPARATOR = ":".getBytes();
48+
private static final byte[] IDX = "idx".getBytes();
49+
4650
private final RedisConnection connection;
4751
private final RedisConverter converter;
4852

4953
/**
5054
* Creates new {@link IndexWriter}.
5155
*
52-
* @param keyspace The key space to write index values to. Must not be {@literal null}.
5356
* @param connection must not be {@literal null}.
5457
* @param converter must not be {@literal null}.
5558
*/
@@ -127,7 +130,7 @@ public void removeKeyFromIndexes(String keyspace, Object key) {
127130
Assert.notNull(key, "Key must not be null");
128131

129132
byte[] binKey = toBytes(key);
130-
byte[] indexHelperKey = ByteUtils.concatAll(toBytes(keyspace + ":"), binKey, toBytes(":idx"));
133+
byte[] indexHelperKey = createIndexKey(keyspace, binKey);
131134

132135
for (byte[] indexKey : connection.sMembers(indexHelperKey)) {
133136

@@ -147,10 +150,10 @@ public void removeKeyFromIndexes(String keyspace, Object key) {
147150
*/
148151
public void removeAllIndexes(String keyspace) {
149152

150-
Set<byte[]> potentialIndex = connection.keys(toBytes(keyspace + ":*"));
153+
Set<byte[]> potentialIndex = connection.keys(createIndexKey(keyspace, "*"));
151154

152155
if (!potentialIndex.isEmpty()) {
153-
connection.del(potentialIndex.toArray(new byte[potentialIndex.size()][]));
156+
connection.del(potentialIndex.toArray(new byte[0][]));
154157
}
155158
}
156159

@@ -162,7 +165,7 @@ private void removeKeyFromExistingIndexes(byte[] key, Iterable<IndexedData> inde
162165
}
163166

164167
/**
165-
* Remove given key from all indexes matching {@link IndexedData#getIndexName()}:
168+
* Remove given key from all indexes matching {@link IndexedData#getIndexName()}.
166169
*
167170
* @param key
168171
* @param indexedData
@@ -171,8 +174,7 @@ protected void removeKeyFromExistingIndexes(byte[] key, IndexedData indexedData)
171174

172175
Assert.notNull(indexedData, "IndexedData must not be null");
173176

174-
Set<byte[]> existingKeys = connection
175-
.keys(toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName() + ":*"));
177+
Set<byte[]> existingKeys = connection.keys(createIndexKey(indexedData.getKeyPrefix(), "*"));
176178

177179
if (!CollectionUtils.isEmpty(existingKeys)) {
178180
for (byte[] existingKey : existingKeys) {
@@ -216,30 +218,66 @@ protected void addKeyToIndex(byte[] key, IndexedData indexedData) {
216218
return;
217219
}
218220

219-
byte[] indexKey = toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName() + ":");
221+
byte[] indexKey = toBytes(indexedData.getKeyPrefix(), SEPARATOR);
220222
indexKey = ByteUtils.concat(indexKey, toBytes(value));
221223
connection.sAdd(indexKey, key);
222224

223225
// keep track of indexes used for the object
224-
connection.sAdd(ByteUtils.concatAll(toBytes(indexedData.getKeyspace() + ":"), key, toBytes(":idx")), indexKey);
226+
connection.sAdd(createIndexKey(indexedData.getKeyspace(), key), indexKey);
225227
} else if (indexedData instanceof GeoIndexedPropertyValue propertyValue) {
226228

227229
Object value = propertyValue.getValue();
228230
if (value == null) {
229231
return;
230232
}
231233

232-
byte[] indexKey = toBytes(indexedData.getKeyspace() + ":" + indexedData.getIndexName());
234+
byte[] indexKey = toBytes(indexedData.getKeyPrefix());
233235
connection.geoAdd(indexKey, propertyValue.getPoint(), key);
234236

235237
// keep track of indexes used for the object
236-
connection.sAdd(ByteUtils.concatAll(toBytes(indexedData.getKeyspace() + ":"), key, toBytes(":idx")), indexKey);
238+
connection.sAdd(createIndexKey(indexedData.getKeyspace(), key), indexKey);
237239
} else {
238240
throw new IllegalArgumentException(
239241
String.format("Cannot write index data for unknown index type %s", indexedData.getClass()));
240242
}
241243
}
242244

245+
private byte[] createIndexKey(String keyspace, byte[] key) {
246+
return createIndexKey(keyspace, key, IDX);
247+
}
248+
249+
private byte[] createIndexKey(Object... items) {
250+
251+
Object[] elements = new Object[items.length + (items.length - 1)];
252+
253+
int j = 0;
254+
for (int i = 0; i < items.length; i++) {
255+
256+
elements[j++] = items[i];
257+
if (items.length - 1 > i) {
258+
elements[j++] = SEPARATOR;
259+
}
260+
}
261+
262+
return toBytes(elements);
263+
}
264+
265+
private byte[] toBytes(Object... values) {
266+
267+
byte[][] arrays = new byte[values.length][];
268+
269+
for (int i = 0; i < values.length; i++) {
270+
271+
if (values[i] instanceof byte[] bb) {
272+
arrays[i] = bb;
273+
} else {
274+
arrays[i] = toBytes(values[i]);
275+
}
276+
}
277+
278+
return ByteUtils.concatAll(arrays);
279+
}
280+
243281
private byte[] toBytes(@Nullable Object source) {
244282

245283
if (source == null) {

src/main/java/org/springframework/data/redis/core/RedisQueryEngine.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Map;
2626
import java.util.Set;
2727

28+
import org.springframework.core.convert.ConversionService;
2829
import org.springframework.data.geo.Circle;
2930
import org.springframework.data.geo.GeoResult;
3031
import org.springframework.data.geo.GeoResults;
@@ -81,8 +82,8 @@ private RedisQueryEngine(CriteriaAccessor<RedisOperationChain> criteriaAccessor,
8182

8283
@Override
8384
@SuppressWarnings("unchecked")
84-
public <T> List<T> execute(RedisOperationChain criteria, Comparator<?> sort, long offset, int rows,
85-
String keyspace, Class<T> type) {
85+
public <T> List<T> execute(RedisOperationChain criteria, Comparator<?> sort, long offset, int rows, String keyspace,
86+
Class<T> type) {
8687
List<T> result = doFind(criteria, offset, rows, keyspace, type);
8788

8889
if (sort != null) {
@@ -199,8 +200,7 @@ private List<byte[]> findKeys(RedisOperationChain criteria, int rows, String key
199200
}
200201

201202
@Override
202-
public List<?> execute(RedisOperationChain criteria, Comparator<?> sort, long offset, int rows,
203-
String keyspace) {
203+
public List<?> execute(RedisOperationChain criteria, Comparator<?> sort, long offset, int rows, String keyspace) {
204204
return execute(criteria, sort, offset, rows, keyspace, Object.class);
205205
}
206206

@@ -229,14 +229,13 @@ public long count(RedisOperationChain criteria, String keyspace) {
229229

230230
private byte[][] keys(String prefix, Collection<PathAndValue> source) {
231231

232+
ConversionService conversionService = getRequiredAdapter().getConverter().getConversionService();
232233
byte[][] keys = new byte[source.size()][];
233234
int i = 0;
234235
for (PathAndValue pathAndValue : source) {
235236

236-
byte[] convertedValue = getRequiredAdapter().getConverter().getConversionService()
237-
.convert(pathAndValue.getFirstValue(), byte[].class);
238-
byte[] fullPath = getRequiredAdapter().getConverter().getConversionService()
239-
.convert(prefix + pathAndValue.getPath() + ":", byte[].class);
237+
byte[] convertedValue = conversionService.convert(pathAndValue.getFirstValue(), byte[].class);
238+
byte[] fullPath = conversionService.convert(prefix + pathAndValue.getPath() + ":", byte[].class);
240239

241240
keys[i] = ByteUtils.concat(fullPath, convertedValue);
242241
i++;

src/main/java/org/springframework/data/redis/core/convert/IndexedData.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,13 @@ public interface IndexedData {
3838
*/
3939
String getKeyspace();
4040

41+
/**
42+
* Return the key prefix for usage in Redis.
43+
*
44+
* @return concatenated form of the keyspace and the index name.
45+
* @since 3.3.4
46+
*/
47+
default String getKeyPrefix() {
48+
return getKeyspace() + ":" + getIndexName();
49+
}
4150
}

0 commit comments

Comments
 (0)