Skip to content

Commit b40f1f2

Browse files
DATAREDIS-425 - Fix pagination and or connected query parts.
1 parent 59631a5 commit b40f1f2

File tree

6 files changed

+313
-30
lines changed

6 files changed

+313
-30
lines changed

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

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
import java.io.Serializable;
1919
import java.util.ArrayList;
2020
import java.util.Collection;
21+
import java.util.Collections;
2122
import java.util.Comparator;
2223
import java.util.LinkedHashMap;
2324
import java.util.List;
2425
import java.util.Map;
25-
import java.util.Set;
2626

2727
import org.springframework.dao.DataAccessException;
2828
import org.springframework.data.keyvalue.core.CriteriaAccessor;
@@ -32,6 +32,7 @@
3232
import org.springframework.data.redis.connection.RedisConnection;
3333
import org.springframework.data.redis.core.convert.RedisData;
3434
import org.springframework.data.redis.repository.query.RedisOperationChain;
35+
import org.springframework.data.redis.repository.query.RedisOperationChain.PathAndValue;
3536
import org.springframework.data.redis.util.ByteUtils;
3637

3738
/**
@@ -65,8 +66,8 @@ public RedisQueryEngine(CriteriaAccessor<RedisOperationChain> criteriaAccessor,
6566
* (non-Javadoc)
6667
* @see org.springframework.data.keyvalue.core.QueryEngine#execute(java.lang.Object, java.lang.Object, int, int, java.io.Serializable, java.lang.Class)
6768
*/
68-
public <T> Collection<T> execute(final RedisOperationChain criteria, final Comparator<?> sort, int offset, int rows,
69-
final Serializable keyspace, Class<T> type) {
69+
public <T> Collection<T> execute(final RedisOperationChain criteria, final Comparator<?> sort, final int offset,
70+
final int rows, final Serializable keyspace, Class<T> type) {
7071

7172
RedisCallback<Map<byte[], Map<byte[], byte[]>>> callback = new RedisCallback<Map<byte[], Map<byte[], byte[]>>>() {
7273

@@ -81,12 +82,25 @@ public Map<byte[], Map<byte[], byte[]>> doInRedis(RedisConnection connection) th
8182
i++;
8283
}
8384

84-
Set<byte[]> allKeys = connection.sInter(keys);
85+
List<byte[]> allKeys = new ArrayList<byte[]>();
86+
if (!criteria.getSismember().isEmpty()) {
87+
allKeys.addAll(connection.sInter(keys(keyspace + ":", criteria.getSismember())));
88+
}
89+
if (!criteria.getOrSismember().isEmpty()) {
90+
allKeys.addAll(connection.sUnion(keys(keyspace + ":", criteria.getOrSismember())));
91+
}
8592

8693
byte[] keyspaceBin = getAdapter().getConverter().getConversionService().convert(keyspace + ":", byte[].class);
8794

8895
final Map<byte[], Map<byte[], byte[]>> rawData = new LinkedHashMap<byte[], Map<byte[], byte[]>>();
8996

97+
if (allKeys.size() == 0 || allKeys.size() < offset) {
98+
return Collections.emptyMap();
99+
}
100+
101+
if (offset >= 0 && rows > 0) {
102+
allKeys = allKeys.subList(Math.max(0, offset), Math.min(offset + rows, allKeys.size()));
103+
}
90104
for (byte[] id : allKeys) {
91105

92106
byte[] singleKey = ByteUtils.concat(keyspaceBin, id);
@@ -150,6 +164,23 @@ public Long doInRedis(RedisConnection connection) throws DataAccessException {
150164
});
151165
}
152166

167+
private byte[][] keys(String prefix, Collection<PathAndValue> source) {
168+
169+
byte[][] keys = new byte[source.size()][];
170+
int i = 0;
171+
for (PathAndValue pathAndValue : source) {
172+
173+
byte[] convertedValue = getAdapter().getConverter().getConversionService()
174+
.convert(pathAndValue.getFirstValue(), byte[].class);
175+
byte[] fullPath = getAdapter().getConverter().getConversionService()
176+
.convert(prefix + pathAndValue.getPath() + ":", byte[].class);
177+
178+
keys[i] = ByteUtils.concat(fullPath, convertedValue);
179+
i++;
180+
}
181+
return keys;
182+
}
183+
153184
/**
154185
* @author Christoph Strobl
155186
* @since 1.7

src/main/java/org/springframework/data/redis/repository/query/RedisOperationChain.java

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
package org.springframework.data.redis.repository.query;
1717

1818
import java.util.Collection;
19+
import java.util.Collections;
1920
import java.util.LinkedHashSet;
2021
import java.util.Set;
2122

23+
import org.springframework.util.ObjectUtils;
24+
2225
/**
2326
* Simple set of operations requried to run queries against Redis.
2427
*
@@ -27,27 +30,102 @@
2730
*/
2831
public class RedisOperationChain {
2932

30-
Set<Object> sismember = new LinkedHashSet<Object>();
31-
Set<Object> orSismember = new LinkedHashSet<Object>();
33+
private Set<PathAndValue> sismember = new LinkedHashSet<PathAndValue>();
34+
private Set<PathAndValue> orSismember = new LinkedHashSet<PathAndValue>();
35+
36+
public void sismember(String path, Object value) {
37+
sismember(new PathAndValue(path, value));
38+
}
3239

33-
public void sismember(Object next) {
34-
sismember.add(next);
40+
public void sismember(PathAndValue pathAndValue) {
41+
sismember.add(pathAndValue);
3542
}
3643

37-
public Set<Object> getSismember() {
44+
public Set<PathAndValue> getSismember() {
3845
return sismember;
3946
}
4047

41-
public void orSismember(Object next) {
42-
orSismember.add(next);
48+
public void orSismember(String path, Object value) {
49+
orSismember(new PathAndValue(path, value));
4350
}
4451

45-
public void orSismember(Collection<Object> next) {
52+
public void orSismember(PathAndValue pathAndValue) {
53+
orSismember.add(pathAndValue);
54+
}
55+
56+
public void orSismember(Collection<PathAndValue> next) {
4657
orSismember.addAll(next);
4758
}
4859

49-
public Set<Object> getOrSismember() {
60+
public Set<PathAndValue> getOrSismember() {
5061
return orSismember;
5162
}
5263

64+
public static class PathAndValue {
65+
66+
private final String path;
67+
private final Collection<Object> values;
68+
69+
public PathAndValue(String path, Object singleValue) {
70+
71+
this.path = path;
72+
this.values = Collections.singleton(singleValue);
73+
}
74+
75+
public PathAndValue(String path, Collection<Object> values) {
76+
77+
this.path = path;
78+
this.values = values != null ? values : Collections.emptySet();
79+
}
80+
81+
public boolean isSingleValue() {
82+
return values.size() == 1;
83+
}
84+
85+
public String getPath() {
86+
return path;
87+
}
88+
89+
public Collection<Object> values() {
90+
return values;
91+
}
92+
93+
public Object getFirstValue() {
94+
return values.isEmpty() ? null : values.iterator().next();
95+
}
96+
97+
@Override
98+
public String toString() {
99+
return path + ":" + (isSingleValue() ? getFirstValue() : values);
100+
}
101+
102+
@Override
103+
public int hashCode() {
104+
105+
int result = ObjectUtils.nullSafeHashCode(path);
106+
result += ObjectUtils.nullSafeHashCode(values);
107+
return result;
108+
}
109+
110+
@Override
111+
public boolean equals(Object obj) {
112+
if (this == obj) {
113+
return true;
114+
}
115+
if (obj == null) {
116+
return false;
117+
}
118+
if (!(obj instanceof PathAndValue)) {
119+
return false;
120+
}
121+
PathAndValue that = (PathAndValue) obj;
122+
if (!ObjectUtils.nullSafeEquals(this.path, that.path)) {
123+
return false;
124+
}
125+
126+
return ObjectUtils.nullSafeEquals(this.values, that.values);
127+
}
128+
129+
}
130+
53131
}

src/main/java/org/springframework/data/redis/repository/query/RedisQueryCreator.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private RedisOperationChain from(Part part, Iterator<Object> iterator, RedisOper
4949

5050
switch (part.getType()) {
5151
case SIMPLE_PROPERTY:
52-
sink.sismember(part.getProperty().toDotPath() + ":" + iterator.next());
52+
sink.sismember(part.getProperty().toDotPath(), iterator.next());
5353
break;
5454
default:
5555
throw new IllegalArgumentException(part.getType() + "is not supported for redis query derivation");
@@ -74,7 +74,7 @@ protected RedisOperationChain and(Part part, RedisOperationChain base, Iterator<
7474
*/
7575
@Override
7676
protected RedisOperationChain or(RedisOperationChain base, RedisOperationChain criteria) {
77-
base.orSismember(criteria.sismember);
77+
base.orSismember(criteria.getSismember());
7878
return base;
7979
}
8080

@@ -87,6 +87,12 @@ protected KeyValueQuery<RedisOperationChain> complete(final RedisOperationChain
8787

8888
KeyValueQuery<RedisOperationChain> query = new KeyValueQuery<RedisOperationChain>(criteria);
8989

90+
if (query.getCritieria().getSismember().size() == 1 && query.getCritieria().getOrSismember().size() == 1) {
91+
92+
query.getCritieria().getOrSismember().add(query.getCritieria().getSismember().iterator().next());
93+
query.getCritieria().getSismember().clear();
94+
}
95+
9096
if (sort != null) {
9197
query.setSort(sort);
9298
}

src/test/java/org/springframework/data/redis/core/convert/ConversionTestEntities.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
/**
3030
* @author Christoph Strobl
3131
*/
32-
class ConversionTestEntities {
32+
public class ConversionTestEntities {
3333

3434
static final String KEYSPACE_PERSON = "persons";
3535
static final String KEYSPACE_TWOT = "twot";
3636
static final String KEYSPACE_LOCATION = "locations";
3737

3838
@RedisHash(KEYSPACE_PERSON)
39-
static class Person {
39+
public static class Person {
4040

4141
@Id String id;
4242
String firstname;
@@ -59,80 +59,80 @@ static class Person {
5959
Species species;
6060
}
6161

62-
static class PersonWithAddressReference extends Person {
62+
public static class PersonWithAddressReference extends Person {
6363

6464
@Reference AddressWithId addressRef;
6565
}
6666

67-
static class Address {
67+
public static class Address {
6868

6969
String city;
7070
@Indexed String country;
7171
}
7272

73-
static class AddressWithId extends Address {
73+
public static class AddressWithId extends Address {
7474

7575
@Id String id;
7676
}
7777

78-
static enum Gender {
78+
public static enum Gender {
7979
MALE, FEMALE
8080
}
8181

82-
static class AddressWithPostcode extends Address {
82+
public static class AddressWithPostcode extends Address {
8383

8484
String postcode;
8585
}
8686

87-
static class TaVeren extends Person {
87+
public static class TaVeren extends Person {
8888

8989
Object feature;
9090
Map<String, Object> characteristics;
9191
List<Object> items;
9292
}
9393

9494
@RedisHash(KEYSPACE_LOCATION)
95-
static class Location {
95+
public static class Location {
9696

9797
@Id String id;
9898
String name;
9999
Address address;
100100
}
101101

102102
@RedisHash(timeToLive = 5)
103-
static class ExpiringPerson {
103+
public static class ExpiringPerson {
104104

105105
@Id String id;
106106
String name;
107107
}
108108

109-
static class ExipringPersonWithExplicitProperty extends ExpiringPerson {
109+
public static class ExipringPersonWithExplicitProperty extends ExpiringPerson {
110110

111111
@TimeToLive(unit = TimeUnit.MINUTES) Long ttl;
112112
}
113113

114-
static class Species {
114+
public static class Species {
115115

116116
String name;
117117
List<String> alsoKnownAs;
118118
}
119119

120120
@RedisHash(KEYSPACE_TWOT)
121-
static class TheWheelOfTime {
121+
public static class TheWheelOfTime {
122122

123123
List<Person> mainCharacters;
124124
List<Species> species;
125125
Map<String, Location> places;
126126
}
127127

128-
static class Item {
128+
public static class Item {
129129

130130
@Indexed String type;
131131
String description;
132132
Size size;
133133
}
134134

135-
static class Size {
135+
public static class Size {
136136

137137
int width;
138138
int height;

0 commit comments

Comments
 (0)