Skip to content

Commit c5e3dba

Browse files
DATAREDIS-471 - Hacking (Maps / Lists).
Make sure simple/complex values get written to Map and List types.
1 parent d32c804 commit c5e3dba

File tree

2 files changed

+266
-13
lines changed

2 files changed

+266
-13
lines changed

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

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.Collection;
20+
import java.util.Collections;
2021
import java.util.Comparator;
2122
import java.util.HashMap;
2223
import java.util.List;
@@ -46,6 +47,7 @@
4647
import org.springframework.data.mapping.PreferredConstructor;
4748
import org.springframework.data.mapping.PropertyHandler;
4849
import org.springframework.data.mapping.context.PersistentPropertyPath;
50+
import org.springframework.data.mapping.model.MappingException;
4951
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
5052
import org.springframework.data.mapping.model.PropertyValueProvider;
5153
import org.springframework.data.redis.core.PartialUpdate;
@@ -391,22 +393,69 @@ protected void writePartialUpdate(PartialUpdate<?> update, RedisData sink) {
391393

392394
for (PropertyUpdate pUpdate : update.getPropertyUpdates()) {
393395

394-
PersistentPropertyPath<KeyValuePersistentProperty> persistentPropertyPath = mappingContext
395-
.getPersistentPropertyPath(pUpdate.getPropertyPath(), update.getTarget());
396+
String path = pUpdate.getPropertyPath();
396397

397398
if (UpdateCommand.SET.equals(pUpdate.getCmd())) {
398399

399-
Set<IndexedData> data = indexResolver.resolveIndexesFor(entity.getKeySpace(), pUpdate.getPropertyPath(),
400-
persistentPropertyPath.getLeafProperty().getTypeInformation(), pUpdate.getValue());
400+
KeyValuePersistentProperty targetProperty = getTargetPropertyOrNullForPath(path, update.getTarget());
401+
402+
if (targetProperty == null) {
403+
404+
writeInternal(entity.getKeySpace(), pUpdate.getPropertyPath(), pUpdate.getValue(),
405+
ClassTypeInformation.OBJECT, sink);
406+
continue;
407+
}
408+
if (targetProperty.isCollectionLike()) {
409+
410+
Collection<Object> c = pUpdate.getValue() instanceof Collection ? (Collection<Object>) pUpdate.getValue()
411+
: Collections.<Object> singleton(pUpdate.getValue());
412+
writeCollection(entity.getKeySpace(), pUpdate.getPropertyPath(), c, targetProperty.getTypeInformation(),
413+
sink);
414+
} else if (targetProperty.isMap()) {
415+
416+
Map<Object, Object> map = new HashMap<Object, Object>();
417+
418+
if (pUpdate.getValue() instanceof Map) {
419+
map.putAll((Map) pUpdate.getValue());
420+
} else if (pUpdate.getValue() instanceof Map.Entry) {
421+
map.put(((Map.Entry) pUpdate.getValue()).getKey(), ((Map.Entry) pUpdate.getValue()).getValue());
422+
} else {
423+
throw new MappingException(
424+
String.format("Cannot set update value for map property '%s' to '%s'. Please use a Map or Map.Entry.",
425+
pUpdate.getPropertyPath(), pUpdate.getValue()));
426+
}
427+
428+
writeMap(entity.getKeySpace(), pUpdate.getPropertyPath(), targetProperty.getMapValueType(), map, sink);
429+
}
401430

402-
writeInternal(entity.getKeySpace(), pUpdate.getPropertyPath(), pUpdate.getValue(),
403-
persistentPropertyPath.getLeafProperty().getTypeInformation(), sink);
431+
else {
404432

405-
sink.addIndexedData(data);
433+
writeInternal(entity.getKeySpace(), pUpdate.getPropertyPath(), pUpdate.getValue(),
434+
targetProperty.getTypeInformation(), sink);
435+
436+
Set<IndexedData> data = indexResolver.resolveIndexesFor(entity.getKeySpace(), pUpdate.getPropertyPath(),
437+
targetProperty.getTypeInformation(), pUpdate.getValue());
438+
439+
sink.addIndexedData(data);
440+
}
406441
}
407442
}
408443
}
409444

