Skip to content

Commit 331bb3e

Browse files
authored
Reduced duplication in the FindAndModify operations (#898)
JAVA-4544
1 parent e445622 commit 331bb3e

File tree

4 files changed

+328
-741
lines changed

4 files changed

+328
-741
lines changed

driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java

Lines changed: 243 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,34 @@
1818

1919
import com.mongodb.MongoNamespace;
2020
import com.mongodb.WriteConcern;
21-
import com.mongodb.internal.async.SingleResultCallback;
21+
import com.mongodb.client.model.Collation;
2222
import com.mongodb.connection.ConnectionDescription;
23-
import com.mongodb.connection.ServerDescription;
23+
import com.mongodb.internal.async.SingleResultCallback;
2424
import com.mongodb.internal.binding.AsyncWriteBinding;
2525
import com.mongodb.internal.binding.WriteBinding;
2626
import com.mongodb.internal.session.SessionContext;
27+
import com.mongodb.lang.Nullable;
2728
import org.bson.BsonDocument;
2829
import org.bson.BsonInt64;
30+
import org.bson.BsonString;
31+
import org.bson.BsonValue;
2932
import org.bson.FieldNameValidator;
3033
import org.bson.codecs.Decoder;
34+
import org.bson.conversions.Bson;
35+
36+
import java.util.concurrent.TimeUnit;
3137

3238
import static com.mongodb.assertions.Assertions.notNull;
3339
import static com.mongodb.internal.operation.CommandOperationHelper.CommandCreator;
3440
import static com.mongodb.internal.operation.CommandOperationHelper.executeRetryableWrite;
3541
import static com.mongodb.internal.operation.CommandOperationHelper.executeRetryableWriteAsync;
42+
import static com.mongodb.internal.operation.DocumentHelper.putIfNotNull;
43+
import static com.mongodb.internal.operation.DocumentHelper.putIfNotZero;
3644
import static com.mongodb.internal.operation.OperationHelper.isRetryableWrite;
45+
import static com.mongodb.internal.operation.OperationHelper.validateCollation;
46+
import static com.mongodb.internal.operation.OperationHelper.validateHintForFindAndModify;
3747
import static com.mongodb.internal.operation.ServerVersionHelper.serverIsAtLeastVersionThreeDotTwo;
48+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
3849

3950
/**
4051
* Abstract base class for findAndModify-based operations
@@ -49,6 +60,15 @@ public abstract class BaseFindAndModifyOperation<T> implements AsyncWriteOperati
4960
private final boolean retryWrites;
5061
private final Decoder<T> decoder;
5162

63+
private BsonDocument filter;
64+
private BsonDocument projection;
65+
private BsonDocument sort;
66+
private long maxTimeMS;
67+
private Collation collation;
68+
private Bson hint;
69+
private String hintString;
70+
private BsonValue comment;
71+
5272
/**
5373
* Construct a new instance.
5474
*
@@ -81,8 +101,6 @@ public void executeAsync(final AsyncWriteBinding binding, final SingleResultCall
81101
getCommandCreator(binding.getSessionContext()), FindAndModifyHelper.asyncTransformer(), cmd -> cmd, callback);
82102
}
83103

84-
protected abstract String getDatabaseName();
85-
86104
/**
87105
* Gets the namespace.
88106
*
@@ -122,22 +140,231 @@ public boolean isRetryWrites() {
122140
return retryWrites;
123141
}
124142

125-
protected abstract CommandCreator getCommandCreator(SessionContext sessionContext);
126143

127-
protected void addTxnNumberToCommand(final ServerDescription serverDescription, final ConnectionDescription connectionDescription,
128-
final BsonDocument commandDocument, final SessionContext sessionContext) {
129-
if (isRetryableWrite(isRetryWrites(), getWriteConcern(), serverDescription, connectionDescription, sessionContext)) {
130-
commandDocument.put("txnNumber", new BsonInt64(sessionContext.advanceTransactionNumber()));
131-
}
144+
/**
145+
* Gets the query filter.
146+
*
147+
* @return the query filter
148+
* @mongodb.driver.manual reference/method/db.collection.find/ Filter
149+
*/
150+
public BsonDocument getFilter() {
151+
return filter;
152+
}
153+
154+
/**
155+
* Sets the query filter to apply to the query.
156+
*
157+
* @param filter the query filter, which may be null.
158+
* @return this
159+
* @mongodb.driver.manual reference/method/db.collection.find/ Filter
160+
*/
161+
public BaseFindAndModifyOperation<T> filter(final BsonDocument filter) {
162+
this.filter = filter;
163+
return this;
164+
}
165+
166+
/**
167+
* Gets a document describing the fields to return for all matching documents.
168+
*
169+
* @return the project document, which may be null
170+
* @mongodb.driver.manual reference/method/db.collection.find/ Projection
171+
*/
172+
public BsonDocument getProjection() {
173+
return projection;
174+
}
175+
176+
/**
177+
* Sets a document describing the fields to return for all matching documents.
178+
*
179+
* @param projection the project document, which may be null.
180+
* @return this
181+
* @mongodb.driver.manual reference/method/db.collection.find/ Projection
182+
*/
183+
public BaseFindAndModifyOperation<T> projection(final BsonDocument projection) {
184+
this.projection = projection;
185+
return this;
186+
}
187+
188+
/**
189+
* Gets the maximum execution time on the server for this operation. The default is 0, which places no limit on the execution time.
190+
*
191+
* @param timeUnit the time unit to return the result in
192+
* @return the maximum execution time in the given time unit
193+
*/
194+
public long getMaxTime(final TimeUnit timeUnit) {
195+
notNull("timeUnit", timeUnit);
196+
return timeUnit.convert(maxTimeMS, TimeUnit.MILLISECONDS);
197+
}
198+
199+
/**
200+
* Sets the maximum execution time on the server for this operation.
201+
*
202+
* @param maxTime the max time
203+
* @param timeUnit the time unit, which may not be null
204+
* @return this
205+
*/
206+
public BaseFindAndModifyOperation<T> maxTime(final long maxTime, final TimeUnit timeUnit) {
207+
notNull("timeUnit", timeUnit);
208+
this.maxTimeMS = TimeUnit.MILLISECONDS.convert(maxTime, timeUnit);
209+
return this;
210+
}
211+
212+
/**
213+
* Gets the sort criteria to apply to the query. The default is null, which means that the documents will be returned in an undefined
214+
* order.
215+
*
216+
* @return a document describing the sort criteria
217+
* @mongodb.driver.manual reference/method/cursor.sort/ Sort
218+
*/
219+
public BsonDocument getSort() {
220+
return sort;
221+
}
222+
223+
/**
224+
* Sets the sort criteria to apply to the query.
225+
*
226+
* @param sort the sort criteria, which may be null.
227+
* @return this
228+
* @mongodb.driver.manual reference/method/cursor.sort/ Sort
229+
*/
230+
public BaseFindAndModifyOperation<T> sort(final BsonDocument sort) {
231+
this.sort = sort;
232+
return this;
233+
}
234+
235+
/**
236+
* Returns the collation options
237+
*
238+
* @return the collation options
239+
* @since 3.4
240+
* @mongodb.server.release 3.4
241+
*/
242+
public Collation getCollation() {
243+
return collation;
244+
}
245+
246+
/**
247+
* Returns the hint for which index to use. The default is not to set a hint.
248+
*
249+
* @return the hint
250+
* @since 4.1
251+
*/
252+
@Nullable
253+
public Bson getHint() {
254+
return hint;
255+
}
256+
257+
/**
258+
* Sets the hint for which index to use. A null value means no hint is set.
259+
*
260+
* @param hint the hint
261+
* @return this
262+
* @since 4.1
263+
*/
264+
public BaseFindAndModifyOperation<T> hint(@Nullable final Bson hint) {
265+
this.hint = hint;
266+
return this;
267+
}
268+
269+
/**
270+
* Gets the hint string to apply.
271+
*
272+
* @return the hint string, which should be the name of an existing index
273+
* @since 4.1
274+
*/
275+
@Nullable
276+
public String getHintString() {
277+
return hintString;
278+
}
279+
280+
/**
281+
* Sets the hint to apply.
282+
*
283+
* @param hint the name of the index which should be used for the operation
284+
* @return this
285+
* @since 4.1
286+
*/
287+
public BaseFindAndModifyOperation<T> hintString(@Nullable final String hint) {
288+
this.hintString = hint;
289+
return this;
290+
}
291+
292+
/**
293+
* Sets the collation options
294+
*
295+
* <p>A null value represents the server default.</p>
296+
* @param collation the collation options to use
297+
* @return this
298+
* @since 3.4
299+
* @mongodb.server.release 3.4
300+
*/
301+
public BaseFindAndModifyOperation<T> collation(final Collation collation) {
302+
this.collation = collation;
303+
return this;
304+
}
305+
306+
/**
307+
* @return comment for this operation. A null value means no comment is set.
308+
* @since 4.6
309+
* @mongodb.server.release 4.4
310+
*/
311+
public BsonValue getComment() {
312+
return comment;
132313
}
133314

134-
protected void addWriteConcernToCommand(final ConnectionDescription connectionDescription, final BsonDocument commandDocument,
135-
final SessionContext sessionContext) {
136-
if (getWriteConcern().isAcknowledged() && !getWriteConcern().isServerDefault()
137-
&& serverIsAtLeastVersionThreeDotTwo(connectionDescription) && !sessionContext.hasActiveTransaction()) {
138-
commandDocument.put("writeConcern", getWriteConcern().asDocument());
139-
}
315+
/**
316+
* Sets the comment for this operation. A null value means no comment is set.
317+
*
318+
* @param comment the comment
319+
* @return this
320+
* @since 4.6
321+
* @mongodb.server.release 4.4
322+
*/
323+
public BaseFindAndModifyOperation<T> comment(final BsonValue comment) {
324+
this.comment = comment;
325+
return this;
140326
}
141327

142328
protected abstract FieldNameValidator getFieldNameValidator();
329+
330+
protected abstract void specializeCommand(BsonDocument initialCommand, ConnectionDescription connectionDescription);
331+
332+
private CommandCreator getCommandCreator(final SessionContext sessionContext) {
333+
return (serverDescription, connectionDescription) -> {
334+
validateCollation(connectionDescription, getCollation());
335+
BsonDocument commandDocument = new BsonDocument("findAndModify", new BsonString(getNamespace().getCollectionName()));
336+
putIfNotNull(commandDocument, "query", getFilter());
337+
putIfNotNull(commandDocument, "fields", getProjection());
338+
putIfNotNull(commandDocument, "sort", getSort());
339+
340+
specializeCommand(commandDocument, connectionDescription);
341+
342+
putIfNotZero(commandDocument, "maxTimeMS", getMaxTime(MILLISECONDS));
343+
if (getWriteConcern().isAcknowledged() && !getWriteConcern().isServerDefault()
344+
&& serverIsAtLeastVersionThreeDotTwo(connectionDescription) && !sessionContext.hasActiveTransaction()) {
345+
commandDocument.put("writeConcern", getWriteConcern().asDocument());
346+
}
347+
if (getCollation() != null) {
348+
commandDocument.put("collation", getCollation().asDocument());
349+
}
350+
if (getHint() != null || getHintString() != null) {
351+
validateHintForFindAndModify(connectionDescription, getWriteConcern());
352+
if (getHint() != null) {
353+
commandDocument.put("hint", getHint().toBsonDocument(BsonDocument.class, null));
354+
} else {
355+
commandDocument.put("hint", new BsonString(getHintString()));
356+
}
357+
}
358+
putIfNotNull(commandDocument, "comment", getComment());
359+
360+
if (isRetryableWrite(isRetryWrites(), getWriteConcern(), serverDescription, connectionDescription, sessionContext)) {
361+
commandDocument.put("txnNumber", new BsonInt64(sessionContext.advanceTransactionNumber()));
362+
}
363+
return commandDocument;
364+
};
365+
}
366+
367+
private String getDatabaseName() {
368+
return getNamespace().getDatabaseName();
369+
}
143370
}

0 commit comments

Comments
 (0)