Skip to content

Commit 49ff6f1

Browse files
Add fluent & reactive API for replace operation.
...and update the documentation. See: #4462 Original Pull Request: #4463
1 parent f7549f7 commit 49ff6f1

17 files changed

+1171
-159
lines changed

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

+36-2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,24 @@ default Optional<T> findAndModify() {
8787
T findAndModifyValue();
8888
}
8989

90+
/**
91+
* Trigger <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a>
92+
* execution by calling one of the terminating methods./** Trigger replace execution by calling one of the terminating
93+
* methods.
94+
*
95+
* @author Christoph Strobl
96+
* @since 4.2
97+
*/
98+
interface TerminatingReplace {
99+
100+
/**
101+
* Find first and replace/upsert.
102+
*
103+
* @return never {@literal null}.
104+
*/
105+
UpdateResult replaceFirst();
106+
}
107+
90108
/**
91109
* Trigger
92110
* <a href="https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndReplace/">findOneAndReplace</a>
@@ -95,7 +113,7 @@ default Optional<T> findAndModify() {
95113
* @author Mark Paluch
96114
* @since 2.1
97115
*/
98-
interface TerminatingFindAndReplace<T> {
116+
interface TerminatingFindAndReplace<T> extends TerminatingReplace {
99117

100118
/**
101119
* Find, replace and return the first matching document.
@@ -243,14 +261,30 @@ interface FindAndModifyWithOptions<T> {
243261
TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options);
244262
}
245263

264+
/**
265+
* @author Christoph Strobl
266+
* @since 4.2
267+
*/
268+
interface ReplaceWithOptions extends TerminatingReplace {
269+
270+
/**
271+
* Explicitly define {@link ReplaceOptions}.
272+
*
273+
* @param options must not be {@literal null}.
274+
* @return new instance of {@link FindAndReplaceOptions}.
275+
* @throws IllegalArgumentException if options is {@literal null}.
276+
*/
277+
TerminatingReplace withOptions(ReplaceOptions options);
278+
}
279+
246280
/**
247281
* Define {@link FindAndReplaceOptions}.
248282
*
249283
* @author Mark Paluch
250284
* @author Christoph Strobl
251285
* @since 2.1
252286
*/
253-
interface FindAndReplaceWithOptions<T> extends TerminatingFindAndReplace<T> {
287+
interface FindAndReplaceWithOptions<T> extends TerminatingFindAndReplace<T>, ReplaceWithOptions {
254288

255289
/**
256290
* Explicitly define {@link FindAndReplaceOptions} for the {@link Update}.

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

+20
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ public FindAndReplaceWithProjection<T> withOptions(FindAndReplaceOptions options
126126
options, replacement, targetType);
127127
}
128128

129+
@Override
130+
public TerminatingReplace withOptions(ReplaceOptions options) {
131+
132+
FindAndReplaceOptions target = new FindAndReplaceOptions();
133+
if(options.isUpsert()) {
134+
target.upsert();
135+
}
136+
return new ExecutableUpdateSupport<>(template, domainType, query, update, collection, findAndModifyOptions,
137+
target, replacement, targetType);
138+
}
139+
129140
@Override
130141
public UpdateWithUpdate<T> matching(Query query) {
131142

@@ -175,6 +186,15 @@ public UpdateResult upsert() {
175186
getCollectionName(), targetType);
176187
}
177188

189+
@Override
190+
public UpdateResult replaceFirst() {
191+
if(replacement != null) {
192+
return template.replace(query, domainType, replacement, findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName());
193+
}
194+
195+
return template.replace(query, domainType, update, findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName());
196+
}
197+
178198
private UpdateResult doUpdate(boolean multi, boolean upsert) {
179199
return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi);
180200
}

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

+2-12
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,9 @@
3131
* @author Christoph Strobl
3232
* @since 2.1
3333
*/
34-
public class FindAndReplaceOptions {
34+
public class FindAndReplaceOptions extends ReplaceOptions {
3535

3636
private boolean returnNew;
37-
private boolean upsert;
3837

3938
private static final FindAndReplaceOptions NONE = new FindAndReplaceOptions() {
4039

@@ -109,7 +108,7 @@ public FindAndReplaceOptions returnNew() {
109108
*/
110109
public FindAndReplaceOptions upsert() {
111110

112-
this.upsert = true;
111+
super.upsert();
113112
return this;
114113
}
115114

@@ -122,13 +121,4 @@ public boolean isReturnNew() {
122121
return returnNew;
123122
}
124123

125-
/**
126-
* Get the bit indicating if to create a new document if not exists.
127-
*
128-
* @return {@literal true} if set.
129-
*/
130-
public boolean isUpsert() {
131-
return upsert;
132-
}
133-
134124
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
*
2222
* @author Mark Pollack
2323
* @author Oliver Gierke
24+
* @author Christoph Strobl
2425
* @see MongoAction
2526
*/
2627
public enum MongoActionOperation {
2728

28-
REMOVE, UPDATE, INSERT, INSERT_LIST, SAVE, BULK;
29+
REMOVE, UPDATE, INSERT, INSERT_LIST, SAVE, BULK, REPLACE;
2930
}

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

+50-53
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
4141
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
4242
import org.springframework.data.mongodb.core.query.BasicQuery;
43+
import org.springframework.data.mongodb.core.query.Collation;
4344
import org.springframework.data.mongodb.core.query.Criteria;
4445
import org.springframework.data.mongodb.core.query.NearQuery;
4546
import org.springframework.data.mongodb.core.query.Query;
@@ -1754,119 +1755,115 @@ default long exactCount(Query query, String collectionName) {
17541755
<T> List<T> findAllAndRemove(Query query, Class<T> entityClass, String collectionName);
17551756

17561757
/**
1757-
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
1758-
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document.
1759-
* <br />
1758+
* Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement}
1759+
* document. <br />
17601760
* The collection name is derived from the {@literal replacement} type. <br />
1761-
* Options are defaulted to {@link ReplaceOptions#empty()}. <br />
1762-
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
1761+
* Options are defaulted to {@link ReplaceOptions#none()}.
17631762
*
1764-
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
1765-
* fields specification. Must not be {@literal null}.
1763+
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record. The query may
1764+
* contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation}
1765+
* to use. Must not be {@literal null}.
17661766
* @param replacement the replacement document. Must not be {@literal null}.
17671767
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
17681768
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
17691769
* {@link #getCollectionName(Class) derived} from the given replacement value.
1770+
* @since 4.2
17701771
*/
17711772
default <T> UpdateResult replace(Query query, T replacement) {
1772-
return replace(query, replacement, ReplaceOptions.empty());
1773+
return replace(query, replacement, ReplaceOptions.none());
17731774
}
17741775

17751776
/**
1776-
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
1777-
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement}
1778-
* document.<br />
1779-
* Options are defaulted to {@link ReplaceOptions#empty()}. <br />
1780-
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
1777+
* Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement}
1778+
* document. Options are defaulted to {@link ReplaceOptions#none()}.
17811779
*
1782-
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
1783-
* fields specification. Must not be {@literal null}.
1780+
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record. The query may
1781+
* contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation}
1782+
* to use. Must not be {@literal null}.
17841783
* @param replacement the replacement document. Must not be {@literal null}.
17851784
* @param collectionName the collection to query. Must not be {@literal null}.
17861785
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
17871786
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
17881787
* {@link #getCollectionName(Class) derived} from the given replacement value.
1788+
* @since 4.2
17891789
*/
17901790
default <T> UpdateResult replace(Query query, T replacement, String collectionName) {
1791-
return replace(query, replacement, ReplaceOptions.empty(), collectionName);
1791+
return replace(query, replacement, ReplaceOptions.none(), collectionName);
17921792
}
17931793

17941794
/**
1795-
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
1796-
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
1797-
* taking {@link ReplaceOptions} into account.<br />
1798-
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
1795+
* Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement}
1796+
* document taking {@link ReplaceOptions} into account.
17991797
*
1800-
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
1801-
* fields specification. Must not be {@literal null}.
1798+
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record.The query may
1799+
* contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation}
1800+
* to use. Must not be {@literal null}.
18021801
* @param replacement the replacement document. Must not be {@literal null}.
1803-
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
1802+
* @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}.
18041803
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
18051804
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
18061805
* {@link #getCollectionName(Class) derived} from the given replacement value.
1806+
* @since 4.2
18071807
*/
18081808
default <T> UpdateResult replace(Query query, T replacement, ReplaceOptions options) {
18091809
return replace(query, replacement, options, getCollectionName(ClassUtils.getUserClass(replacement)));
18101810
}
18111811

18121812
/**
1813-
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
1814-
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
1815-
* taking {@link ReplaceOptions} into account.<br />
1816-
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
1813+
* Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement}
1814+
* document taking {@link ReplaceOptions} into account.
18171815
*
1818-
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
1819-
* fields specification. Must not be {@literal null}.
1816+
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record. The query may *
1817+
* contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation}
1818+
* to use. Must not be {@literal null}.
18201819
* @param replacement the replacement document. Must not be {@literal null}.
1821-
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
1820+
* @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}.
18221821
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
18231822
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
18241823
* {@link #getCollectionName(Class) derived} from the given replacement value.
1824+
* @since 4.2
18251825
*/
18261826
default <T> UpdateResult replace(Query query, T replacement, ReplaceOptions options, String collectionName) {
18271827

18281828
Assert.notNull(replacement, "Replacement must not be null");
1829-
return replace(query, replacement, options, (Class<T>) ClassUtils.getUserClass(replacement), collectionName);
1829+
return replace(query, (Class<T>) ClassUtils.getUserClass(replacement), replacement, options, collectionName);
18301830
}
18311831

18321832
/**
1833-
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
1834-
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
1835-
* taking {@link ReplaceOptions} into account.<br />
1836-
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
1833+
* Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement}
1834+
* document taking {@link ReplaceOptions} into account.
18371835
*
1838-
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
1839-
* fields specification. Must not be {@literal null}.
1840-
* @param replacement the replacement document. Must not be {@literal null}.
1841-
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
1836+
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record. The query may
1837+
* contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation}
1838+
* to use. Must not be {@literal null}.
18421839
* @param entityType the type used for mapping the {@link Query} to domain type fields and deriving the collection
1840+
* @param replacement the replacement document. Must not be {@literal null}.
1841+
* @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}.
18431842
* from. Must not be {@literal null}.
18441843
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
18451844
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
18461845
* {@link #getCollectionName(Class) derived} from the given replacement value.
1846+
* @since 4.2
18471847
*/
1848-
default <S> UpdateResult replace(Query query, S replacement, ReplaceOptions options, Class<S> entityType) {
1849-
1850-
return replace(query, replacement, options, entityType, getCollectionName(ClassUtils.getUserClass(entityType)));
1848+
default <S,T> UpdateResult replace(Query query, Class<S> entityType, T replacement, ReplaceOptions options) {
1849+
return replace(query, entityType, replacement, options, getCollectionName(ClassUtils.getUserClass(entityType)));
18511850
}
18521851

18531852
/**
1854-
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
1855-
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
1856-
* taking {@link ReplaceOptions} into account.<br />
1857-
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
1853+
* Replace a single document matching the {@link Criteria} of given {@link Query} with the {@code replacement}
1854+
* document taking {@link ReplaceOptions} into account.
18581855
*
1859-
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
1860-
* fields specification. Must not be {@literal null}.
1861-
* @param replacement the replacement document. Must not be {@literal null}.
1862-
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
1856+
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record. The query may
1857+
* contain an index {@link Query#withHint(String) hint} or the {@link Query#collation(Collation) collation}
1858+
* to use. Must not be {@literal null}.
18631859
* @param entityType the type used for mapping the {@link Query} to domain type fields. Must not be {@literal null}.
1860+
* @param replacement the replacement document. Must not be {@literal null}.
1861+
* @param options the {@link ReplaceOptions} holding additional information. Must not be {@literal null}.
18641862
* @param collectionName the collection to query. Must not be {@literal null}.
18651863
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
1866-
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
1867-
* {@link #getCollectionName(Class) derived} from the given replacement value.
1864+
* @since 4.2
18681865
*/
1869-
<S> UpdateResult replace(Query query, S replacement, ReplaceOptions options, Class<S> entityType,
1866+
<S,T> UpdateResult replace(Query query, Class<S> entityType, T replacement, ReplaceOptions options,
18701867
String collectionName);
18711868

18721869
/**

0 commit comments

Comments
 (0)