Skip to content

Commit a1ed85c

Browse files
authored
Allow updateOne and replaceOne to supply sort option (#1585)
Allow updateOne and replaceOne to supply sort option Adding sort option for updateOne and replaceOne commands, so if it matches more than one candidate document, the first one matched by sort order will be updated. JAVA-5722
1 parent bdd4af2 commit a1ed85c

File tree

10 files changed

+134
-22
lines changed

10 files changed

+134
-22
lines changed

driver-core/src/main/com/mongodb/client/model/ReplaceOptions.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class ReplaceOptions {
3737
private String hintString;
3838
private BsonValue comment;
3939
private Bson variables;
40+
private Bson sort;
4041

4142
/**
4243
* Returns true if a new document should be inserted if there are no matches to the query filter. The default is false.
@@ -221,6 +222,43 @@ public ReplaceOptions let(final Bson variables) {
221222
return this;
222223
}
223224

225+
/**
226+
* Gets the sort criteria to apply to the operation.
227+
*
228+
* <p>
229+
* The sort criteria determines which document the operation replaces if the query matches multiple documents.
230+
* The first document matched by the sort criteria will be replaced.
231+
* The default is null, which means no specific sort criteria is applied.
232+
*
233+
* @return a document describing the sort criteria, or null if no sort is specified.
234+
* @mongodb.driver.manual reference/method/db.collection.replaceOne/ Sort
235+
* @mongodb.server.release 8.0
236+
* @since 5.3
237+
* @see #sort(Bson)
238+
*/
239+
@Nullable
240+
public Bson getSort() {
241+
return sort;
242+
}
243+
244+
/**
245+
* Sets the sort criteria to apply to the operation. A null value means no sort criteria is set.
246+
*
247+
* <p>
248+
* The sort criteria determines which document the operation replaces if the query matches multiple documents.
249+
* The first document matched by the specified sort criteria will be replaced.
250+
*
251+
* @param sort the sort criteria, which may be null.
252+
* @return this
253+
* @mongodb.driver.manual reference/method/db.collection.replaceOne/ Sort
254+
* @mongodb.server.release 8.0
255+
* @since 5.3
256+
*/
257+
public ReplaceOptions sort(@Nullable final Bson sort) {
258+
this.sort = sort;
259+
return this;
260+
}
261+
224262
@Override
225263
public String toString() {
226264
return "ReplaceOptions{"
@@ -231,6 +269,7 @@ public String toString() {
231269
+ ", hintString=" + hintString
232270
+ ", comment=" + comment
233271
+ ", let=" + variables
272+
+ ", sort=" + sort
234273
+ '}';
235274
}
236275
}

driver-core/src/main/com/mongodb/client/model/UpdateOptions.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class UpdateOptions {
4040
private String hintString;
4141
private BsonValue comment;
4242
private Bson variables;
43+
private Bson sort;
4344

4445
/**
4546
* Returns true if a new document should be inserted if there are no matches to the query filter. The default is false.
@@ -256,6 +257,43 @@ public UpdateOptions let(final Bson variables) {
256257
return this;
257258
}
258259

260+
/**
261+
* Gets the sort criteria to apply to the operation.
262+
*
263+
* <p>
264+
* The sort criteria determines which document the operation updates if the query matches multiple documents.
265+
* The first document matched by the sort criteria will be updated.
266+
* The default is null, which means no specific sort criteria is applied.
267+
*
268+
* @return a document describing the sort criteria, or null if no sort is specified.
269+
* @mongodb.driver.manual reference/method/db.collection.updateOne/ Sort
270+
* @mongodb.server.release 8.0
271+
* @since 5.3
272+
* @see #sort(Bson)
273+
*/
274+
@Nullable
275+
public Bson getSort() {
276+
return sort;
277+
}
278+
279+
/**
280+
* Sets the sort criteria to apply to the operation. A null value means no sort criteria is set.
281+
*
282+
* <p>
283+
* The sort criteria determines which document the operation updates if the query matches multiple documents.
284+
* The first document matched by the specified sort criteria will be updated.
285+
*
286+
* @param sort the sort criteria, which may be null.
287+
* @return this
288+
* @mongodb.driver.manual reference/method/db.collection.updateOne/ Sort
289+
* @mongodb.server.release 8.0
290+
* @since 5.3
291+
*/
292+
public UpdateOptions sort(@Nullable final Bson sort) {
293+
this.sort = sort;
294+
return this;
295+
}
296+
259297
@Override
260298
public String toString() {
261299
return "UpdateOptions{"
@@ -267,6 +305,7 @@ public String toString() {
267305
+ ", hintString=" + hintString
268306
+ ", comment=" + comment
269307
+ ", let=" + variables
308+
+ ", sort=" + sort
270309
+ '}';
271310
}
272311
}

driver-core/src/main/com/mongodb/internal/bulk/UpdateRequest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public final class UpdateRequest extends WriteRequest {
4040
private List<BsonDocument> arrayFilters;
4141
@Nullable private BsonDocument hint;
4242
@Nullable private String hintString;
43+
@Nullable private BsonDocument sort;
4344

4445
public UpdateRequest(final BsonDocument filter, @Nullable final BsonValue update, final Type updateType) {
4546
if (updateType != Type.UPDATE && updateType != Type.REPLACE) {
@@ -128,5 +129,15 @@ public UpdateRequest hintString(@Nullable final String hint) {
128129
this.hintString = hint;
129130
return this;
130131
}
132+
133+
@Nullable
134+
public BsonDocument getSort() {
135+
return sort;
136+
}
137+
138+
public UpdateRequest sort(@Nullable final BsonDocument sort) {
139+
this.sort = sort;
140+
return this;
141+
}
131142
}
132143

driver-core/src/main/com/mongodb/internal/connection/SplittablePayload.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,11 @@ public void encode(final BsonWriter writer, final WriteRequestWithIndex writeReq
270270
} else if (update.getHintString() != null) {
271271
writer.writeString("hint", update.getHintString());
272272
}
273+
if (update.getSort() != null) {
274+
writer.writeName("sort");
275+
getCodec(assertNotNull(update.getSort())).encode(writer, assertNotNull(update.getSort()),
276+
EncoderContext.builder().build());
277+
}
273278
writer.writeEndDocument();
274279
} else {
275280
DeleteRequest deleteRequest = (DeleteRequest) writeRequestWithIndex.getWriteRequest();

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,8 @@ MixedBulkWriteOperation bulkWrite(final List<? extends WriteModel<? extends TDoc
464464
.upsert(replaceOneModel.getReplaceOptions().isUpsert())
465465
.collation(replaceOneModel.getReplaceOptions().getCollation())
466466
.hint(toBsonDocument(replaceOneModel.getReplaceOptions().getHint()))
467-
.hintString(replaceOneModel.getReplaceOptions().getHintString());
467+
.hintString(replaceOneModel.getReplaceOptions().getHintString())
468+
.sort(toBsonDocument(replaceOneModel.getReplaceOptions().getSort()));
468469
} else if (writeModel instanceof UpdateOneModel) {
469470
UpdateOneModel<TDocument> updateOneModel = (UpdateOneModel<TDocument>) writeModel;
470471
BsonValue update = updateOneModel.getUpdate() != null ? toBsonDocument(updateOneModel.getUpdate())
@@ -475,7 +476,8 @@ MixedBulkWriteOperation bulkWrite(final List<? extends WriteModel<? extends TDoc
475476
.collation(updateOneModel.getOptions().getCollation())
476477
.arrayFilters(toBsonDocumentList(updateOneModel.getOptions().getArrayFilters()))
477478
.hint(toBsonDocument(updateOneModel.getOptions().getHint()))
478-
.hintString(updateOneModel.getOptions().getHintString());
479+
.hintString(updateOneModel.getOptions().getHintString())
480+
.sort(toBsonDocument(updateOneModel.getOptions().getSort()));
479481
} else if (writeModel instanceof UpdateManyModel) {
480482
UpdateManyModel<TDocument> updateManyModel = (UpdateManyModel<TDocument>) writeModel;
481483
BsonValue update = updateManyModel.getUpdate() != null ? toBsonDocument(updateManyModel.getUpdate())

driver-core/src/test/unit/com/mongodb/client/model/UpdateOptionsSpecification.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,12 @@ class UpdateOptionsSpecification extends Specification {
7878
where:
7979
hint << [null, '_id_']
8080
}
81+
82+
def 'should set sort'() {
83+
expect:
84+
new UpdateOptions().sort(sort).getSort() == sort
85+
86+
where:
87+
sort << [null, new BsonDocument('_id', new BsonInt32(1))]
88+
}
8189
}

driver-core/src/test/unit/com/mongodb/internal/operation/UpdateRequestSpecification.groovy

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,13 @@ class UpdateRequestSpecification extends Specification {
125125
where:
126126
arrayFilters << [null, [], [new BsonDocument('a.b', new BsonInt32(42))]]
127127
}
128+
129+
def 'should set sort property'() {
130+
expect:
131+
new UpdateRequest(new BsonDocument(), new BsonDocument(), type).sort(sort).getSort() == sort
132+
133+
where:
134+
type << [WriteRequest.Type.UPDATE, WriteRequest.Type.REPLACE]
135+
sort << [null, new BsonDocument('_id', new BsonInt32(1))]
136+
}
128137
}

driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,9 @@ private UpdateOptions getUpdateOptions(final BsonDocument arguments) {
11591159
case "collation":
11601160
options.collation(asCollation(cur.getValue().asDocument()));
11611161
break;
1162+
case "sort":
1163+
options.sort(cur.getValue().asDocument());
1164+
break;
11621165
default:
11631166
throw new UnsupportedOperationException("Unsupported argument: " + cur.getKey());
11641167
}
@@ -1193,6 +1196,9 @@ private ReplaceOptions getReplaceOptions(final BsonDocument arguments) {
11931196
case "collation":
11941197
options.collation(asCollation(cur.getValue().asDocument()));
11951198
break;
1199+
case "sort":
1200+
options.sort(cur.getValue().asDocument());
1201+
break;
11961202
default:
11971203
throw new UnsupportedOperationException("Unsupported argument: " + cur.getKey());
11981204
}

driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,6 @@ public static void doSkips(final TestDef def) {
135135
.test("crud", "findOneAndUpdate-hint-unacknowledged", "Unacknowledged findOneAndUpdate with hint document on 4.4+ server")
136136
.test("crud", "findOneAndDelete-hint-unacknowledged", "Unacknowledged findOneAndDelete with hint string on 4.4+ server")
137137
.test("crud", "findOneAndDelete-hint-unacknowledged", "Unacknowledged findOneAndDelete with hint document on 4.4+ server");
138-
def.skipJira("https://jira.mongodb.org/browse/JAVA-5622")
139-
.test("crud", "updateOne-sort", "UpdateOne with sort option")
140-
.test("crud", "updateOne-sort", "updateOne with sort option unsupported (server-side error)")
141-
.test("crud", "replaceOne-sort", "ReplaceOne with sort option")
142-
.test("crud", "replaceOne-sort", "replaceOne with sort option unsupported (server-side error)")
143-
.test("crud", "BulkWrite updateOne-sort", "BulkWrite updateOne with sort option")
144-
.test("crud", "BulkWrite updateOne-sort", "BulkWrite updateOne with sort option unsupported (server-side error)")
145-
.test("crud", "BulkWrite replaceOne-sort", "BulkWrite replaceOne with sort option")
146-
.test("crud", "BulkWrite replaceOne-sort", "BulkWrite replaceOne with sort option unsupported (server-side error)");
147138

148139
// gridfs
149140

driver-sync/src/test/unit/com/mongodb/client/internal/MongoCollectionSpecification.groovy

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -813,15 +813,15 @@ class MongoCollectionSpecification extends Specification {
813813
def expectedOperation = { boolean upsert, WriteConcern wc, Boolean bypassValidation, Collation collation ->
814814
new MixedBulkWriteOperation(namespace,
815815
[new UpdateRequest(new BsonDocument('a', new BsonInt32(1)), new BsonDocument('a', new BsonInt32(10)), REPLACE)
816-
.collation(collation).upsert(upsert).hint(hint).hintString(hintString)], true, wc, retryWrites)
816+
.collation(collation).upsert(upsert).hint(hint).hintString(hintString).sort(sort)], true, wc, retryWrites)
817817
.bypassDocumentValidation(bypassValidation)
818818
}
819819
def replaceOneMethod = collection.&replaceOne
820820

821821
when:
822822
def result = execute(replaceOneMethod, session, new Document('a', 1), new Document('a', 10),
823823
new ReplaceOptions().upsert(true).bypassDocumentValidation(bypassDocumentValidation).collation(collation)
824-
.hint(hint).hintString(hintString))
824+
.hint(hint).hintString(hintString).sort(sort))
825825
executor.getClientSession() == session
826826
def operation = executor.getWriteOperation() as MixedBulkWriteOperation
827827

@@ -830,15 +830,16 @@ class MongoCollectionSpecification extends Specification {
830830
result == expectedResult
831831

832832
where:
833-
[bypassDocumentValidation, modifiedCount, upsertedId, writeConcern, session, retryWrites, hint, hintString] << [
833+
[bypassDocumentValidation, modifiedCount, upsertedId, writeConcern, session, retryWrites, hint, hintString, sort] << [
834834
[null, true, false],
835835
[1],
836836
[null, new BsonInt32(42)],
837837
[ACKNOWLEDGED, UNACKNOWLEDGED],
838838
[null, Stub(ClientSession)],
839839
[true, false],
840840
[null, new BsonDocument('_id', new BsonInt32(1))],
841-
[null, '_id_']
841+
[null, '_id_'],
842+
[null, new BsonDocument('_id', new BsonInt32(1))]
842843
].combinations()
843844
}
844845

@@ -880,11 +881,11 @@ class MongoCollectionSpecification extends Specification {
880881
def collection = new MongoCollectionImpl(namespace, Document, codecRegistry, readPreference, writeConcern,
881882
retryWrites, true, readConcern, JAVA_LEGACY, null, TIMEOUT_SETTINGS, executor)
882883
def expectedOperation = { boolean upsert, WriteConcern wc, Boolean bypassDocumentValidation, Collation collation,
883-
List<Bson> filters, BsonDocument hintDoc, String hintStr ->
884+
List<Bson> filters, BsonDocument hintDoc, String hintStr, BsonDocument sortDoc ->
884885
new MixedBulkWriteOperation(namespace,
885886
[new UpdateRequest(new BsonDocument('a', new BsonInt32(1)), new BsonDocument('a', new BsonInt32(10)), UPDATE)
886887
.multi(false).upsert(upsert).collation(collation).arrayFilters(filters)
887-
.hint(hintDoc).hintString(hintStr)], true, wc, retryWrites)
888+
.hint(hintDoc).hintString(hintStr).sort(sortDoc)], true, wc, retryWrites)
888889
.bypassDocumentValidation(bypassDocumentValidation)
889890
}
890891
def updateOneMethod = collection.&updateOne
@@ -894,29 +895,30 @@ class MongoCollectionSpecification extends Specification {
894895
def operation = executor.getWriteOperation() as MixedBulkWriteOperation
895896

896897
then:
897-
expect operation, isTheSameAs(expectedOperation(false, writeConcern, null, null, null, null, null))
898+
expect operation, isTheSameAs(expectedOperation(false, writeConcern, null, null, null, null, null, null))
898899
executor.getClientSession() == session
899900
result == expectedResult
900901

901902
when:
902903
result = execute(updateOneMethod, session, new Document('a', 1), new Document('a', 10),
903904
new UpdateOptions().upsert(true).bypassDocumentValidation(true).collation(collation)
904-
.arrayFilters(arrayFilters).hint(hint).hintString(hintString))
905+
.arrayFilters(arrayFilters).hint(hint).hintString(hintString).sort(sort))
905906
operation = executor.getWriteOperation() as MixedBulkWriteOperation
906907

907908
then:
908-
expect operation, isTheSameAs(expectedOperation(true, writeConcern, true, collation, arrayFilters, hint, hintString))
909+
expect operation, isTheSameAs(expectedOperation(true, writeConcern, true, collation, arrayFilters, hint, hintString, sort))
909910
executor.getClientSession() == session
910911
result == expectedResult
911912

912913
where:
913-
[writeConcern, arrayFilters, session, retryWrites, hint, hintString] << [
914+
[writeConcern, arrayFilters, session, retryWrites, hint, hintString, sort] << [
914915
[ACKNOWLEDGED, UNACKNOWLEDGED],
915916
[null, [], [new BsonDocument('a.b', new BsonInt32(42))]],
916917
[null, Stub(ClientSession)],
917918
[true, false],
918919
[null, new BsonDocument('_id', new BsonInt32(1))],
919-
[null, '_id_']
920+
[null, '_id_'],
921+
[null, new BsonDocument('_id', new BsonInt32(1))]
920922
].combinations()
921923
}
922924

0 commit comments

Comments
 (0)