Skip to content

Commit 3ecbb8f

Browse files
committed
DATAMONGO-2218 - Add support for replaceOne operation in BulkOperations
1 parent 39859b7 commit 3ecbb8f

File tree

4 files changed

+111
-25
lines changed

4 files changed

+111
-25
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperations.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ enum BulkMode {
135135
*/
136136
BulkOperations remove(List<Query> removes);
137137

138+
/**
139+
* Add a single replace operation to the bulk operation.
140+
*
141+
* @param query Update criteria.
142+
* @param document the document to replace, must not be {@literal null}.
143+
* @return the current {@link BulkOperations} instance with the replace added, will never be {@literal null}.
144+
*/
145+
BulkOperations replaceOne(Query query, Object document);
146+
138147
/**
139148
* Execute all bulk operations using the default write concern.
140149
*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,12 @@
1515
*/
1616
package org.springframework.data.mongodb.core;
1717

18+
import com.mongodb.WriteConcern;
19+
import com.mongodb.client.model.*;
1820
import lombok.NonNull;
1921
import lombok.Value;
20-
21-
import java.util.ArrayList;
22-
import java.util.Collections;
23-
import java.util.List;
24-
import java.util.Optional;
25-
import java.util.stream.Collectors;
26-
2722
import org.bson.Document;
2823
import org.bson.conversions.Bson;
29-
import org.springframework.dao.DataAccessException;
3024
import org.springframework.dao.support.PersistenceExceptionTranslator;
3125
import org.springframework.data.mongodb.core.convert.QueryMapper;
3226
import org.springframework.data.mongodb.core.convert.UpdateMapper;
@@ -38,18 +32,11 @@
3832
import org.springframework.lang.Nullable;
3933
import org.springframework.util.Assert;
4034

41-
import com.mongodb.BulkWriteException;
42-
import com.mongodb.WriteConcern;
43-
import com.mongodb.client.MongoCollection;
44-
import com.mongodb.client.model.BulkWriteOptions;
45-
import com.mongodb.client.model.DeleteManyModel;
46-
import com.mongodb.client.model.DeleteOneModel;
47-
import com.mongodb.client.model.DeleteOptions;
48-
import com.mongodb.client.model.InsertOneModel;
49-
import com.mongodb.client.model.UpdateManyModel;
50-
import com.mongodb.client.model.UpdateOneModel;
51-
import com.mongodb.client.model.UpdateOptions;
52-
import com.mongodb.client.model.WriteModel;
35+
import java.util.ArrayList;
36+
import java.util.Collections;
37+
import java.util.List;
38+
import java.util.Optional;
39+
import java.util.stream.Collectors;
5340

5441
/**
5542
* Default implementation for {@link BulkOperations}.
@@ -266,6 +253,32 @@ public BulkOperations remove(List<Query> removes) {
266253
return this;
267254
}
268255

256+
/*
257+
* (non-Javadoc)
258+
* @see org.springframework.data.mongodb.core.BulkOperations#replaceOne(org.springframework.data.mongodb.core.query.Query, java.lang.Object)
259+
*/
260+
@Override
261+
public BulkOperations replaceOne(Query query, Object document) {
262+
263+
Assert.notNull(query, "Query must not be null!");
264+
Assert.notNull(document, "Document must not be null!");
265+
266+
ReplaceOptions replaceOptions = new ReplaceOptions();
267+
query.getCollation().map(Collation::toMongoCollation).ifPresent(replaceOptions::collation);
268+
Bson mappedQuery = getMappedQuery(query.getQueryObject());
269+
270+
if (document instanceof Document) {
271+
models.add(new ReplaceOneModel<>(mappedQuery, (Document) document, replaceOptions));
272+
return this;
273+
}
274+
275+
Document sink = new Document();
276+
mongoOperations.getConverter().write(document, sink);
277+
models.add(new ReplaceOneModel<>(mappedQuery, sink, replaceOptions));
278+
279+
return this;
280+
}
281+
269282
/*
270283
* (non-Javadoc)
271284
* @see org.springframework.data.mongodb.core.BulkOperations#executeBulk()
@@ -274,7 +287,7 @@ public BulkOperations remove(List<Query> removes) {
274287
public com.mongodb.bulk.BulkWriteResult execute() {
275288

276289
try {
277-
290+
278291
return mongoOperations.execute(collectionName, collection -> {
279292
return collection.bulkWrite(models.stream().map(this::mapWriteModel).collect(Collectors.toList()), bulkOptions);
280293
});

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,31 @@ public void removeUnordered() {
200200
testRemove(BulkMode.UNORDERED);
201201
}
202202

203+
@Test // DATAMONGO-2218
204+
public void replaceOneOrdered() {
205+
testReplaceOne(BulkMode.ORDERED);
206+
}
207+
208+
@Test // DATAMONGO-2218
209+
public void replaceOneUnordered() {
210+
testReplaceOne(BulkMode.UNORDERED);
211+
}
212+
213+
@Test // DATAMONGO-2218
214+
public void replaceOneDoesReplace() {
215+
216+
insertSomeDocuments();
217+
218+
com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).//
219+
replaceOne(where("_id", "1"), rawDoc("1", "value2")).//
220+
execute();
221+
222+
assertThat(result, notNullValue());
223+
assertThat(result.getMatchedCount(), is(1));
224+
assertThat(result.getModifiedCount(), is(1));
225+
assertThat(result.getInsertedCount(), is(0));
226+
}
227+
203228
/**
204229
* If working on the same set of documents, only an ordered bulk operation will yield predictable results.
205230
*/
@@ -278,6 +303,19 @@ private void testRemove(BulkMode mode) {
278303
assertThat(createBulkOps(mode).remove(removes).execute().getDeletedCount(), is(3));
279304
}
280305

306+
private void testReplaceOne(BulkMode mode) {
307+
308+
BulkOperations bulkOps = createBulkOps(mode);
309+
310+
insertSomeDocuments();
311+
312+
Query query = where("_id", "1");
313+
Document document = rawDoc("1", "value2");
314+
int modifiedCount = bulkOps.replaceOne(query, document).execute().getModifiedCount();
315+
316+
assertThat(modifiedCount, is(1));
317+
}
318+
281319
private BulkOperations createBulkOps(BulkMode mode) {
282320
return createBulkOps(mode, null);
283321
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsUnitTests.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.List;
2727
import java.util.Optional;
2828

29+
import com.mongodb.client.model.*;
2930
import org.bson.Document;
3031
import org.junit.Before;
3132
import org.junit.Test;
@@ -51,10 +52,6 @@
5152

5253
import com.mongodb.client.MongoCollection;
5354
import com.mongodb.client.MongoDatabase;
54-
import com.mongodb.client.model.DeleteManyModel;
55-
import com.mongodb.client.model.UpdateManyModel;
56-
import com.mongodb.client.model.UpdateOneModel;
57-
import com.mongodb.client.model.WriteModel;
5855

5956
/**
6057
* Unit tests for {@link DefaultBulkOperations}.
@@ -133,6 +130,18 @@ public void removeShouldUseCollationWhenPresent() {
133130
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build());
134131
}
135132

133+
@Test // DATAMONGO-2218
134+
public void replaceOneShouldUseCollationWhenPresent() {
135+
136+
ops.replaceOne(new BasicQuery("{}").collation(Collation.of("de")), new SomeDomainType()).execute();
137+
138+
verify(collection).bulkWrite(captor.capture(), any());
139+
140+
assertThat(captor.getValue().get(0)).isInstanceOf(ReplaceOneModel.class);
141+
assertThat(((ReplaceOneModel<Document>) captor.getValue().get(0)).getReplaceOptions().getCollation())
142+
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("de").build());
143+
}
144+
136145
@Test // DATAMONGO-1678
137146
public void bulkUpdateShouldMapQueryAndUpdateCorrectly() {
138147

@@ -156,6 +165,23 @@ public void bulkRemoveShouldMapQueryCorrectly() {
156165
assertThat(updateModel.getFilter()).isEqualTo(new Document("first_name", "danerys"));
157166
}
158167

168+
@Test // DATAMONGO-2218
169+
public void bulkReplaceOneShouldMapQueryCorrectly() {
170+
171+
SomeDomainType replacement = new SomeDomainType();
172+
replacement.firstName = "Minsu";
173+
replacement.lastName = "Kim";
174+
175+
ops.replaceOne(query(where("firstName").is("danerys")), replacement).execute();
176+
177+
verify(collection).bulkWrite(captor.capture(), any());
178+
179+
ReplaceOneModel<Document> updateModel = (ReplaceOneModel<Document>) captor.getValue().get(0);
180+
assertThat(updateModel.getFilter()).isEqualTo(new Document("first_name", "danerys"));
181+
assertThat(updateModel.getReplacement().getString("first_name")).isEqualTo("Minsu");
182+
assertThat(updateModel.getReplacement().getString("lastName")).isEqualTo("Kim");
183+
}
184+
159185
class SomeDomainType {
160186

161187
@Id String id;

0 commit comments

Comments
 (0)