From 01eaf33e1d34e1e5c3e32d54fed3d23f5df0db99 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 22 Jan 2025 20:28:27 -0800 Subject: [PATCH 1/4] Client Bulk Write sort option. - Add sort option to updateOne. - Add sort option to replaceOne. JAVA-5723 --- .../model/bulk/ClientReplaceOneOptions.java | 15 ++ .../model/bulk/ClientUpdateOneOptions.java | 15 ++ .../bulk/ConcreteClientReplaceOneOptions.java | 18 ++ .../bulk/ConcreteClientUpdateOneOptions.java | 23 +++ .../DefaultClusterableServerFactory.java | 1 + .../operation/ClientBulkWriteOperation.java | 14 ++ .../client-bulkWrite-replaceOne-sort.json | 163 +++++++++++++++++ .../crud/client-bulkWrite-updateOne-sort.json | 167 ++++++++++++++++++ .../client/unified/UnifiedCrudHelper.java | 18 +- 9 files changed, 430 insertions(+), 4 deletions(-) create mode 100644 driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-replaceOne-sort.json create mode 100644 driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-updateOne-sort.json diff --git a/driver-core/src/main/com/mongodb/client/model/bulk/ClientReplaceOneOptions.java b/driver-core/src/main/com/mongodb/client/model/bulk/ClientReplaceOneOptions.java index 2142d736f60..4de01a94843 100644 --- a/driver-core/src/main/com/mongodb/client/model/bulk/ClientReplaceOneOptions.java +++ b/driver-core/src/main/com/mongodb/client/model/bulk/ClientReplaceOneOptions.java @@ -74,4 +74,19 @@ static ClientReplaceOneOptions clientReplaceOneOptions() { */ @Override ClientReplaceOneOptions upsert(@Nullable Boolean upsert); + + /** + * Sets the sort criteria to apply to the operation. A null value means no sort criteria is set. + * + *

+ * The sort criteria determines which document the operation replaces if the query matches multiple documents. + * The first document matched by the specified sort criteria will be replaced. + * + * @param sort The sort criteria. {@code null} represents the server default. + * @return this + * @mongodb.driver.manual reference/method/db.collection.replaceOne/ Sort + * @mongodb.server.release 8.0 + * @since 5.4 + */ + ClientReplaceOneOptions sort(@Nullable Bson sort); } diff --git a/driver-core/src/main/com/mongodb/client/model/bulk/ClientUpdateOneOptions.java b/driver-core/src/main/com/mongodb/client/model/bulk/ClientUpdateOneOptions.java index 9b04ec6ef15..c5abea43b2a 100644 --- a/driver-core/src/main/com/mongodb/client/model/bulk/ClientUpdateOneOptions.java +++ b/driver-core/src/main/com/mongodb/client/model/bulk/ClientUpdateOneOptions.java @@ -85,4 +85,19 @@ static ClientUpdateOneOptions clientUpdateOneOptions() { */ @Override ClientUpdateOneOptions upsert(@Nullable Boolean upsert); + + /** + * Sets the sort criteria to apply to the operation. A null value means no sort criteria is set. + * + *

+ * The sort criteria determines which document the operation updates if the query matches multiple documents. + * The first document matched by the specified sort criteria will be updated. + * + * @param sort The sort criteria. {@code null} represents the server default. + * @return this + * @mongodb.driver.manual reference/method/db.collection.updateOne/ Sort + * @mongodb.server.release 8.0 + * @since 5.4 + */ + ClientUpdateOneOptions sort(@Nullable Bson sort); } diff --git a/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientReplaceOneOptions.java b/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientReplaceOneOptions.java index 18e9d060763..f7172488bfc 100644 --- a/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientReplaceOneOptions.java +++ b/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientReplaceOneOptions.java @@ -38,6 +38,8 @@ public final class ConcreteClientReplaceOneOptions implements ClientReplaceOneOp private String hintString; @Nullable private Boolean upsert; + @Nullable + private Bson sort; public ConcreteClientReplaceOneOptions() { } @@ -89,6 +91,21 @@ public ClientReplaceOneOptions upsert(@Nullable final Boolean upsert) { return this; } + /** + * @see ClientReplaceOneOptions#sort(Bson) + */ + public ClientReplaceOneOptions sort(final Bson sort) { + this.sort = sort; + return this; + } + + /** + * @see ClientReplaceOneOptions#sort(Bson) + */ + public Optional getSort() { + return ofNullable(sort); + } + /** * @see #upsert(Boolean) */ @@ -103,6 +120,7 @@ public String toString() { + ", hint=" + hint + ", hintString='" + hintString + '\'' + ", upsert=" + upsert + + ", sort=" + sort + '}'; } } diff --git a/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientUpdateOneOptions.java b/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientUpdateOneOptions.java index fdf960ed1df..3bd5f1451d7 100644 --- a/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientUpdateOneOptions.java +++ b/driver-core/src/main/com/mongodb/internal/client/model/bulk/ConcreteClientUpdateOneOptions.java @@ -20,12 +20,19 @@ import com.mongodb.lang.Nullable; import org.bson.conversions.Bson; +import java.util.Optional; + +import static java.util.Optional.ofNullable; + /** * This class is not part of the public API and may be removed or changed at any time. */ public final class ConcreteClientUpdateOneOptions extends AbstractClientUpdateOptions implements ClientUpdateOneOptions { static final ConcreteClientUpdateOneOptions MUTABLE_EMPTY = new ConcreteClientUpdateOneOptions(); + @Nullable + private Bson sort; + public ConcreteClientUpdateOneOptions() { } @@ -54,6 +61,21 @@ public ConcreteClientUpdateOneOptions upsert(@Nullable final Boolean upsert) { return (ConcreteClientUpdateOneOptions) super.upsert(upsert); } + /** + * @see ClientUpdateOneOptions#sort(Bson) + */ + public ConcreteClientUpdateOneOptions sort(final Bson sort) { + this.sort = sort; + return this; + } + + /** + * @see ClientUpdateOneOptions#sort(Bson) + */ + public Optional getSort() { + return ofNullable(sort); + } + @Override public String toString() { return "ClientUpdateOneOptions{" @@ -62,6 +84,7 @@ public String toString() { + ", hint=" + getHint().orElse(null) + ", hintString=" + getHintString().map(s -> '\'' + s + '\'') .orElse(null) + ", upsert=" + isUpsert().orElse(null) + + ", sort=" + getSort().orElse(null) + '}'; } } diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java index 880e1db8521..aa8973ec092 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java +++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java @@ -93,6 +93,7 @@ public ClusterableServer create(final Cluster cluster, final ServerAddress serve new InternalStreamConnectionFactory(clusterMode, true, heartbeatStreamFactory, null, applicationName, mongoDriverInformation, emptyList(), loggerSettings, null, serverApi), clusterMode, serverApi, isFunctionAsAServiceEnvironment, sdamProvider, heartbeatOperationContextFactory); + ConnectionPool connectionPool = new DefaultConnectionPool(serverId, new InternalStreamConnectionFactory(clusterMode, streamFactory, credential, applicationName, mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi), diff --git a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java index ccd7f272e95..6ce6dd9a347 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java @@ -75,6 +75,7 @@ import com.mongodb.internal.client.model.bulk.ConcreteClientReplaceOneOptions; import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateManyModel; import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateOneModel; +import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateOneOptions; import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateResult; import com.mongodb.internal.client.model.bulk.UnacknowledgedClientBulkWriteResult; import com.mongodb.internal.connection.AsyncConnection; @@ -1247,6 +1248,15 @@ private void encodeWriteModelInternals( }); } + private void encodeWriteModelInternals(final BsonWriter writer, final ConcreteClientUpdateOneModel model) { + encodeWriteModelInternals(writer, (AbstractClientUpdateModel) model); + ConcreteClientUpdateOneOptions options = model.getOptions(); + options.getSort().ifPresent(value -> { + writer.writeName("sort"); + encodeUsingRegistry(writer, value); + }); + } + private void encodeWriteModelInternals(final BsonWriter writer, final AbstractClientUpdateModel model) { writer.writeName("filter"); encodeUsingRegistry(writer, model.getFilter()); @@ -1294,6 +1304,10 @@ private void encodeWriteModelInternals(final BsonBinaryWriter writer, final Conc }); options.getHintString().ifPresent(value -> writer.writeString("hint", value)); options.isUpsert().ifPresent(value -> writer.writeBoolean("upsert", value)); + options.getSort().ifPresent(value -> { + writer.writeName("sort"); + encodeUsingRegistry(writer, value); + }); } private void encodeWriteModelInternals(final BsonWriter writer, final AbstractClientDeleteModel model) { diff --git a/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-replaceOne-sort.json b/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-replaceOne-sort.json new file mode 100644 index 00000000000..b86bc5f9429 --- /dev/null +++ b/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-replaceOne-sort.json @@ -0,0 +1,163 @@ +{ + "description": "client bulkWrite updateOne-sort", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite replaceOne with sort option", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "replaceOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ops": [ + { + "update": 0, + "filter": { + "_id": { + "$gt": 1 + } + }, + "updateMods": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "nErrors": 0, + "nMatched": 1, + "nModified": 1 + }, + "commandName": "bulkWrite" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + } + ] +} diff --git a/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-updateOne-sort.json b/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-updateOne-sort.json new file mode 100644 index 00000000000..ef75dcb3741 --- /dev/null +++ b/driver-core/src/test/resources/unified-test-format/crud/client-bulkWrite-updateOne-sort.json @@ -0,0 +1,167 @@ +{ + "description": "client bulkWrite updateOne-sort", + "schemaVersion": "1.4", + "runOnRequirements": [ + { + "minServerVersion": "8.0", + "serverless": "forbid" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "_yamlAnchors": { + "namespace": "crud-tests.coll0" + }, + "tests": [ + { + "description": "client bulkWrite updateOne with sort option", + "operations": [ + { + "object": "client0", + "name": "clientBulkWrite", + "arguments": { + "models": [ + { + "updateOne": { + "namespace": "crud-tests.coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": { + "$inc": { + "x": 1 + } + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "commandName": "bulkWrite", + "databaseName": "admin", + "command": { + "bulkWrite": 1, + "ops": [ + { + "update": 0, + "filter": { + "_id": { + "$gt": 1 + } + }, + "updateMods": { + "$inc": { + "x": 1 + } + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ], + "nsInfo": [ + { + "ns": "crud-tests.coll0" + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "nErrors": 0, + "nMatched": 1, + "nModified": 1 + }, + "commandName": "bulkWrite" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + } + ] +} diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java index 5c925d97272..ff41124fa25 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java @@ -134,6 +134,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.singleton; import static java.util.Objects.requireNonNull; +import static java.util.Optional.ofNullable; import static java.util.stream.Collectors.toList; @SuppressWarnings("deprecation") @@ -1607,7 +1608,7 @@ private static SearchIndexModel toIndexSearchModel(final BsonValue bsonValue) { BsonDocument model = bsonValue.asDocument(); BsonDocument definition = model.getDocument("definition"); SearchIndexType type = model.containsKey("type") ? getSearchIndexType(model.getString("type")) : null; - String name = Optional.ofNullable(model.getString("name", null)) + String name = ofNullable(model.getString("name", null)) .map(BsonString::getValue). orElse(null); return new SearchIndexModel(name, definition, type); @@ -1616,7 +1617,7 @@ private static SearchIndexModel toIndexSearchModel(final BsonValue bsonValue) { OperationResult executeListSearchIndexes(final BsonDocument operation) { MongoCollection collection = getMongoCollection(operation); - Optional arguments = Optional.ofNullable(operation.getOrDefault("arguments", null)).map(BsonValue::asDocument); + Optional arguments = ofNullable(operation.getOrDefault("arguments", null)).map(BsonValue::asDocument); if (arguments.isPresent()) { ListSearchIndexesIterable iterable = createListSearchIndexesIterable(collection, arguments.get()); @@ -1634,7 +1635,7 @@ OperationResult executeListSearchIndexes(final BsonDocument operation) { private ListSearchIndexesIterable createListSearchIndexesIterable(final MongoCollection collection, final BsonDocument arguments) { - Optional name = Optional.ofNullable(arguments.getOrDefault("name", null)) + Optional name = ofNullable(arguments.getOrDefault("name", null)) .map(BsonValue::asString).map(BsonString::getValue); ListSearchIndexesIterable iterable = collection.listSearchIndexes(BsonDocument.class); @@ -1930,6 +1931,9 @@ private static ClientReplaceOneOptions getClientReplaceOneOptions(final BsonDocu case "upsert": options.upsert(argument.asBoolean().getValue()); break; + case "sort": + options.sort(argument.asDocument()); + break; default: throw new UnsupportedOperationException(format("Unsupported argument: key=%s, argument=%s", key, argument)); } @@ -1938,7 +1942,13 @@ private static ClientReplaceOneOptions getClientReplaceOneOptions(final BsonDocu } private static ClientUpdateOneOptions getClientUpdateOneOptions(final BsonDocument arguments) { - return fillAbstractClientUpdateOptions(new ConcreteClientUpdateOneOptions(), arguments); + ConcreteClientUpdateOneOptions options = new ConcreteClientUpdateOneOptions(); + + ofNullable(arguments.remove("sort")) + .map(BsonValue::asDocument) + .ifPresent(options::sort); + + return fillAbstractClientUpdateOptions(options, arguments); } private static ClientUpdateManyOptions getClientUpdateManyOptions(final BsonDocument arguments) { From 6573562c161c63d81d423e7c59711887a6123dcb Mon Sep 17 00:00:00 2001 From: Viacheslav Babanin Date: Mon, 27 Jan 2025 17:25:38 -0800 Subject: [PATCH 2/4] Update driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java Co-authored-by: Maxim Katcharov --- .../mongodb/internal/operation/ClientBulkWriteOperation.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java index 6ce6dd9a347..6d0a66baef3 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java @@ -1250,8 +1250,7 @@ private void encodeWriteModelInternals( private void encodeWriteModelInternals(final BsonWriter writer, final ConcreteClientUpdateOneModel model) { encodeWriteModelInternals(writer, (AbstractClientUpdateModel) model); - ConcreteClientUpdateOneOptions options = model.getOptions(); - options.getSort().ifPresent(value -> { + model.getOptions().getSort().ifPresent(value -> { writer.writeName("sort"); encodeUsingRegistry(writer, value); }); From 0e204ab7f5b2ad7b349d27db4bcd2930b122927d Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Mon, 27 Jan 2025 18:35:12 -0800 Subject: [PATCH 3/4] Remove Optional. JAVA-5723 --- .../com/mongodb/client/unified/UnifiedCrudHelper.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java index ff41124fa25..735c35dc9ed 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedCrudHelper.java @@ -1944,9 +1944,12 @@ private static ClientReplaceOneOptions getClientReplaceOneOptions(final BsonDocu private static ClientUpdateOneOptions getClientUpdateOneOptions(final BsonDocument arguments) { ConcreteClientUpdateOneOptions options = new ConcreteClientUpdateOneOptions(); - ofNullable(arguments.remove("sort")) - .map(BsonValue::asDocument) - .ifPresent(options::sort); + if (arguments.containsKey("sort")) { + BsonDocument sort = arguments + .remove("sort") + .asDocument(); + options.sort(sort); + } return fillAbstractClientUpdateOptions(options, arguments); } From b60ea84981427047fb70d228fa06297289195fba Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 29 Jan 2025 23:17:24 -0800 Subject: [PATCH 4/4] Fix static checks. JAVA-5723 --- .../com/mongodb/internal/operation/ClientBulkWriteOperation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java index 6d0a66baef3..f6ff7632c8f 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/ClientBulkWriteOperation.java @@ -75,7 +75,6 @@ import com.mongodb.internal.client.model.bulk.ConcreteClientReplaceOneOptions; import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateManyModel; import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateOneModel; -import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateOneOptions; import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateResult; import com.mongodb.internal.client.model.bulk.UnacknowledgedClientBulkWriteResult; import com.mongodb.internal.connection.AsyncConnection;