Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9faa39f

Browse files
mp911dechristophstrobl
authored andcommitted
Use by-id lookup for queries referring to identifier values.
Closes: #2851 Original Pull Request: #2854
1 parent d4ac0a5 commit 9faa39f

File tree

2 files changed

+117
-6
lines changed

2 files changed

+117
-6
lines changed

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

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,20 @@
3333
import org.springframework.data.keyvalue.core.SortAccessor;
3434
import org.springframework.data.keyvalue.core.SpelSortAccessor;
3535
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
36+
import org.springframework.data.mapping.PersistentPropertyPath;
3637
import org.springframework.data.redis.connection.RedisConnection;
3738
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
3839
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
3940
import org.springframework.data.redis.connection.util.ByteArrayWrapper;
4041
import org.springframework.data.redis.core.convert.GeoIndexedPropertyValue;
42+
import org.springframework.data.redis.core.convert.RedisConverter;
4143
import org.springframework.data.redis.core.convert.RedisData;
44+
import org.springframework.data.redis.core.mapping.RedisPersistentProperty;
4245
import org.springframework.data.redis.repository.query.RedisOperationChain;
4346
import org.springframework.data.redis.repository.query.RedisOperationChain.NearPath;
4447
import org.springframework.data.redis.repository.query.RedisOperationChain.PathAndValue;
4548
import org.springframework.data.redis.util.ByteUtils;
4649
import org.springframework.expression.spel.standard.SpelExpressionParser;
47-
import org.springframework.lang.NonNullApi;
4850
import org.springframework.lang.Nullable;
4951
import org.springframework.util.CollectionUtils;
5052

