Skip to content

Commit f5a506c

Browse files
christophstroblmp911de
authored andcommitted
DATAREDIS-530 - Fix PartialUpdate removing existing indexes.
We now make sure to leave existing indexes untouched when using PartialUpdate. We also fixed a glitch, where index values have not been removed correctly when saving entities with null values, along the way. Original pull request: #207.
1 parent 0d24b5a commit f5a506c

File tree

5 files changed

+100
-12
lines changed

5 files changed

+100
-12
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ public void createIndexes(Object key, Iterable<IndexedData> indexValues) {
7676
* @param indexValues can be {@literal null}.
7777
*/
7878
public void updateIndexes(Object key, Iterable<IndexedData> indexValues) {
79+
createOrUpdateIndexes(key, indexValues, IndexWriteMode.PARTIAL_UPDATE);
80+
}
81+
82+
/**
83+
* Updates indexes by first removing key from existing one and then persisting new index data.
84+
*
85+
* @param key must not be {@literal null}.
86+
* @param indexValues can be {@literal null}.
87+
*/
88+
public void deleteAndUpdateIndexes(Object key, Iterable<IndexedData> indexValues) {
7989
createOrUpdateIndexes(key, indexValues, IndexWriteMode.UPDATE);
8090
}
8191

@@ -96,7 +106,7 @@ private void createOrUpdateIndexes(Object key, Iterable<IndexedData> indexValues
96106
removeKeyFromIndexes(data.getKeyspace(), binKey);
97107
}
98108
}
99-
109+
} else if (ObjectUtils.nullSafeEquals(IndexWriteMode.PARTIAL_UPDATE, writeMode)) {
100110
removeKeyFromExistingIndexes(binKey, indexValues);
101111
}
102112