445+
KeyValuePersistentProperty getTargetPropertyOrNullForPath(String path, Class<?> type) {
446+
447+
try {
448+
449+
PersistentPropertyPath<KeyValuePersistentProperty> persistentPropertyPath = mappingContext
450+
.getPersistentPropertyPath(path, type);
451+
return persistentPropertyPath.getLeafProperty();
452+
} catch (Exception e) {
453+
// that's just fine
454+
}
455+
456+
return null;
457+
}
458+
410459
/**
411460
* @param keyspace
412461
* @param path

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

Lines changed: 210 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.springframework.core.convert.converter.Converter;
5555
import org.springframework.data.convert.ReadingConverter;
5656
import org.springframework.data.convert.WritingConverter;
57+
import org.springframework.data.mapping.model.MappingException;
5758
import org.springframework.data.redis.core.PartialUpdate;
5859
import org.springframework.data.redis.core.convert.ConversionTestEntities.Address;
5960
import org.springframework.data.redis.core.convert.ConversionTestEntities.AddressWithId;
@@ -1345,7 +1346,7 @@ public void writeShouldNotAppendClassTypeHint() {
13451346
* @see DATAREDIS-471
13461347
*/
13471348
@Test
1348-
public void writeShouldWritePartialUpdateValueCorrectly() {
1349+
public void writeShouldWritePartialUpdateSimpleValueCorrectly() {
13491350

13501351
Person value = new Person();
13511352
value.firstname = "rand";
@@ -1361,11 +1362,7 @@ public void writeShouldWritePartialUpdateValueCorrectly() {
13611362
* @see DATAREDIS-471
13621363
*/
13631364
@Test
1364-
public void writeShouldWritePartialUpdatePathValueCorrectly() {
1365-
1366-
Person value = new Person();
1367-
value.firstname = "rand";
1368-
value.age = 24;
1365+
public void writeShouldWritePartialUpdatePathWithSimpleValueCorrectly() {
13691366

13701367
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("firstname", "rand").set("age",
13711368
24);
@@ -1374,6 +1371,213 @@ public void writeShouldWritePartialUpdatePathValueCorrectly() {
13741371
isBucket().containingUtf8String("firstname", "rand").containingUtf8String("age", "24"));
13751372
}
13761373

1374+
/**
1375+
* @see DATAREDIS-471
1376+
*/
1377+
@Test
1378+
public void writeShouldWritePartialUpdateNestedPathWithSimpleValueCorrectly() {
1379+
1380+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("address.city", "two rivers");
1381+
1382+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("address.city", "two rivers"));
1383+
}
1384+
1385+
/**
1386+
* @see DATAREDIS-471
1387+
*/
1388+
@Test
1389+
public void writeShouldWritePartialUpdatePathWithComplexValueCorrectly() {
1390+
1391+
Address address = new Address();
1392+
address.city = "two rivers";
1393+
address.country = "andor";
1394+
1395+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("address", address);
1396+
1397+
assertThat(write(update).getBucket(),
1398+
isBucket().containingUtf8String("address.city", "two rivers").containingUtf8String("address.country", "andor"));
1399+
}
1400+
1401+
/**
1402+
* @see DATAREDIS-471
1403+
*/
1404+
@Test
1405+
public void writeShouldWritePartialUpdatePathWithSimpleListValueCorrectly() {
1406+
1407+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("nicknames",
1408+
Arrays.asList("dragon", "lews"));
1409+
1410+
assertThat(write(update).getBucket(),
1411+
isBucket().containingUtf8String("nicknames.[0]", "dragon").containingUtf8String("nicknames.[1]", "lews"));
1412+
}
1413+
1414+
/**
1415+
* @see DATAREDIS-471
1416+
*/
1417+
@Test
1418+
public void writeShouldWritePartialUpdatePathWithComplexListValueCorrectly() {
1419+
1420+
Person mat = new Person();
1421+
mat.firstname = "mat";
1422+
mat.age = 24;
1423+
1424+
Person perrin = new Person();
1425+
perrin.firstname = "perrin";
1426+
1427+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("coworkers",
1428+
Arrays.asList(mat, perrin));
1429+
1430+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("coworkers.[0].firstname", "mat")
1431+
.containingUtf8String("coworkers.[0].age", "24").containingUtf8String("coworkers.[1].firstname", "perrin"));
1432+
}
1433+
1434+
/**
1435+
* @see DATAREDIS-471
1436+
*/
1437+
@Test
1438+
public void writeShouldWritePartialUpdatePathWithSimpleListValueWhenNotPassedInAsCollectionCorrectly() {
1439+
1440+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("nicknames", "dragon");
1441+
1442+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("nicknames.[0]", "dragon"));
1443+
}
1444+
1445+
/**
1446+
* @see DATAREDIS-471
1447+
*/
1448+
@Test
1449+
public void writeShouldWritePartialUpdatePathWithComplexListValueWhenNotPassedInAsCollectionCorrectly() {
1450+
1451+
Person mat = new Person();
1452+
mat.firstname = "mat";
1453+
mat.age = 24;
1454+
1455+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("coworkers", mat);
1456+
1457+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("coworkers.[0].firstname", "mat")
1458+
.containingUtf8String("coworkers.[0].age", "24"));
1459+
}
1460+
1461+
/**
1462+
* @see DATAREDIS-471
1463+
*/
1464+
@Test
1465+
public void writeShouldWritePartialUpdatePathWithSimpleListValueWhenNotPassedInAsCollectionWithPositionalParameterCorrectly() {
1466+
1467+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("nicknames.[5]", "dragon");
1468+
1469+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("nicknames.[5]", "dragon"));
1470+
}
1471+
1472+
/**
1473+
* @see DATAREDIS-471
1474+
*/
1475+
@Test
1476+
public void writeShouldWritePartialUpdatePathWithComplexListValueWhenNotPassedInAsCollectionWithPositionalParameterCorrectly() {
1477+
1478+
Person mat = new Person();
1479+
mat.firstname = "mat";
1480+
mat.age = 24;
1481+
1482+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("coworkers.[5]", mat);
1483+
1484+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("coworkers.[5].firstname", "mat")
1485+
.containingUtf8String("coworkers.[5].age", "24"));
1486+
}
1487+
1488+
/**
1489+
* @see DATAREDIS-471
1490+
*/
1491+
@Test
1492+
public void writeShouldWritePartialUpdatePathWithSimpleMapValueCorrectly() {
1493+
1494+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("physicalAttributes",
1495+
Collections.singletonMap("eye-color", "grey"));
1496+
1497+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("physicalAttributes.[eye-color]", "grey"));
1498+
}
1499+
1500+
/**
1501+
* @see DATAREDIS-471
1502+
*/
1503+
@Test
1504+
public void writeShouldWritePartialUpdatePathWithComplexMapValueCorrectly() {
1505+
1506+
Person tam = new Person();
1507+
tam.firstname = "tam";
1508+
tam.alive = false;
1509+
1510+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("relatives",
1511+
Collections.singletonMap("father", tam));
1512+
1513+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("relatives.[father].firstname", "tam")
1514+
.containingUtf8String("relatives.[father].alive", "0"));
1515+
}
1516+
1517+
/**
1518+
* @see DATAREDIS-471
1519+
*/
1520+
@Test
1521+
public void writeShouldWritePartialUpdatePathWithSimpleMapValueWhenNotPassedInAsCollectionCorrectly() {
1522+
1523+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("physicalAttributes",
1524+
Collections.singletonMap("eye-color", "grey").entrySet().iterator().next());
1525+
1526+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("physicalAttributes.[eye-color]", "grey"));
1527+
}
1528+
1529+
/**
1530+
* @see DATAREDIS-471
1531+
*/
1532+
@Test
1533+
public void writeShouldWritePartialUpdatePathWithComplexMapValueWhenNotPassedInAsCollectionCorrectly() {
1534+
1535+
Person tam = new Person();
1536+
tam.firstname = "tam";
1537+
tam.alive = false;
1538+
1539+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("relatives",
1540+
Collections.singletonMap("father", tam).entrySet().iterator().next());
1541+
1542+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("relatives.[father].firstname", "tam")
1543+
.containingUtf8String("relatives.[father].alive", "0"));
1544+
}
1545+
1546+
/**
1547+
* @see DATAREDIS-471
1548+
*/
1549+
@Test
1550+
public void writeShouldWritePartialUpdatePathWithSimpleMapValueWhenNotPassedInAsCollectionWithPositionalParameterCorrectly() {
1551+
1552+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("physicalAttributes.[eye-color]",
1553+
"grey");
1554+
1555+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("physicalAttributes.[eye-color]", "grey"));
1556+
}
1557+
1558+
/**
1559+
* @see DATAREDIS-471
1560+
*/
1561+
@Test
1562+
public void writeShouldWritePartialUpdatePathWithSimpleMapValueOnNestedElementCorrectly() {
1563+
1564+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("relatives.[father].firstname",
1565+
"tam");
1566+
1567+
assertThat(write(update).getBucket(), isBucket().containingUtf8String("relatives.[father].firstname", "tam"));
1568+
}
1569+
1570+
/**
1571+
* @see DATAREDIS-471
1572+
*/
1573+
@Test(expected = MappingException.class)
1574+
public void writeShouldThrowExceptionOnPartialUpdatePathWithSimpleMapValueWhenItsASingleValueWithoutPath() {
1575+
1576+
PartialUpdate<Person> update = new PartialUpdate<Person>("123", Person.class).set("physicalAttributes", "grey");
1577+
1578+
write(update);
1579+
}
1580+
13771581
private RedisData write(Object source) {
13781582

13791583
RedisData rdo = new RedisData();

0 commit comments

Comments
 (0)