DATAREDIS-425 - Preserve item order in list, update documentation.
We now preserve the item order when converting list elements. This does not mean that we place elements at the exact position retrieved from the store but rather maintain their order based on the index value. Additionally applied some documentation polishing and cluster tests. Original Pull Request: #156
This commit is contained in:
@@ -383,8 +383,8 @@ public class ApplicationConfig {
|
||||
public static class MyIndexConfiguration extends IndexConfiguration {
|
||||
|
||||
@Override
|
||||
protected Iterable<RedisIndexSetting> initialConfiguration() {
|
||||
return Collections.singleton(new RedisIndexSetting("persons", "firstname"));
|
||||
protected Iterable<IndexDefinition> initialConfiguration() {
|
||||
return Collections.singleton(new SimpleIndexDefinition("persons", "firstname"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,8 +411,8 @@ public class ApplicationConfig {
|
||||
public static class MyIndexConfiguration extends IndexConfiguration {
|
||||
|
||||
@Override
|
||||
protected Iterable<RedisIndexSetting> initialConfiguration() {
|
||||
return Collections.singleton(new RedisIndexSetting("persons", "firstname"));
|
||||
protected Iterable<IndexDefinition> initialConfiguration() {
|
||||
return Collections.singleton(new SimpleIndexDefinition("persons", "firstname"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -537,7 +537,38 @@ Here's an overview of the keywords supported for Redis and what a method contain
|
||||
|===============
|
||||
====
|
||||
|
||||
[[redis.misc.cdi-integration]]
|
||||
[[redis.repositories.cluster]]
|
||||
== Redis Repositories running on Cluster
|
||||
|
||||
Using the Redis repository support in a clustered Redis environment is fine. Please see the <<cluster, Redis Cluster>> section for `ConnectionFactory` configuration details.
|
||||
Still some considerations have to be done as the default key distribution will spread entities and secondary indexes through out the whole cluster and its slots.
|
||||
|
||||
[options = "header, autowidth"]
|
||||
|===============
|
||||
|key|type|slot|node
|
||||
|persons:e2c7dcee-b8cd-4424-883e-736ce564363e|id for hash|15171|127.0.0.1:7381
|
||||
|persons:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56|id for hash|7373|127.0.0.1:7380
|
||||
|persons:firstname:rand|index|1700|127.0.0.1:7379
|
||||
|
|
||||
|===============
|
||||
====
|
||||
|
||||
Some commands like `SINTER` and `SUNION` can only be processed on the Server side when all involved keys map to the same slot. Otherwise computation has to be done on client side.
|
||||
Therefore it be useful to pin keyspaces to a single slot which allows to make use of Redis serverside computation right away.
|
||||
|
||||
[options = "header, autowidth"]
|
||||
|===============
|
||||
|key|type|slot|node
|
||||
|{persons}:e2c7dcee-b8cd-4424-883e-736ce564363e|id for hash|2399|127.0.0.1:7379
|
||||
|{persons}:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56|id for hash|2399|127.0.0.1:7379
|
||||
|{persons}:firstname:rand|index|2399|127.0.0.1:7379
|
||||
|
|
||||
|===============
|
||||
====
|
||||
|
||||
TIP: Define and pin keyspaces via `@RedisHash("{yourkeyspace}") to specific slots when using Redis cluster.
|
||||
|
||||
[[redis.repositories.cdi-integration]]
|
||||
== CDI integration
|
||||
|
||||
Instances of the repository interfaces are usually created by a container, which Spring is the most natural choice when working with Spring Data. There's sophisticated support to easily set up Spring to create bean instances. Spring Data Redis ships with a custom CDI extension that allows using the repository abstraction in CDI environments. The extension is part of the JAR so all you need to do to activate it is dropping the Spring Data Redis JAR into your classpath.
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core.convert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
@@ -53,6 +56,7 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.comparator.NullSafeComparator;
|
||||
|
||||
/**
|
||||
* {@link RedisConverter} implementation creating flat binary map structure out of a given domain type. Considers
|
||||
@@ -103,6 +107,9 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
private final GenericConversionService conversionService;
|
||||
private final EntityInstantiators entityInstantiators;
|
||||
private final TypeMapper<RedisData> typeMapper;
|
||||
private final Comparator<String> listKeyComparator = new NullSafeComparator<String>(
|
||||
NaturalOrderingKeyComparator.INSTANCE, true);
|
||||
|
||||
private ReferenceResolver referenceResolver;
|
||||
private IndexResolver indexResolver;
|
||||
private CustomConversions customConversions;
|
||||
@@ -533,11 +540,15 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
|
||||
Bucket partial = source.getBucket().extract(path + ".[");
|
||||
|
||||
List<String> keys = new ArrayList<String>(partial.keySet());
|
||||
keys.sort(listKeyComparator);
|
||||
|
||||
Collection<Object> target = CollectionFactory.createCollection(collectionType, valueType, partial.size());
|
||||
|
||||
for (byte[] value : partial.values()) {
|
||||
target.add(fromBytes(value, valueType));
|
||||
for (String key : keys) {
|
||||
target.add(fromBytes(partial.get(key), valueType));
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -551,7 +562,8 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
private Collection<?> readCollectionOfComplexTypes(String path, Class<?> collectionType, Class<?> valueType,
|
||||
Bucket source) {
|
||||
|
||||
Set<String> keys = source.extractAllKeysFor(path);
|
||||
List<String> keys = new ArrayList<String>(source.extractAllKeysFor(path));
|
||||
keys.sort(listKeyComparator);
|
||||
|
||||
Collection<Object> target = CollectionFactory.createCollection(collectionType, valueType, keys.size());
|
||||
|
||||
@@ -810,4 +822,90 @@ public class MappingRedisConverter implements RedisConverter, InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
private enum NaturalOrderingKeyComparator implements Comparator<String> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public int compare(String s1, String s2) {
|
||||
|
||||
int s1offset = 0;
|
||||
int s2offset = 0;
|
||||
|
||||
while (s1offset < s1.length() && s2offset < s2.length()) {
|
||||
|
||||
Part thisPart = extractPart(s1, s1offset);
|
||||
Part thatPart = extractPart(s2, s2offset);
|
||||
|
||||
int result = thisPart.compareTo(thatPart);
|
||||
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
s1offset += thisPart.length();
|
||||
s2offset += thatPart.length();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Part extractPart(String source, int offset) {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
char c = source.charAt(offset);
|
||||
builder.append(c);
|
||||
|
||||
boolean isDigit = Character.isDigit(c);
|
||||
for (int i = offset + 1; i < source.length(); i++) {
|
||||
|
||||
c = source.charAt(i);
|
||||
if ((isDigit && !Character.isDigit(c)) || (!isDigit && Character.isDigit(c))) {
|
||||
break;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
|
||||
return new Part(builder.toString(), isDigit);
|
||||
}
|
||||
|
||||
private static class Part implements Comparable<Part> {
|
||||
|
||||
private final String rawValue;
|
||||
private final Long longValue;
|
||||
|
||||
Part(String value, boolean isDigit) {
|
||||
|
||||
this.rawValue = value;
|
||||
this.longValue = isDigit ? Long.valueOf(value) : null;
|
||||
}
|
||||
|
||||
boolean isNumeric() {
|
||||
return longValue != null;
|
||||
}
|
||||
|
||||
int length() {
|
||||
return rawValue.length();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Comparable#compareTo(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Part that) {
|
||||
|
||||
if (this.isNumeric() && that.isNumeric()) {
|
||||
return this.longValue.compareTo(that.longValue);
|
||||
}
|
||||
|
||||
return this.rawValue.compareTo(that.rawValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user