@@ -229,6 +239,6 @@ private byte[] toBytes(Object source) {
229239
*/
230240
private static enum IndexWriteMode {
231241

232-
CREATE, UPDATE
242+
CREATE, UPDATE, PARTIAL_UPDATE
233243
}
234244
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ public Object doInRedis(RedisConnection connection) throws DataAccessException {
236236
if (isNew) {
237237
indexWriter.createIndexes(key, rdo.getIndexedData());
238238
} else {
239-
indexWriter.updateIndexes(key, rdo.getIndexedData());
239+
indexWriter.deleteAndUpdateIndexes(key, rdo.getIndexedData());
240240
}
241241
return null;
242242
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.springframework.data.keyvalue.core.KeyValueAdapter;
2424
import org.springframework.data.keyvalue.core.KeyValueCallback;
2525
import org.springframework.data.keyvalue.core.KeyValueTemplate;
26-
import org.springframework.data.redis.RedisSystemException;
2726
import org.springframework.data.redis.core.mapping.RedisMappingContext;
2827
import org.springframework.util.Assert;
2928
import org.springframework.util.ClassUtils;
@@ -121,6 +120,7 @@ public void insert(final Serializable id, final Object objectToInsert) {
121120

122121
if (objectToInsert instanceof PartialUpdate) {
123122
doPartialUpdate((PartialUpdate<?>) objectToInsert);
123+
return;
124124
}
125125

126126
super.insert(id, objectToInsert);
@@ -135,6 +135,7 @@ public void update(Object objectToUpdate) {
135135

136136
if (objectToUpdate instanceof PartialUpdate) {
137137
doPartialUpdate((PartialUpdate<?>) objectToUpdate);
138+
return;
138139
}
139140

140141
super.update(objectToUpdate);

src/test/java/org/springframework/data/redis/core/RedisKeyValueAdapterUnitTests.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import static org.hamcrest.CoreMatchers.*;
2020
import static org.junit.Assert.*;
2121
import static org.mockito.Matchers.*;
22-
import static org.mockito.Matchers.any;
2322
import static org.mockito.Mockito.*;
2423
import static org.springframework.test.util.ReflectionTestUtils.*;
2524

@@ -98,20 +97,22 @@ public void destroyShouldNotDestroyConnectionFactory() throws Exception {
9897

9998
/**
10099
* @see DATAREDIS-512
100+
* @see DATAREDIS-530
101101
*/
102102
@Test
103103
public void putShouldRemoveExistingIndexValuesWhenUpdating() {
104104

105105
RedisData rd = new RedisData(Bucket.newBucketFromStringMap(Collections.singletonMap("_id", "1")));
106106
rd.addIndexedData(new SimpleIndexedPropertyValue("persons", "firstname", "rand"));
107107

108-
when(redisConnectionMock.keys(any(byte[].class)))
108+
when(redisConnectionMock.sMembers(org.mockito.Matchers.any(byte[].class)))
109109
.thenReturn(new LinkedHashSet<byte[]>(Arrays.asList("persons:firstname:rand".getBytes())));
110110
when(redisConnectionMock.del((byte[][]) anyVararg())).thenReturn(1L);
111111

112112
adapter.put("1", rd, "persons");
113113

114-
verify(redisConnectionMock, times(1)).sRem(any(byte[].class), any(byte[].class));
114+
verify(redisConnectionMock, times(1)).sRem(org.mockito.Matchers.any(byte[].class),
115+
org.mockito.Matchers.any(byte[].class));
115116
}
116117

117118
/**
@@ -123,20 +124,20 @@ public void putShouldNotTryToRemoveExistingIndexValuesWhenInsertingNew() {
123124
RedisData rd = new RedisData(Bucket.newBucketFromStringMap(Collections.singletonMap("_id", "1")));
124125
rd.addIndexedData(new SimpleIndexedPropertyValue("persons", "firstname", "rand"));
125126

126-
when(redisConnectionMock.sMembers(any(byte[].class)))
127+
when(redisConnectionMock.sMembers(org.mockito.Matchers.any(byte[].class)))
127128
.thenReturn(new LinkedHashSet<byte[]>(Arrays.asList("persons:firstname:rand".getBytes())));
128129
when(redisConnectionMock.del((byte[][]) anyVararg())).thenReturn(0L);
129130

130131
adapter.put("1", rd, "persons");
131132

132-
verify(redisConnectionMock, never()).sRem(any(byte[].class), (byte[][]) anyVararg());
133+
verify(redisConnectionMock, never()).sRem(org.mockito.Matchers.any(byte[].class), (byte[][]) anyVararg());
133134
}
134135

135136
/**
136137
* @see DATAREDIS-491
137138
*/
138139
@Test
139-
public void shouldInitKeyExpirationListenerOnStartup() throws Exception{
140+
public void shouldInitKeyExpirationListenerOnStartup() throws Exception {
140141

141142
adapter.destroy();
142143

src/test/java/org/springframework/data/redis/core/RedisKeyValueTemplateTests.java

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public void partialUpdate() {
229229
Person update1 = new Person(rand.id, null, "al-thor");
230230
PartialUpdate<Person> update = new PartialUpdate<Person>(rand.id, update1);
231231

232-
template.doPartialUpdate(update);
232+
template.insert(update);
233233

234234
assertThat(template.findById(rand.id, Person.class), is(equalTo(new Person(rand.id, "rand", "al-thor"))));
235235
nativeTemplate.execute(new RedisCallback<Void>() {
@@ -251,7 +251,7 @@ public Void doInRedis(RedisConnection connection) throws DataAccessException {
251251
*/
252252
update = new PartialUpdate<Person>(rand.id, Person.class).set("firstname", "frodo");
253253

254-
template.doPartialUpdate(update);
254+
template.update(update);
255255

256256
assertThat(template.findById(rand.id, Person.class), is(equalTo(new Person(rand.id, "frodo", "al-thor"))));
257257
nativeTemplate.execute(new RedisCallback<Void>() {
@@ -790,6 +790,81 @@ public Void doInRedis(RedisConnection connection) throws DataAccessException {
790790
});
791791
}
792792

793+
/**
794+
* @see DATAREDIS-530
795+
*/
796+
@Test
797+
public void partialUpdateShouldLeaveIndexesNotInvolvedInUpdateUntouched() {
798+
799+
final Person rand = new Person();
800+
rand.firstname = "rand";
801+
rand.lastname = "al-thor";
802+
rand.email = "rand@twof.com";
803+
804+
template.insert(rand);
805+
806+
/*
807+
* Set the lastname and make sure we've an index on it afterwards
808+
*/
809+
PartialUpdate<Person> update = PartialUpdate.newPartialUpdate(rand.id, Person.class).set("lastname", "doe");
810+
811+
template.doPartialUpdate(update);
812+
813+
nativeTemplate.execute(new RedisCallback<Void>() {
814+
815+
@Override
816+
public Void doInRedis(RedisConnection connection) throws DataAccessException {
817+
818+
assertThat(connection.hGet(("template-test-person:" + rand.id).getBytes(), "lastname".getBytes()),
819+
is(equalTo("doe".getBytes())));
820+
assertThat(connection.exists("template-test-person:email:rand@twof.com".getBytes()), is(true));
821+
assertThat(connection.exists("template-test-person:lastname:al-thor".getBytes()), is(false));
822+
assertThat(connection.sIsMember("template-test-person:lastname:doe".getBytes(), rand.id.getBytes()), is(true));
823+
return null;
824+
}
825+
});
826+
}
827+
828+
/**
829+
* @see DATAREDIS-530
830+
*/
831+
@Test
832+
public void updateShouldAlterIndexesCorrectlyWhenValuesGetRemovedFromHash() {
833+
834+
final Person rand = new Person();
835+
rand.firstname = "rand";
836+
rand.lastname = "al-thor";
837+
rand.email = "rand@twof.com";
838+
839+
template.insert(rand);
840+
841+
nativeTemplate.execute(new RedisCallback<Void>() {
842+
843+
@Override
844+
public Void doInRedis(RedisConnection connection) throws DataAccessException {
845+
846+
assertThat(connection.exists("template-test-person:email:rand@twof.com".getBytes()), is(true));
847+
assertThat(connection.exists("template-test-person:lastname:al-thor".getBytes()), is(true));
848+
return null;
849+
}
850+
});
851+
852+
rand.lastname = null;
853+
854+
template.update(rand);
855+
856+
nativeTemplate.execute(new RedisCallback<Void>() {
857+
858+
@Override
859+
public Void doInRedis(RedisConnection connection) throws DataAccessException {
860+
861+
assertThat(connection.exists("template-test-person:email:rand@twof.com".getBytes()), is(true));
862+
assertThat(connection.exists("template-test-person:lastname:al-thor".getBytes()), is(false));
863+
return null;
864+
}
865+
});
866+
}
867+
793868
@EqualsAndHashCode
794869
@RedisHash("template-test-type-mapping")
795870
static class VariousTypes {
@@ -828,6 +903,7 @@ static class Person {
828903
@Id String id;
829904
String firstname;
830905
@Indexed String lastname;
906+
@Indexed String email;
831907
Integer age;
832908
List<String> nicknames;
833909

0 commit comments

Comments
 (0)