@@ -100,7 +102,7 @@ private <T> List<T> doFind(RedisOperationChain criteria, long offset, int rows,
100102

101103
RedisCallback<Map<byte[], Map<byte[], byte[]>>> callback = connection -> {
102104

103-
List<byte[]> keys = findKeys(criteria, rows, keyspace, connection);
105+
List<byte[]> keys = findKeys(criteria, rows, keyspace, type, connection);
104106
byte[] keyspaceBin = getRequiredAdapter().getConverter().getConversionService().convert(keyspace + ":",
105107
byte[].class);
106108

@@ -143,18 +145,35 @@ private <T> List<T> doFind(RedisOperationChain criteria, long offset, int rows,
143145
return result;
144146
}
145147

146-
private List<byte[]> findKeys(RedisOperationChain criteria, int rows, String keyspace, RedisConnection connection) {
148+
private List<byte[]> findKeys(RedisOperationChain criteria, int rows, String keyspace, Class<?> domainType,
149+
RedisConnection connection) {
147150

148151
List<byte[]> allKeys = new ArrayList<>();
149152

150153
if (!criteria.getSismember().isEmpty()) {
151-
allKeys.addAll(connection.sInter(keys(keyspace + ":", criteria.getSismember())));
154+
155+
Set<PathAndValue> sismember = criteria.getSismember();
156+
if (sismember.size() == 1) {
157+
KeySelector keySelector = KeySelector.of(getRequiredAdapter().getConverter(), sismember, domainType);
158+
if (!keySelector.setValueLookup().isEmpty()) {
159+
allKeys.addAll(connection.sInter(keys(keyspace + ":", keySelector.setValueLookup())));
160+
}
161+
162+
allKeys.addAll(keySelector.keys());
163+
} else {
164+
allKeys.addAll(connection.sInter(keys(keyspace + ":", sismember)));
165+
}
152166
}
153167

154-
if (!criteria.getOrSismember().isEmpty()) {
168+
KeySelector keySelector = KeySelector.of(getRequiredAdapter().getConverter(), criteria.getOrSismember(),
169+
domainType);
170+
171+
if (!keySelector.setValueLookup().isEmpty()) {
155172
allKeys.addAll(connection.sUnion(keys(keyspace + ":", criteria.getOrSismember())));
156173
}
157174

175+
allKeys.addAll(keySelector.keys());
176+
158177
if (criteria.getNear() != null) {
159178

160179
GeoRadiusCommandArgs limit = GeoRadiusCommandArgs.newGeoRadiusArgs();
@@ -170,7 +189,6 @@ private List<byte[]> findKeys(RedisOperationChain criteria, int rows, String key
170189
}
171190
}
172191

173-
174192
Set<ByteArrayWrapper> unique = new LinkedHashSet<>(allKeys.size());
175193
allKeys.forEach(key -> unique.add(new ByteArrayWrapper(key)));
176194

@@ -244,4 +262,35 @@ public RedisOperationChain resolve(KeyValueQuery<?> query) {
244262
return (RedisOperationChain) query.getCriteria();
245263
}
246264
}
265+
266+
/**
267+
* Value object capturing the direct object keys and set of values that need to be looked up from the secondary
268+
* indexes.
269+
*
270+
* @param keys
271+
* @param setValueLookup
272+
* @since 3.2.4
273+
*/
274+
record KeySelector(Collection<byte[]> keys, Set<PathAndValue> setValueLookup) {
275+
276+
static KeySelector of(RedisConverter converter, Set<PathAndValue> pathAndValues, Class<?> domainType) {
277+
278+
Set<byte[]> keys = new LinkedHashSet<>();
279+
Set<PathAndValue> remainder = new LinkedHashSet<>();
280+
281+
for (PathAndValue pathAndValue : pathAndValues) {
282+
283+
PersistentPropertyPath<RedisPersistentProperty> path = converter.getMappingContext()
284+
.getPersistentPropertyPath(pathAndValue.getPath(), domainType);
285+
if (path.getLeafProperty().isIdProperty()) {
286+
byte[] key = converter.getConversionService().convert(pathAndValue.getFirstValue(), byte[].class);
287+
keys.add(key);
288+
} else {
289+
remainder.add(pathAndValue);
290+
}
291+
}
292+
293+
return new KeySelector(keys, remainder);
294+
}
295+
}
247296
}

‎src/test/java/org/springframework/data/redis/repository/RedisRepositoryIntegrationTestBase.java‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.junit.jupiter.api.BeforeEach;
2929
import org.junit.jupiter.api.Test;
30+
3031
import org.springframework.beans.factory.annotation.Autowired;
3132
import org.springframework.data.annotation.Id;
3233
import org.springframework.data.annotation.Reference;
@@ -101,6 +102,57 @@ void simpleFindShouldReturnEntitiesCorrectly() {
101102
assertThat(repo.findByLastname("al'thor")).contains(rand);
102103
}
103104

105+
@Test // GH-2851
106+
void shouldReturnSingleEntityByIdViaQueryMethod() {
107+
108+
Person rand = new Person();
109+
rand.firstname = "rand";
110+
rand.lastname = "al'thor";
111+
112+
Person egwene = new Person();
113+
egwene.firstname = "egwene";
114+
115+
repo.saveAll(Arrays.asList(rand, egwene));
116+
117+
assertThat(repo.findEntityById(rand.getId())).isEqualTo(rand);
118+
assertThat(repo.findEntityById(egwene.getId())).isEqualTo(egwene);
119+
}
120+
121+
@Test // GH-2851
122+
void shouldProjectSingleResult() {
123+
124+
Person rand = new Person();
125+
rand.firstname = "rand";
126+
rand.lastname = "al'thor";
127+
128+
Person egwene = new Person();
129+
egwene.firstname = "egwene";
130+
131+
repo.saveAll(Arrays.asList(rand, egwene));
132+
133+
PersonProjection projectionById = repo.findProjectionById(rand.getId());
134+
assertThat(projectionById).isNotNull();
135+
assertThat(projectionById.getFirstname()).isEqualTo(rand.firstname);
136+
}
137+
138+
@Test // GH-2851
139+
void shouldProjectCollection() {
140+
141+
Person rand = new Person();
142+
rand.firstname = "rand";
143+
rand.lastname = "al'thor";
144+
145+
Person egwene = new Person();
146+
egwene.firstname = "egwene";
147+
148+
repo.saveAll(Arrays.asList(rand, egwene));
149+
150+
List<PersonProjection> projectionById = repo.findProjectionBy();
151+
assertThat(projectionById).hasSize(2) //
152+
.extracting(PersonProjection::getFirstname) //
153+
.contains(rand.getFirstname(), egwene.getFirstname());
154+
}
155+
104156
@Test // DATAREDIS-425
105157
void simpleFindByMultipleProperties() {
106158

@@ -570,10 +622,20 @@ public interface PersonRepository extends PagingAndSortingRepository<Person, Str
570622

571623
Slice<Person> findByHometownLocationNear(Point point, Distance distance, Pageable pageable);
572624

625+
Person findEntityById(String id);
626+
627+
PersonProjection findProjectionById(String id);
628+
629+
List<PersonProjection> findProjectionBy();
630+
573631
@Override
574632
<S extends Person> List<S> findAll(Example<S> example);
575633
}
576634

635+
public interface PersonProjection {
636+
String getFirstname();
637+
}
638+
577639
public interface CityRepository extends CrudRepository<City, String> {
578640

579641
List<City> findByLocationNear(Point point, Distance distance);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /