From 9283b2e6f333525bfe3eeb192a2c18716be4a21c Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:16:01 -0400 Subject: [PATCH 01/14] DOCSP-41829: remove getting started buttons (#26) --- source/get-started.txt | 3 --- source/get-started/connect-to-mongodb.txt | 3 --- source/get-started/create-a-connection-string.txt | 3 --- source/get-started/create-a-deployment.txt | 2 -- source/get-started/download-and-install.txt | 2 -- 5 files changed, 13 deletions(-) diff --git a/source/get-started.txt b/source/get-started.txt index 16cd76a..ed15a44 100644 --- a/source/get-started.txt +++ b/source/get-started.txt @@ -43,6 +43,3 @@ MongoDB Atlas and interact with data. Follow this guide to connect a sample {+language+} application to a MongoDB Atlas deployment. If you prefer to connect to MongoDB using a different driver or programming language, see the :driver:`list of official MongoDB drivers <>`. - -.. button:: Next: Download and Install - :uri: /get-started/download-and-install/ diff --git a/source/get-started/connect-to-mongodb.txt b/source/get-started/connect-to-mongodb.txt index 984694a..10c3f61 100644 --- a/source/get-started/connect-to-mongodb.txt +++ b/source/get-started/connect-to-mongodb.txt @@ -78,9 +78,6 @@ the sample data, and prints out the result. .. include:: /includes/get-started/quickstart-troubleshoot.rst -.. button:: Next Steps - :uri: /get-started/next-steps/ - .. TODO add after output .. tip:: Data Classes .. .. To learn more about using data classes to store and retrieve data, diff --git a/source/get-started/create-a-connection-string.txt b/source/get-started/create-a-connection-string.txt index 0218bf5..be13296 100644 --- a/source/get-started/create-a-connection-string.txt +++ b/source/get-started/create-a-connection-string.txt @@ -58,6 +58,3 @@ After completing these steps, you have a connection string that contains your database username and password. .. include:: /includes/get-started/quickstart-troubleshoot.rst - -.. button:: Next: Connect to MongoDB - :uri: /get-started/connect-to-mongodb/ \ No newline at end of file diff --git a/source/get-started/create-a-deployment.txt b/source/get-started/create-a-deployment.txt index 2f67f10..974cdd8 100644 --- a/source/get-started/create-a-deployment.txt +++ b/source/get-started/create-a-deployment.txt @@ -28,5 +28,3 @@ in your database. .. include:: /includes/get-started/quickstart-troubleshoot.rst -.. button:: Next: Create a Connection String - :uri: /get-started/create-a-connection-string/ diff --git a/source/get-started/download-and-install.txt b/source/get-started/download-and-install.txt index 0163f3c..8eb916c 100644 --- a/source/get-started/download-and-install.txt +++ b/source/get-started/download-and-install.txt @@ -82,5 +82,3 @@ and the driver dependencies installed. .. include:: /includes/get-started/quickstart-troubleshoot.rst -.. button:: Next: Create a MongoDB Deployment - :uri: /get-started/create-a-deployment/ From ae8646ce1e142e669d257e4a7ba913ba27bff55e Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Fri, 2 Aug 2024 09:13:52 -0400 Subject: [PATCH 02/14] DOCSP-41138: Distinct (#24) --- source/includes/read/distinct.kt | 61 +++++++++++ source/read.txt | 5 +- source/read/distinct.txt | 182 +++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 source/includes/read/distinct.kt create mode 100644 source/read/distinct.txt diff --git a/source/includes/read/distinct.kt b/source/includes/read/distinct.kt new file mode 100644 index 0000000..92f12d9 --- /dev/null +++ b/source/includes/read/distinct.kt @@ -0,0 +1,61 @@ +import com.mongodb.ConnectionString +import com.mongodb.MongoClientSettings +import com.mongodb.client.model.Filters.and +import com.mongodb.client.model.Filters.eq +import com.mongodb.kotlin.client.MongoClient + +// start-data-class +data class Restaurant( + val name: String, + val borough: String, + val cuisine: String +) +// end-data-class + +fun main() { + val uri = "" + + val settings = MongoClientSettings.builder() + .applyConnectionString(ConnectionString(uri)) + .retryWrites(true) + .build() + + val mongoClient = MongoClient.create(settings) + val database = mongoClient.getDatabase("sample_restaurants") + val collection = database.getCollection("restaurants") + + // start-distinct + val results = collection.distinct(Restaurant::borough.name) + + results.forEach { result -> + println(result) + } + // end-distinct + + // start-distinct-query + val results = collection.distinct( + Restaurant::borough.name, + eq(Restaurant::cuisine.name, "Italian") + ) + + results.forEach { result -> + println(result) + } + // end-distinct-query + + // start-distinct-comment + val results = collection.distinct( + Restaurant::name.name, + and( + eq(Restaurant::borough.name, "Bronx"), + eq(Restaurant::cuisine.name, "Pizza") + ) + ).comment("Bronx pizza restaurants") + + results.forEach { result -> + println(result) + + } + // end-distinct-comment +} + diff --git a/source/read.txt b/source/read.txt index 8433061..4800b25 100644 --- a/source/read.txt +++ b/source/read.txt @@ -27,6 +27,7 @@ Read Data from MongoDB /read/project /read/specify-documents-to-return /read/count + /read/distinct Overview -------- @@ -133,8 +134,8 @@ collection: :copyable: :dedent: -.. TODO: To learn more about the ``distinct()`` method, see the -.. :ref:`kotlin-sync-distinct` guide. +To learn more about the ``distinct()`` method, see the +:ref:`kotlin-sync-distinct` guide. Monitor Data Changes -------------------- diff --git a/source/read/distinct.txt b/source/read/distinct.txt new file mode 100644 index 0000000..0781bd1 --- /dev/null +++ b/source/read/distinct.txt @@ -0,0 +1,182 @@ +.. _kotlin-sync-distinct: + +============================== +Retrieve Distinct Field Values +============================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: read, unique, code example + +Overview +-------- + +Within a collection, different documents might contain different values for a single field. +For example, one document in the ``restaurant`` collection has a ``borough`` value of ``"Manhattan"``, and +another has a ``borough`` value of ``"Queens"``. With the {+driver-short+}, you can +retrieve all the distinct values that a field contains across multiple documents +in a collection. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``restaurants`` collection in the ``sample_restaurants`` +database from the :atlas:`Atlas sample datasets `. To learn how to create a +free MongoDB Atlas cluster and load the sample datasets, see the +:atlas:`Get Started with Atlas ` guide. + +The following {+language+} data class models the documents in this collection: + +.. literalinclude:: /includes/read/distinct.kt + :start-after: start-data-class + :end-before: end-data-class + :language: kotlin + :copyable: + +``distinct()`` Method +--------------------- + +To retrieve the distinct values for a specified field, call the ``distinct()`` +method and pass in the name of the field you want to find distinct values for. + +Retrieve Distinct Values Across a Collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example retrieves the distinct values of the ``borough`` field in +the ``restaurants`` collection: + +.. io-code-block:: + + .. input:: /includes/read/distinct.kt + :start-after: start-distinct + :end-before: end-distinct + :language: kotlin + :dedent: + + .. output:: + :visible: false + + Bronx + Brooklyn + Manhattan + Missing + Queens + Staten Island + +The results show every distinct value that appears in the ``borough`` field +across all documents in the collection. Although several documents have the same +value in the ``borough`` field, each value appears in the results only once. + +Retrieve Distinct Values Across Specified Documents +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can provide a **query filter** to the ``distinct()`` method to find the distinct +field values across a subset of documents in a collection. A query filter is an expression that specifies search +criteria used to match documents in an operation. For more information about +creating a query filter, see :ref:`kotlin-sync-specify-query`. + +The following example retrieves the distinct values of the ``borough`` field for +all documents that have a ``cuisine`` field value of ``"Italian"``: + +.. io-code-block:: + + .. input:: /includes/read/distinct.kt + :start-after: start-distinct-query + :end-before: end-distinct-query + :language: kotlin + :dedent: + + .. output:: + :visible: false + + Bronx + Brooklyn + Manhattan + Queens + Staten Island + +Modify Distinct Behavior +~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``distinct()`` method can be modified by chaining methods to the ``distinct()`` method +call. If you don't specify any options, the driver does not customize the operation. + +The following table describes some methods you can use to customize the +``distinct()`` operation: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Method + - Description + + * - ``batchSize()`` + - | Sets the number of documents to return per batch. + + * - ``collation()`` + - | Specifies the kind of language collation to use when sorting + results. For more information, see :manual:`Collation ` + in the {+mdb-server+} manual. + + * - ``comment()`` + - | Specifies a comment to attach to the operation. + + * - ``filter()`` + - | Sets the query filter to apply to the query. + + * - ``forEach()`` + - | Performs the given action on each element returned by the ``distinct()`` operation. + + * - ``maxTime()`` + - | Sets the maximum amount of time to allow the operation to run, in milliseconds. + +For a complete list of methods you can use to modify the ``distinct()`` method, see +the `DistinctIterable <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-distinct-iterable/index.html>`__ API documentation. + +The following example retrieves the distinct values of the ``name`` field for +all documents that have a ``borough`` field value of ``"Bronx"`` and a +``cuisine`` field value of ``"Pizza"``. It also uses +the ``comment`` option to add a comment to the operation. + +.. io-code-block:: + + .. input:: /includes/read/distinct.kt + :start-after: start-distinct-comment + :end-before: end-distinct-comment + :language: kotlin + :dedent: + + .. output:: + :visible: false + + $1.25 Pizza + 18 East Gunhill Pizza + 2 Bros + Aenos Pizza + Alitalia Pizza Restaurant + ... + +Additional Information +---------------------- + +To learn more about the distinct command, see the :manual:`Distinct guide +` in the MongoDB Server Manual. + +API Documentation +~~~~~~~~~~~~~~~~~ + +To learn more about any of the methods or types discussed in this +guide, see the following API documentation: + +- `distinct() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/distinct.html>`__ +- `DistinctIterable <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-distinct-iterable/index.html>`__ \ No newline at end of file From de2efafee8819c75afd5f1cd4ab05883251b8525 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:09:03 -0400 Subject: [PATCH 03/14] DOCSP-41130: bulk write operations (#27) * DOCSP-41130: bulk write * DOCSP-41130 * add code * add api references * dedent * MM PR fixes 1 + source constant edit --- snooty.toml | 4 +- source/connect/stable-api.txt | 16 +- source/data-formats/time-series.txt | 8 +- source/includes/write/bulk.kt | 81 +++++ source/indexes/atlas-search-index.txt | 10 +- source/indexes/compound-index.txt | 6 +- source/indexes/single-field-index.txt | 6 +- source/read/count.txt | 4 +- source/read/project.txt | 4 +- source/read/retrieve.txt | 6 +- source/read/specify-a-query.txt | 2 +- source/read/specify-documents-to-return.txt | 8 +- source/work-with-indexes.txt | 8 +- source/write-operations.txt | 6 +- source/write/bulk-write.txt | 344 ++++++++++++++++++++ source/write/delete.txt | 4 +- source/write/insert.txt | 4 +- source/write/update.txt | 4 +- 18 files changed, 475 insertions(+), 50 deletions(-) create mode 100644 source/includes/write/bulk.kt create mode 100644 source/write/bulk-write.txt diff --git a/snooty.toml b/snooty.toml index 89425e9..60ae09b 100644 --- a/snooty.toml +++ b/snooty.toml @@ -27,7 +27,7 @@ full-version = "{+version-number+}.2" version = "v{+version-number+}" mdb-server = "MongoDB Server" stable-api = "Stable API" -api = "https://mongodb.github.io/mongo-java-driver/{+version-number+}/apidocs/mongodb-driver-kotlin-sync" +api = "https://mongodb.github.io/mongo-java-driver/{+version-number+}/apidocs/mongodb-driver-kotlin-sync/mongodb-driver-kotlin-sync" java-api = "https://mongodb.github.io/mongo-java-driver/{+version-number+}" -core-api = "{+java-api+}/apidocs/mongodb-driver-core/" +core-api = "{+java-api+}/apidocs/mongodb-driver-core" kotlin-docs = "https://kotlinlang.org" diff --git a/source/connect/stable-api.txt b/source/connect/stable-api.txt index 9c17b6e..f3c28fe 100644 --- a/source/connect/stable-api.txt +++ b/source/connect/stable-api.txt @@ -125,11 +125,11 @@ API Documentation For more information about using the {+stable-api+} with {+driver-short+}, see the following API documentation: -- `ServerApi <{+core-api+}com/mongodb/ServerApi.html>`__ -- `ServerApi.Builder <{+core-api+}com/mongodb/ServerApi.Builder.html>`__ -- `ServerApiVersion <{+core-api+}com/mongodb/ServerApiVersion.html>`__ -- `ServerAddress <{+core-api+}com/mongodb/ServerAddress.html>`__ -- `MongoClientSettings <{+core-api+}com/mongodb/MongoClientSettings.html>`__ -- `MongoClientSettings.Builder <{+core-api+}com/mongodb/MongoClientSettings.Builder.html>`__ -- `MongoClient <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-client/index.html>`__ -- `MongoClient.create() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-client/-factory/create.html>`__ +- `ServerApi <{+core-api+}/com/mongodb/ServerApi.html>`__ +- `ServerApi.Builder <{+core-api+}/com/mongodb/ServerApi.Builder.html>`__ +- `ServerApiVersion <{+core-api+}/com/mongodb/ServerApiVersion.html>`__ +- `ServerAddress <{+core-api+}/com/mongodb/ServerAddress.html>`__ +- `MongoClientSettings <{+core-api+}/com/mongodb/MongoClientSettings.html>`__ +- `MongoClientSettings.Builder <{+core-api+}/com/mongodb/MongoClientSettings.Builder.html>`__ +- `MongoClient <{+api+}/com.mongodb.kotlin.client/-mongo-client/index.html>`__ +- `MongoClient.create() <{+api+}/com.mongodb.kotlin.client/-mongo-client/-factory/create.html>`__ diff --git a/source/data-formats/time-series.txt b/source/data-formats/time-series.txt index 6f7d754..e318527 100644 --- a/source/data-formats/time-series.txt +++ b/source/data-formats/time-series.txt @@ -175,7 +175,7 @@ API Documentation To learn more about the methods mentioned in this guide, see the following API documentation: -- `createCollection() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-database/create-collection.html>`__ -- `listCollections() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-database/list-collections.html>`__ -- `insertOne() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/insert-one.html>`__ -- `insertMany() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/insert-many.html>`__ \ No newline at end of file +- `createCollection() <{+api+}/com.mongodb.kotlin.client/-mongo-database/create-collection.html>`__ +- `listCollections() <{+api+}/com.mongodb.kotlin.client/-mongo-database/list-collections.html>`__ +- `insertOne() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/insert-one.html>`__ +- `insertMany() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/insert-many.html>`__ \ No newline at end of file diff --git a/source/includes/write/bulk.kt b/source/includes/write/bulk.kt new file mode 100644 index 0000000..b2f9930 --- /dev/null +++ b/source/includes/write/bulk.kt @@ -0,0 +1,81 @@ +import com.mongodb.client.model.* +import com.mongodb.client.model.Filters.* +import com.mongodb.kotlin.client.MongoClient + +// start-data-class +data class Restaurant( + val name: String, + val borough: String, + val cuisine: String +) +// end-data-class + +fun main() { + val uri = "" + + val mongoClient = MongoClient.create(uri) + val database = mongoClient.getDatabase("sample_restaurants") + val collection = database.getCollection("restaurants") + + // start-bulk-insert-one + val blueMoon = InsertOneModel(Restaurant("Blue Moon Grill", "Brooklyn", "American")) + // end-bulk-insert-one + + // start-bulk-update-one + val updateOneFilter = Filters.eq(Restaurant::name.name, "White Horse Tavern") + val updateOneDoc = Updates.set(Restaurant::borough.name, "Queens") + val tavernUpdate = UpdateOneModel(updateOneFilter, updateOneDoc) + // end-bulk-update-one + + // start-bulk-update-many + val updateManyFilter = Filters.eq(Restaurant::name.name, "Wendy's") + val updateManyDoc = Updates.set(Restaurant::cuisine.name, "Fast food") + val wendysUpdate = UpdateManyModel(updateManyFilter, updateManyDoc) + // end-bulk-update-many + + // start-bulk-replace-one + val replaceFilter = Filters.eq(Restaurant::name.name, "Cooper Town Diner") + val replaceDoc = Restaurant("Smith Town Diner", "Brooklyn", "American") + val replacement = ReplaceOneModel(replaceFilter, replaceDoc) + // end-bulk-replace-one + + // start-bulk-delete-one + val deleteOne = DeleteOneModel(Filters.eq( + Restaurant::name.name, + "Morris Park Bake Shop" + )) + // end-bulk-delete-one + + // start-bulk-delete-many + val deleteMany = DeleteManyModel(Filters.eq( + Restaurant::cuisine.name, + "Experimental" + )) + // end-bulk-delete-many + + // start-bulk-write-mixed + val insertOneMdl = InsertOneModel(Restaurant("Red's Pizza", "Brooklyn", "Pizzeria")) + val updateOneMdl = UpdateOneModel( + Filters.eq(Restaurant::name.name, "Moonlit Tavern"), + Updates.set(Restaurant::borough.name, "Queens") + ) + val deleteManyMdl = DeleteManyModel( + Filters.eq(Restaurant::name.name, "Crepe") + ) + + val bulkResult = collection.bulkWrite( + listOf(insertOneMdl, updateOneMdl, deleteManyMdl) + ) + + println(bulkResult) + // end-bulk-write-mixed + + val bulkOperations = listOf(insertOneMdl, updateOneMdl, deleteManyMdl) + + // start-bulk-write-unordered + val opts = BulkWriteOptions().ordered(false) + collection.bulkWrite(bulkOperations, opts) + // end-bulk-write-unordered + +} + diff --git a/source/indexes/atlas-search-index.txt b/source/indexes/atlas-search-index.txt index 7d9cc53..e786313 100644 --- a/source/indexes/atlas-search-index.txt +++ b/source/indexes/atlas-search-index.txt @@ -47,8 +47,8 @@ each of the preceding methods. Create a Search Index --------------------- -You can use the `createSearchIndex() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/create-search-index.html>`__ -and the `createSearchIndexes() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/create-search-indexes.html>`__ +You can use the `createSearchIndex() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/create-search-index.html>`__ +and the `createSearchIndexes() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/create-search-indexes.html>`__ methods to create one or more Atlas Search indexes. The following code example shows how to create a single index: @@ -77,7 +77,7 @@ List Search Indexes ------------------- You can use the -`listSearchIndexes() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/list-search-indexes.html>`__ +`listSearchIndexes() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/list-search-indexes.html>`__ method to return all Atlas Search indexes in a collection. The following code example shows how to print a list of the search indexes in @@ -95,7 +95,7 @@ Update a Search Index --------------------- You can use the -`updateSearchIndex() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/update-search-index.html>`__ +`updateSearchIndex() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/update-search-index.html>`__ method to update an Atlas Search index. The following code shows how to update a search index: @@ -112,7 +112,7 @@ Delete a Search Index --------------------- You can use the -`dropSearchIndex() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/drop-search-index.html>`__ +`dropSearchIndex() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/drop-search-index.html>`__ method to delete an Atlas Search index. The following code shows how to delete a search index from a collection: diff --git a/source/indexes/compound-index.txt b/source/indexes/compound-index.txt index cf3942a..845c24f 100644 --- a/source/indexes/compound-index.txt +++ b/source/indexes/compound-index.txt @@ -93,6 +93,6 @@ API Documentation To learn more about any of the methods discussed in this guide, see the following API documentation: -- `find() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ -- `filter() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/filter.html>`__ -- `sort() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/sort.html>`__ \ No newline at end of file +- `find() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ +- `filter() <{+api+}/com.mongodb.kotlin.client/-find-iterable/filter.html>`__ +- `sort() <{+api+}/com.mongodb.kotlin.client/-find-iterable/sort.html>`__ \ No newline at end of file diff --git a/source/indexes/single-field-index.txt b/source/indexes/single-field-index.txt index bcfd719..8eca6e8 100644 --- a/source/indexes/single-field-index.txt +++ b/source/indexes/single-field-index.txt @@ -91,6 +91,6 @@ API Documentation To learn more about any of the methods discussed in this guide, see the following API documentation: -- `find() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ -- `filter() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/filter.html>`__ -- `sort() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/sort.html>`__ \ No newline at end of file +- `find() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ +- `filter() <{+api+}/com.mongodb.kotlin.client/-find-iterable/filter.html>`__ +- `sort() <{+api+}/com.mongodb.kotlin.client/-find-iterable/sort.html>`__ \ No newline at end of file diff --git a/source/read/count.txt b/source/read/count.txt index d0952d4..4f8365b 100644 --- a/source/read/count.txt +++ b/source/read/count.txt @@ -201,5 +201,5 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `countDocuments() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/count-documents.html>`__ -- `estimatedDocumentCount() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/estimated-document-count.html>`__ \ No newline at end of file +- `countDocuments() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/count-documents.html>`__ +- `estimatedDocumentCount() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/estimated-document-count.html>`__ \ No newline at end of file diff --git a/source/read/project.txt b/source/read/project.txt index c77d06c..40f4abf 100644 --- a/source/read/project.txt +++ b/source/read/project.txt @@ -123,5 +123,5 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API Documentation: -- `find() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ -- `projection() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/projection.html>`__ \ No newline at end of file +- `find() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ +- `projection() <{+api+}/com.mongodb.kotlin.client/-find-iterable/projection.html>`__ \ No newline at end of file diff --git a/source/read/retrieve.txt b/source/read/retrieve.txt index 17f4fff..3106b07 100644 --- a/source/read/retrieve.txt +++ b/source/read/retrieve.txt @@ -151,7 +151,7 @@ number of documents returned by the query to ``10`` and set a maximum execution :copyable: :dedent: -For a full list of methods that modify the behavior of ``find()``, see the `API documentation <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/index.html>`__ +For a full list of methods that modify the behavior of ``find()``, see the `API documentation <{+api+}/com.mongodb.kotlin.client/-find-iterable/index.html>`__ for the ``FindIterable`` class. Additional Information @@ -168,5 +168,5 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `find() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ -- `FindIterable <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/index.html>`__ \ No newline at end of file +- `find() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ +- `FindIterable <{+api+}/com.mongodb.kotlin.client/-find-iterable/index.html>`__ \ No newline at end of file diff --git a/source/read/specify-a-query.txt b/source/read/specify-a-query.txt index 7761438..02464f5 100644 --- a/source/read/specify-a-query.txt +++ b/source/read/specify-a-query.txt @@ -263,4 +263,4 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `find() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ +- `find() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ diff --git a/source/read/specify-documents-to-return.txt b/source/read/specify-documents-to-return.txt index 1fd2415..0bed801 100644 --- a/source/read/specify-documents-to-return.txt +++ b/source/read/specify-documents-to-return.txt @@ -192,7 +192,7 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `find() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ -- `limit() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/limit.html>`__ -- `sort() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/sort.html>`__ -- `skip() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-find-iterable/skip.html>`__ +- `find() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/find.html>`__ +- `limit() <{+api+}/com.mongodb.kotlin.client/-find-iterable/limit.html>`__ +- `sort() <{+api+}/com.mongodb.kotlin.client/-find-iterable/sort.html>`__ +- `skip() <{+api+}/com.mongodb.kotlin.client/-find-iterable/skip.html>`__ diff --git a/source/work-with-indexes.txt b/source/work-with-indexes.txt index b0f9da7..435119f 100644 --- a/source/work-with-indexes.txt +++ b/source/work-with-indexes.txt @@ -118,7 +118,7 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `createIndex() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/create-index.html>`__ -- `createIndexes() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/create-indexes.html>`__ -- `dropIndex() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/drop-index.html>`__ -- `dropIndexes() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/drop-indexes.html>`__ +- `createIndex() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/create-index.html>`__ +- `createIndexes() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/create-indexes.html>`__ +- `dropIndex() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/drop-index.html>`__ +- `dropIndexes() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/drop-indexes.html>`__ diff --git a/source/write-operations.txt b/source/write-operations.txt index cabd401..398f3d0 100644 --- a/source/write-operations.txt +++ b/source/write-operations.txt @@ -25,10 +25,10 @@ Write Data to MongoDB /write/insert /write/update /write/delete + /write/bulk-write .. /write/replace - /write/bulk-write /write/gridfs Overview @@ -183,5 +183,5 @@ single bulk operation: :copyable: :dedent: -.. To learn more about the ``bulk_write()`` method, see the -.. :ref:`Bulk Write ` guide. +To learn more about the ``bulkWrite()`` method, see the +:ref:`Bulk Write ` guide. diff --git a/source/write/bulk-write.txt b/source/write/bulk-write.txt new file mode 100644 index 0000000..6995d23 --- /dev/null +++ b/source/write/bulk-write.txt @@ -0,0 +1,344 @@ +.. _kotlin-sync-bulk-write: + +===================== +Bulk Write Operations +===================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: insert, update, replace, code example, multiple changes + +Overview +-------- + +This guide shows you how to use the {+driver-short+} to perform a bulk +write operation that makes multiple changes to your data in a single +database call. + +Consider a situation that requires you to insert documents, update +documents, and delete documents for the same task. If you use +the individual write methods to perform each type of operation, each write +accesses the database separately. You can use a bulk write operation to +optimize the number of calls your application makes to the server. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``sample_restaurants.restaurants`` collection +from the :atlas:`Atlas sample datasets `. To learn how to create a +free MongoDB Atlas cluster and load the sample datasets, see the +:atlas:`Get Started with Atlas ` guide. + +The documents in this collection are modeled by the following {+language+} data class: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-data-class + :end-before: end-data-class + :language: kotlin + :copyable: + :dedent: + +Define the Write Operations +--------------------------- + +For each write operation you want to perform, create a corresponding +instance of one of the following operation classes that inherit from the +generic ``WriteModel`` class: + +- ``InsertOneModel`` +- ``UpdateOneModel`` +- ``UpdateManyModel`` +- ``ReplaceOneModel`` +- ``DeleteOneModel`` +- ``DeleteManyModel`` + +Then, pass a list of these instances to the ``bulkWrite()`` method. + +The following sections show how to create and use instances of the +preceding classes. The :ref:`kotlin-sync-bulkwrite-method` section +demonstrates how to pass a list of models to the ``bulkWrite()`` method +to perform the bulk operation. + +Insert Operations +~~~~~~~~~~~~~~~~~ + +To perform an insert operation, create an ``InsertOneModel`` instance and specify +the document you want to insert. + +The following example creates an instance of ``InsertOneModel``: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-bulk-insert-one + :end-before: end-bulk-insert-one + :language: kotlin + :copyable: + :dedent: + +To insert multiple documents, create an instance of ``InsertOneModel`` +for each document. + +.. important:: + + When performing a bulk operation, the ``InsertOneModel`` cannot + insert a document with an ``_id`` that already exists in the + collection. In this situation, the driver throws a + ``MongoBulkWriteException``. + +Update Operations +~~~~~~~~~~~~~~~~~ + +To update a document, create an instance of ``UpdateOneModel`` and pass +the following arguments: + +- A **query filter** that specifies the criteria used to match documents in your collection +- The update operation you want to perform. For more information about update + operations, see the :manual:`Field Update Operators + ` guide in the {+mdb-server+} manual. + +An ``UpdateOneModel`` instance specifies an update for *the first* +document that matches your query filter. + +The following example creates an instance of ``UpdateOneModel``: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-bulk-update-one + :end-before: end-bulk-update-one + :language: kotlin + :copyable: + :dedent: + +To update multiple documents, create an instance of ``UpdateManyModel`` and pass +the same arguments as for ``UpdateOneModel``. The ``UpdateManyModel`` +class specifies updates for *all* documents that match your query +filter. + +The following example creates an instance of ``UpdateManyModel``: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-bulk-update-many + :end-before: end-bulk-update-many + :language: kotlin + :copyable: + :dedent: + +Replace Operations +~~~~~~~~~~~~~~~~~~ + +A replace operation removes all fields and values of a specified document and +replaces them with new fields and values that you specify. To perform a +replace operation, create an instance of ``ReplaceOneModel`` and pass a +query filter and the fields and values you want to replace the matching +document with. + +The following example creates an instance of ``ReplaceOneModel``: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-bulk-replace-one + :end-before: end-bulk-replace-one + :language: kotlin + :copyable: + :dedent: + +To replace multiple documents, you must create an instance of +``ReplaceOneModel`` for each document. + +Delete Operations +~~~~~~~~~~~~~~~~~ + +To delete a document, create an instance of ``DeleteOneModel`` and pass a +query filter specifying the document you want to delete. A +``DeleteOneModel`` instance provides instructions to delete +only *the first* document that matches your query filter. + +The following example creates an instance of ``DeleteOneModel``: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-bulk-delete-one + :end-before: end-bulk-delete-one + :language: kotlin + :copyable: + :dedent: + +To delete multiple documents, create an instance of ``DeleteManyModel`` and pass a +query filter specifying the document you want to delete. An instance of +``DeleteManyModel`` provides instructions to remove *all* documents that +match your query filter. + +The following example creates an instance of ``DeleteManyModel``: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-bulk-delete-many + :end-before: end-bulk-delete-many + :language: kotlin + :copyable: + :dedent: + +.. _kotlin-sync-bulkwrite-method: + +Perform the Bulk Operation +-------------------------- + +After you define a model instance for each operation you want to perform, +pass a list of these instances to the ``bulkWrite()`` method. +By default, the method runs the operations in the order +specified by the list of models. + +The following example performs multiple write operations by using the +``bulkWrite()`` method: + +.. io-code-block:: + + .. input:: /includes/write/bulk.kt + :start-after: start-bulk-write-mixed + :end-before: end-bulk-write-mixed + :language: kotlin + :dedent: + + .. output:: + + AcknowledgedBulkWriteResult{insertedCount=1, matchedCount=5, removedCount=3, + modifiedCount=2, upserts=[], inserts=[BulkWriteInsert{index=0, + id=BsonObjectId{value=...}}]} + +If any of the write operations fail, {+driver-short+} raises a +``BulkWriteError`` and does not perform any further operations. +``BulkWriteError`` provides a ``details`` item that includes the +operation that failed, and details about the exception. + +.. note:: + + When the driver runs a bulk operation, it uses the write concern of the + target collection. The driver reports all write concern errors after + attempting all operations, regardless of execution order. + +Customize Bulk Write Operation +------------------------------ + +The ``bulkWrite()`` method optionally accepts a parameter which +specifies options you can use to configure the bulk write +operation. If you don't specify any options, the driver performs the +bulk operation with default settings. + +The following table describes the setter methods that you can use to +configure a ``BulkWriteOptions`` instance: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Property + - Description + + * - ``ordered()`` + - | If ``true``, the driver performs the write operations in the order + provided. If an error occurs, the remaining operations are not + attempted. + | + | If ``false``, the driver performs the operations in an + arbitrary order and attempts to perform all operations. + | Defaults to ``true``. + + * - ``bypassDocumentValidation()`` + - | Specifies whether the update operation bypasses document validation. This lets you + update documents that don't meet the schema validation requirements, if any + exist. For more information about schema validation, see :manual:`Schema + Validation ` in the MongoDB + Server manual. + | Defaults to ``false``. + + * - ``comment()`` + - | Sets a comment to attach to the operation. + + * - ``let()`` + - | Provides a map of parameter names and values to set top-level + variables for the operation. Values must be constant or closed + expressions that don't reference document fields. + +The following code creates options and uses the ``ordered(false)`` option to +specify an unordered bulk write. Then, the example uses the +``bulkWrite()`` method to perform a bulk operation: + +.. literalinclude:: /includes/write/bulk.kt + :start-after: start-bulk-write-unordered + :end-before: end-bulk-write-unordered + :language: kotlin + :copyable: + :dedent: + +If any of the write operations in an unordered bulk write fail, the {+driver-short+} +reports the errors only after attempting all operations. + +.. note:: + + Unordered bulk operations do not guarantee an order of execution. The + order can differ from the way you list them to optimize the runtime. + +Return Value +------------ + +The ``bulkWrite()`` method returns a ``BulkWriteResult`` object. You can +access the following information from a ``BulkWriteResult`` instance: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Property + - Description + + * - ``wasAcknowledged()`` + - | Indicates if the server acknowledged the write operation. + + * - ``getDeletedCount()`` + - | The number of documents deleted, if any. + + * - ``getInsertedCount()`` + - | The number of documents inserted, if any. + + * - ``getInserts()`` + - | The list of inserted documents, if any. + + * - ``getMatchedCount()`` + - | The number of documents matched for an update, if applicable. + + * - ``getModifiedCount()`` + - | The number of documents modified, if any. + + * - ``getUpserts()`` + - | The list of upserted documents, if any. + +Additional Information +---------------------- + +To learn how to perform individual write operations, see the following guides: + +- :ref:`kotlin-sync-write-insert` +- :ref:`kotlin-sync-write-update` +- :ref:`kotlin-sync-write-delete` + +.. - :ref:`kotlin-sync-write-replace` + +API Documentation +~~~~~~~~~~~~~~~~~ + +To learn more about any of the methods or types discussed in this +guide, see the following API Documentation: + +- `bulkWrite() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/bulk-write.html>`__ +- `InsertOneModel <{+core-api+}/com/mongodb/client/model/InsertOneModel.html>`__ +- `UpdateOneModel <{+core-api+}/com/mongodb/client/model/UpdateOneModel.html>`__ +- `UpdateManyModel <{+core-api+}/com/mongodb/client/model/UpdateManyModel.html>`__ +- `ReplaceOneModel <{+core-api+}/com/mongodb/client/model/ReplaceOneModel.html>`__ +- `DeleteOneModel <{+core-api+}/com/mongodb/client/model/DeleteOneModel.html>`__ +- `DeleteManyModel <{+core-api+}/com/mongodb/client/model/DeleteManyModel.html>`__ +- `BulkWriteOptions <{+core-api+}/com/mongodb/client/model/BulkWriteOptions.html>`__ +- `BulkWriteResult <{+core-api+}/com/mongodb/bulk/BulkWriteResult.html>`__ diff --git a/source/write/delete.txt b/source/write/delete.txt index e474b02..4225f1f 100644 --- a/source/write/delete.txt +++ b/source/write/delete.txt @@ -190,8 +190,8 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `deleteOne() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/delete-one.html>`__ -- `deleteMany() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/delete-many.html>`__ +- `deleteOne() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/delete-one.html>`__ +- `deleteMany() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/delete-many.html>`__ - `DeleteResult <{+core-api+}/com/mongodb/client/result/DeleteResult.html>`__ .. .. _kotlin-sync-delete-instruqt-lab: diff --git a/source/write/insert.txt b/source/write/insert.txt index 9839df0..d08a9b8 100644 --- a/source/write/insert.txt +++ b/source/write/insert.txt @@ -240,8 +240,8 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `insertOne() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/insert-one.html>`__ -- `insertMany() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/insert-many.html>`__ +- `insertOne() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/insert-one.html>`__ +- `insertMany() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/insert-many.html>`__ - `InsertOneOptions <{+core-api+}/com/mongodb/client/model/InsertOneOptions.html>`__ - `InsertManyOptions <{+core-api+}/com/mongodb/client/model/InsertManyOptions.html>`__ - `InsertOneResult <{+core-api+}/com/mongodb/client/result/InsertOneResult.html>`__ diff --git a/source/write/update.txt b/source/write/update.txt index 8d2c32d..5d53628 100644 --- a/source/write/update.txt +++ b/source/write/update.txt @@ -218,7 +218,7 @@ API Documentation To learn more about any of the methods or types discussed in this guide, see the following API documentation: -- `updateOne() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/update-one.html>`__ -- `updateMany() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/update-many.html>`__ +- `updateOne() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/update-one.html>`__ +- `updateMany() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/update-many.html>`__ - `UpdateOptions <{+core-api+}/com/mongodb/client/model/UpdateOptions.html>`__ - `UpdateResult <{+core-api+}/com/mongodb/client/result/UpdateResult.html>`__ From ad617264b2493fe72615fcfe3320af42d03acd00 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:27:36 -0400 Subject: [PATCH 04/14] DOCSP-41819: replace document (#29) * DOCSP-41819: replace document * AS PR fixes 1 * fix * remove unused import --- source/includes/write/replace.kt | 38 ++++++ source/write-operations.txt | 6 +- source/write/replace.txt | 198 +++++++++++++++++++++++++++++++ source/write/update.txt | 8 +- 4 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 source/includes/write/replace.kt create mode 100644 source/write/replace.txt diff --git a/source/includes/write/replace.kt b/source/includes/write/replace.kt new file mode 100644 index 0000000..e647116 --- /dev/null +++ b/source/includes/write/replace.kt @@ -0,0 +1,38 @@ +import com.mongodb.client.model.Filters +import com.mongodb.client.model.ReplaceOptions +import com.mongodb.kotlin.client.MongoClient + +// start-data-class +data class Restaurant( + val name: String, + val borough: String, + val cuisine: String, + val owner: String?, +) +// end-data-class + +fun main() { + val uri = "" + + val mongoClient = MongoClient.create(uri) + val database = mongoClient.getDatabase("sample_restaurants") + val collection = database.getCollection("restaurants") + + // start-replace-one + val filter = Filters.eq(Restaurant::name.name, "Primola Restaurant") + val replacement = Restaurant( + "Frutti Di Mare", + "Queens", + "Seafood", + owner = "Sal Thomas" + ) + val result = collection.replaceOne(filter, replacement) + // end-replace-one + + // start-replace-options + val opts = ReplaceOptions().upsert(true) + val result = collection.replaceOne(filter, replacement, opts) + // end-replace-options + +} + diff --git a/source/write-operations.txt b/source/write-operations.txt index 398f3d0..7760534 100644 --- a/source/write-operations.txt +++ b/source/write-operations.txt @@ -24,11 +24,11 @@ Write Data to MongoDB /write/insert /write/update + /write/replace /write/delete /write/bulk-write .. - /write/replace /write/gridfs Overview @@ -135,8 +135,8 @@ collection with a new document: :copyable: :dedent: -.. To learn more about the ``replace_one()`` method, see the -.. :ref:`Replace Documents ` guide. +To learn more about the ``replaceOne()`` method, see the +:ref:`Replace Documents ` guide. Delete One ---------- diff --git a/source/write/replace.txt b/source/write/replace.txt new file mode 100644 index 0000000..c39ff1a --- /dev/null +++ b/source/write/replace.txt @@ -0,0 +1,198 @@ +.. _kotlin-sync-write-replace: + +================= +Replace Documents +================= + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: modify, change, code example + +Overview +-------- + +In this guide, you can learn how to use the {+driver-short+} to perform a **replace +operation** on a document in a MongoDB collection. A replace operation +removes all fields and values from a specified document except the +``_id`` field, and adds new fields and values that you specify. This +operations differs from an update operation, which changes only +specified fields in one or more documents. + +To learn more about update operations, see the +:ref:`kotlin-sync-write-update` guide. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``sample_restaurants.restaurants`` collection +from the :atlas:`Atlas sample datasets `. To learn how to create a +free MongoDB Atlas cluster and load the sample datasets, see the +:atlas:`Get Started with Atlas ` guide. + +The documents in this collection are modeled by the following {+language+} data class: + +.. literalinclude:: /includes/write/replace.kt + :start-after: start-data-class + :end-before: end-data-class + :language: kotlin + :copyable: + :dedent: + +Replace Operation +----------------- + +You can perform a replace operation in MongoDB by using the +``replaceOne()`` method. This method removes all fields except the +``_id`` field from the first document that matches the query filter. It +then adds the fields and values you specify to the empty document. + +Required Parameters +~~~~~~~~~~~~~~~~~~~ + +You must pass the following parameters to the ``replaceOne()`` method: + +- **Query filter**, which matches which documents to update. To learn + more about query filters, see the :ref:`kotlin-sync-specify-query` + guide. + +- **Replacement document**, which specifies the fields and values that + you want to replace the existing fields and values with. + +Replace One Document +-------------------- + +The following example uses the ``replaceOne()`` method to replace the +fields and values of a document in which the value of the ``name`` field +value is ``"Primola Restaurant"``: + +.. literalinclude:: /includes/write/replace.kt + :start-after: start-replace-one + :end-before: end-replace-one + :language: kotlin + :copyable: + :dedent: + +.. important:: + + The values of ``_id`` fields are immutable. If your replacement + document specifies a value for the ``_id`` field, it must be the same + as the ``_id`` value of the existing document or the driver raises a + ``WriteError``. + +Customize the Replace Operation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``replaceOne()`` method optionally accepts +a parameter that sets options to configure the replace operation. +If you don't specify any options, the driver performs the replace +operation with default settings. + +The following table describes the setter methods that you can use to +configure an ``ReplaceOptions`` instance: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Property + - Description + + * - ``upsert()`` + - | Specifies whether the replace operation performs an upsert operation if no + documents match the query filter. For more information, see :manual:`upsert + behavior ` + in the {+mdb-server+} manual. + | Defaults to ``false`` + + * - ``bypassDocumentValidation()`` + - | Specifies whether the update operation bypasses document validation. This lets you + update documents that don't meet the schema validation requirements, if any + exist. For more information about schema validation, see :manual:`Schema + Validation ` in the MongoDB + Server manual. + | Defaults to ``false``. + + * - ``collation()`` + - | Specifies the kind of language collation to use when sorting + results. For more information, see :manual:`Collation ` + in the {+mdb-server+} manual. + + * - ``hint()`` + - | Sets the index to use when matching documents. + For more information, see the :manual:`hint statement + ` + in the {+mdb-server+} manual. + + * - ``let()`` + - | Provides a map of parameter names and values to set top-level + variables for the operation. Values must be constant or closed + expressions that don't reference document fields. + + * - ``comment()`` + - | Sets a comment to attach to the operation. + +The following code sets the ``upsert`` option to ``true``, which instructs the +driver to insert a new document that has the fields and values specified +in the replacement document if the query filter doesn't match any +existing documents: + +.. literalinclude:: /includes/write/replace.kt + :start-after: start-replace-options + :end-before: end-replace-options + :language: kotlin + :copyable: + :dedent: + +Return Value +~~~~~~~~~~~~ + +The ``replaceOne()`` method returns an ``UpdateResult`` +object. You can use the following methods to access information from +an ``UpdateResult`` instance: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Property + - Description + + * - ``getMatchedCount()`` + - | Returns the number of documents that matched the query filter, regardless of + how many updates were performed. + + * - ``getModifiedCount()`` + - | Returns the number of documents modified by the update operation. If an updated + document is identical to the original, it is not included in this + count. + + * - ``wasAcknowledged()`` + - | Returns ``true`` if the server acknowledged the result. + + * - ``getUpsertedId()`` + - | Returns the ``_id`` value of the document that was upserted + in the database, if the driver performed an upsert. + +Additional Information +---------------------- + +To view a runnable code example that demonstrates how to replace a +document, see :ref:`kotlin-sync-write`. + +API Documentation +~~~~~~~~~~~~~~~~~ + +To learn more about any of the methods or types discussed in this +guide, see the following API documentation: + +- `replaceOne() <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-collection/replace-one.html>`__ +- `UpdateResult <{+core-api+}/com/mongodb/client/result/UpdateResult.html>`__ diff --git a/source/write/update.txt b/source/write/update.txt index 5d53628..b171bf9 100644 --- a/source/write/update.txt +++ b/source/write/update.txt @@ -183,11 +183,11 @@ an ``UpdateResult`` instance: - Description * - ``getMatchedCount()`` - - | Indicates the number of documents that matched the query filter, regardless of + - | Returns the number of documents that matched the query filter, regardless of how many updates were performed. * - ``getModifiedCount()`` - - | Indicates number of documents modified by the update operation. If an updated + - | Returns the number of documents modified by the update operation. If an updated document is identical to the original, it is not included in this count. @@ -195,7 +195,7 @@ an ``UpdateResult`` instance: - | Returns ``true`` if the server acknowledged the result. * - ``getUpsertedId()`` - - | Specifies the ``_id`` value of the document that was upserted + - | Returns the ``_id`` value of the document that was upserted in the database, if the driver performed an upsert. .. note:: @@ -209,7 +209,7 @@ an ``UpdateResult`` instance: Additional Information ---------------------- -To view runnable code examples that demonstrate how to updates documents by +To view runnable code examples that demonstrate how to update documents by using the {+driver-short+}, see :ref:`kotlin-sync-write`. API Documentation From 88f71b90589c42369a0723ab677575acfa50c056 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Fri, 9 Aug 2024 09:09:06 -0400 Subject: [PATCH 05/14] DOCSP-41145: Aggregation landing page (#30) --- snooty.toml | 3 +- source/aggregation.txt | 220 +++++++++++++++++++++ source/includes/aggregation/aggregation.kt | 46 +++++ source/index.txt | 7 + 4 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 source/aggregation.txt create mode 100644 source/includes/aggregation/aggregation.kt diff --git a/snooty.toml b/snooty.toml index 60ae09b..301fa39 100644 --- a/snooty.toml +++ b/snooty.toml @@ -13,7 +13,8 @@ toc_landing_pages = [ "/connect", "/indexes", "work-with-indexes", - "/data-formats" + "/data-formats", + "/aggregation" ] sharedinclude_root = "https://raw.githubusercontent.com/10gen/docs-shared/main/" diff --git a/source/aggregation.txt b/source/aggregation.txt new file mode 100644 index 0000000..c865c9c --- /dev/null +++ b/source/aggregation.txt @@ -0,0 +1,220 @@ +.. _kotlin-sync-aggregation: + +==================================== +Transform Your Data with Aggregation +==================================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, transform, computed, pipeline + :description: Learn how to use the Kotlin Sync driver to perform aggregation operations. + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. .. toctree:: +.. :titlesonly: +.. :maxdepth: 1 + +.. /aggregation/aggregation-tutorials + +Overview +-------- + +In this guide, you can learn how to use the {+driver-short+} to perform +**aggregation operations**. + +You can use aggregation operations to process data in your MongoDB collections and +return computed results. The MongoDB Aggregation framework, which is +part of the Query API, is modeled on the concept of a data processing +pipeline. Documents enter a pipeline that contains one or more stages, +and each stage transforms the documents to output a final aggregated result. + +You can think of an aggregation operation as similar to a car factory. A car factory has +an assembly line, which contains assembly stations with specialized +tools to do specific jobs, like drills and welders. Raw parts enter the +factory, and then the assembly line transforms and assembles them into a +finished product. + +The **aggregation pipeline** is the assembly line, **aggregation stages** are the +assembly stations, and **operator expressions** are the +specialized tools. + +Compare Aggregation and Find Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use find operations to perform the following actions: + +- Select which documents to return +- Select which fields to return +- Sort the results + +You can use aggregation operations to perform the following actions: + +- Perform find operations +- Rename fields +- Calculate fields +- Summarize data +- Group values + +Limitations +~~~~~~~~~~~ + +The following limitations apply when using aggregation operations: + +- Returned documents must not violate the + :manual:`BSON document size limit ` + of 16 megabytes. +- Pipeline stages have a memory limit of 100 megabytes by default. You can exceed this + limit by using the ``allowDiskUse()`` method from ``AggregateIterable`` class. + +.. important:: $graphLookup exception + + The :manual:`$graphLookup + ` stage has a strict + memory limit of 100 megabytes and ignores the ``allowDiskUse`` option. + +Aggregation Example +------------------- + +The examples in this section use the ``restaurants`` collection in the ``sample_restaurants`` +database from the :atlas:`Atlas sample datasets `. To learn how to create a +free MongoDB Atlas cluster and load the sample datasets, see the +:atlas:`Get Started with Atlas ` guide. + +The following {+language+} data class models the documents in this collection: + +.. literalinclude:: /includes/aggregation/aggregation.kt + :start-after: start-data-class + :end-before: end-data-class + :language: kotlin + :copyable: + +Build and Execute an Aggregation Pipeline +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To perform an aggregation on the documents in a collection, pass a list of aggregation +stages to the ``aggregate()`` method. + +This example outputs a count of the number of bakeries in each borough +of New York City. The following code creates aggregation pipeline that contains the +following stages: + +- A :manual:`$match ` stage to filter for documents + in which the value of the ``cuisine`` field is ``"Bakery"``. + +- A :manual:`$group ` stage to group the matching + documents by the ``borough`` field, producing a count of documents for each distinct + value of that field. + +.. TODO: uncomment when Aggregates Builder page is created + +.. .. note:: + +.. The following example uses the builders pattern to implement the stages of an +.. aggregation pipeline. To learn more about how to use the builders pattern, see +.. :ref:`` + +.. io-code-block:: + + .. input:: /includes/aggregation/aggregation.kt + :language: kotlin + :start-after: start-aggregation-pipeline + :end-before: end-aggregation-pipeline + :dedent: + + .. output:: + :visible: false + + Document{{_id=Bronx, count=71}} + Document{{_id=Manhattan, count=221}} + Document{{_id=Brooklyn, count=173}} + Document{{_id=Queens, count=204}} + Document{{_id=Staten Island, count=20}} + Document{{_id=Missing, count=2}} + +.. tip:: + + When specifying a group key for the ``$group`` aggregation stage, ensure that you + escape any ``$`` characters by using the ``\`` character. + +Explain an Aggregation +~~~~~~~~~~~~~~~~~~~~~~ + +To view information about how MongoDB executes your operation, you can +include the ``$explain`` aggregation stage in your pipeline. When MongoDB explains an +operation, it returns **execution plans** and performance statistics. An execution +plan is a potential way MongoDB can complete an operation. +When you instruct MongoDB to explain an operation, it returns both the +plan MongoDB selected for the operation and any rejected execution plans. + +The following code example runs the same aggregation shown in the preceding section +and adds the ``$explain`` stage to output the operation details: + +.. io-code-block:: + + .. input:: /includes/aggregation/aggregation.kt + :language: kotlin + :start-after: start-aggregation-explain + :end-before: end-aggregation-explain + :dedent: + + .. output:: + :visible: false + + { + "explainVersion": "2", + "queryPlanner": { + "namespace": "sample_restaurants.restaurants" + "indexFilterSet": false, + "parsedQuery": { + "cuisine": {"$eq": "Bakery"} + }, + "queryHash": "865F14C3", + "planCacheKey": "0697561B", + "optimizedPipeline": true, + "maxIndexedOrSolutionsReached": false, + "maxIndexedAndSolutionsReached": false, + "maxScansToExplodeReached": false, + "winningPlan": { ... } + ... + } + ... + } + +Additional Information +---------------------- + +To view a full list of expression operators, see :manual:`Aggregation +Operators ` in the {+mdb-server+} manual. + +To learn about assembling an aggregation pipeline and view examples, see +:manual:`Aggregation Pipeline ` in the {+mdb-server+} manual. + +To learn more about creating pipeline stages, see :manual:`Aggregation +Stages ` in the {+mdb-server+} manual. + +To learn more about explaining MongoDB operations, see +:manual:`Explain Output ` and +:manual:`Query Plans ` in the {+mdb-server+} manual. + +.. Aggregation Tutorials +.. ~~~~~~~~~~~~~~~~~~~~~ + +.. To view step-by-step explanations of common aggregation tasks, see +.. :ref:`kotlin-sync-aggregation-tutorials`. + +API Documentation +~~~~~~~~~~~~~~~~~ + +For more information about executing aggregation operations with the {+driver-short+}, +see the following API documentation: + +- `aggregate() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/aggregate.html>`__ +- `AggregateIterable <{+api+}/com.mongodb.kotlin.client/-aggregate-iterable/index.html>`__ \ No newline at end of file diff --git a/source/includes/aggregation/aggregation.kt b/source/includes/aggregation/aggregation.kt new file mode 100644 index 0000000..4b67594 --- /dev/null +++ b/source/includes/aggregation/aggregation.kt @@ -0,0 +1,46 @@ +import com.mongodb.ConnectionString +import com.mongodb.MongoClientSettings +import com.mongodb.client.model.Accumulators +import com.mongodb.client.model.Aggregates +import com.mongodb.client.model.Filters +import com.mongodb.kotlin.client.MongoClient +import org.bson.Document + +// start-data-class +data class Restaurant( + val name: String, + val cuisine: String, + val borough: String +) +// end-data-class + +fun main() { + val uri = "" + + val settings = MongoClientSettings.builder() + .applyConnectionString(ConnectionString(uri)) + .retryWrites(true) + .build() + + val mongoClient = MongoClient.create(settings) + val database = mongoClient.getDatabase("sample_restaurants") + val collection = database.getCollection("restaurants") + + // start-aggregation-pipeline + val pipeline = listOf( + Aggregates.match(Filters.eq(Restaurant::cuisine.name, "Bakery")), + Aggregates.group("\$borough", Accumulators.sum("count", 1)) + ) + + val results = collection.aggregate(pipeline) + + results.forEach { result -> + println(result) + } + // end-aggregation-pipeline + + // start-aggregation-explain + print(collection.aggregate(pipeline).explain()) + // end-aggregation-explain +} + diff --git a/source/index.txt b/source/index.txt index e542231..8ecf273 100644 --- a/source/index.txt +++ b/source/index.txt @@ -18,6 +18,7 @@ /write-operations /read /indexes + /aggregation /data-formats /faq /connection-troubleshooting @@ -78,6 +79,12 @@ Optimize Queries with Indexes Learn how to work with common types of indexes in the :ref:`kotlin-sync-indexes` section. +Transform Your Data with Aggregation +------------------------------------ + +Learn how to use the {+driver-short+} to perform aggregation operations in the +:ref:`kotlin-sync-aggregation` section. + Specialized Data Formats ------------------------ From 89f8076d614a6c6ac826a29b4cad14b76b4ca6f5 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Tue, 13 Aug 2024 09:25:31 -0400 Subject: [PATCH 06/14] DOCSP-41122: Choose a Connection Target (#33) --- source/connect.txt | 10 +- source/connect/connection-targets.txt | 120 ++++++++++++++++++ source/includes/connect/connection-targets.kt | 53 ++++++++ 3 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 source/connect/connection-targets.txt create mode 100644 source/includes/connect/connection-targets.kt diff --git a/source/connect.txt b/source/connect.txt index e1a3497..946eac4 100644 --- a/source/connect.txt +++ b/source/connect.txt @@ -19,13 +19,13 @@ Connect to MongoDB :keywords: client, ssl, tls, localhost .. toctree:: - :titlesonly: - :maxdepth: 1 + :titlesonly: + :maxdepth: 1 - /connect/stable-api -.. + /connect/stable-api + /connect/connection-targets + .. /connect/mongoclient -.. /connect/connection-targets .. /connect/connection-options .. /connect/tls .. /connect/network-compression diff --git a/source/connect/connection-targets.txt b/source/connect/connection-targets.txt new file mode 100644 index 0000000..625e85a --- /dev/null +++ b/source/connect/connection-targets.txt @@ -0,0 +1,120 @@ +.. _kotlin-sync-connection-targets: + +========================== +Choose a Connection Target +========================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: connection string, URI, server, settings, client + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use a connection string and a ``MongoClient`` object +to connect to different types of MongoDB deployments. + +Atlas +----- + +To connect to a MongoDB deployment on Atlas, include the following elements +in your connection string: + +- The URL of your Atlas cluster +- Your MongoDB username +- Your MongoDB password + +Then, pass your connection string to the ``MongoClient`` constructor. + +.. tip:: + + Follow the :atlas:`Atlas driver connection guide ` + to retrieve your connection string. + +When you connect to Atlas, we recommend using the {+stable-api+} client option to avoid +breaking changes when Atlas upgrades to a new version of {+mdb-server+}. +To learn more about the {+stable-api+} feature, see the :ref:`` +guide. + +The following code shows how to use the {+driver-short+} to connect to an Atlas cluster. The +code also uses the ``serverApi()`` method to specify a {+stable-api+} version. + +.. literalinclude:: /includes/connect/connection-targets.kt + :language: kotlin + :start-after: start-connect + :end-before: end-connect + :dedent: + +Local Deployments +----------------- + +To connect to a local MongoDB deployment, use ``localhost`` as the hostname. By +default, the ``mongod`` process runs on port 27017, though you can customize this for +your deployment. + +The following code shows how to use the {+driver-short+} to connect to a local MongoDB +deployment: + +.. literalinclude:: /includes/connect/connection-targets.kt + :language: kotlin + :start-after: start-connect-local + :end-before: end-connect-local + :dedent: + +Replica Sets +------------ + +To connect to a replica set, specify the hostnames (or IP addresses) and +port numbers of the replica-set members. + +If you aren't able to provide a full list of hosts in the replica set, you can +specify one or more of the hosts in the replica set and instruct the {+driver-short+} to +perform automatic discovery to find the others. To instruct the driver to perform +automatic discovery, perform one of the following actions: + +- Specify the name of the replica set as the value of the ``replicaSet`` parameter. +- Specify ``false`` as the value of the ``directConnection`` parameter. +- Specify more than one host in the replica set. + +The following examples show how to connect to a MongoDB replica set running on port +``27017`` of three different hosts by using either the ``ConnectionString`` or +``MongoClientSettings`` class. Select the tab that corresponds to your preferred class. + +.. tabs:: + + .. tab:: ConnectionString + :tabid: connectionstring + + .. literalinclude:: /includes/connect/connection-targets.kt + :language: kotlin + :start-after: start-connect-rs-connection-string + :end-before: end-connect-rs-connection-string + :dedent: + + .. tab:: MongoClientSettings + :tabid: mongoclientsettings + + .. literalinclude:: /includes/connect/connection-targets.kt + :language: kotlin + :start-after: start-connect-rs-settings + :end-before: end-connect-rs-settings + :dedent: + +.. note:: + + The ``MongoClient`` constructor is *non-blocking*. + When you connect to a replica set, the constructor returns immediately while the + client uses background threads to connect to the replica set. + + If you construct a ``MongoClient`` and immediately print the string representation + of its ``nodes`` attribute, the list might be empty while the client connects to + the replica-set members. diff --git a/source/includes/connect/connection-targets.kt b/source/includes/connect/connection-targets.kt new file mode 100644 index 0000000..c9080d9 --- /dev/null +++ b/source/includes/connect/connection-targets.kt @@ -0,0 +1,53 @@ +package org.example +import com.mongodb.ConnectionString +import com.mongodb.MongoClientSettings +import com.mongodb.ServerAddress +import com.mongodb.ServerApi +import com.mongodb.ServerApiVersion +import com.mongodb.kotlin.client.MongoClient + +fun main() { + // start-connect + // Defines Stable API version + val serverApi = ServerApi.builder() + .version(ServerApiVersion.V1) + .build() + + // Uses MongoClientSettings to apply connection string and specify the Stable API version + val settings = MongoClientSettings.builder() + .applyConnectionString("") + .serverApi(serverApi) + .build() + + val mongoClient = MongoClient.create(settings) + // end-connect + + // start-connect-local + val settings = MongoClientSettings.builder() + .applyConnectionString("mongodb://localhost:27017") + .build() + + val mongoClient = MongoClient.create(settings) + // end-connect-local + + // start-connect-rs-connection-string + val mongoClient = MongoClient.create("mongodb://host1:27017,host2:27017,host3:27017/") + // end-connect-rs-connection-string + + // start-connect-rs-settings + val hosts = listOf( + ServerAddress("host1", 27017), + ServerAddress("host2", 27017), + ServerAddress("host3", 27017) + ) + + val settings = MongoClientSettings.builder() + .applyToClusterSettings { builder -> + builder.hosts(hosts) + } + .build() + + val mongoClient = MongoClient.create(settings) + // end-connect-rs-settings +} + From 6ea4b9b0b08993ed37603a7f27090d4cef437545 Mon Sep 17 00:00:00 2001 From: lindseymoore <71525840+lindseymoore@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:07:34 -0400 Subject: [PATCH 07/14] DOCSP-4117 API Documentation (#35) * DOCSP-4117 API Documentation * fix link --- source/index.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/source/index.txt b/source/index.txt index 8ecf273..55df6e4 100644 --- a/source/index.txt +++ b/source/index.txt @@ -26,6 +26,7 @@ /compatibility Validate Driver Artifact Signatures View the Source + API Documentation <{+api+}/com.mongodb.kotlin.client/index.html> Introduction ------------ From 7c1c3ecff7b2d8cc474430b8fbae1c8947e665b3 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:55:21 -0400 Subject: [PATCH 08/14] DOCSP-42514: kotlin user/pass placeholders (#36) * DOCSP-42514: kotlin user/pass placeholders * atlas modal not updated to use these placeholders --- source/connect.txt | 4 ++-- source/includes/connect/compression-tabs.rst | 2 +- source/includes/connect/disable-host-verification-tabs.rst | 2 +- source/includes/connect/tls-tabs.rst | 2 +- source/includes/connect/zlib-level-tabs.rst | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/connect.txt b/source/connect.txt index 946eac4..098d3e8 100644 --- a/source/connect.txt +++ b/source/connect.txt @@ -84,7 +84,7 @@ deployment hosted on Atlas: .. code-block:: kotlin - val uri = "mongodb+srv://:@/?" + val uri = "mongodb+srv://:@/?" val mongoClient = MongoClient.create(uri) Replica Set @@ -161,7 +161,7 @@ selection function: .. code-block:: kotlin - val client = MongoClient.create("mongodb://:@:", + val client = MongoClient.create("mongodb://:@:", server_selector=) .. To learn more about customizing server selection, see diff --git a/source/includes/connect/compression-tabs.rst b/source/includes/connect/compression-tabs.rst index d039503..e8b9cdb 100644 --- a/source/includes/connect/compression-tabs.rst +++ b/source/includes/connect/compression-tabs.rst @@ -22,6 +22,6 @@ .. code-block:: kotlin - val uri = ConnectionString("mongodb+srv://:@/?compressors=snappy,zlib,zstd") + val uri = ConnectionString("mongodb+srv://:@/?compressors=snappy,zlib,zstd") val mongoClient = MongoClient.create(uri) diff --git a/source/includes/connect/disable-host-verification-tabs.rst b/source/includes/connect/disable-host-verification-tabs.rst index 8acb68c..f099f99 100644 --- a/source/includes/connect/disable-host-verification-tabs.rst +++ b/source/includes/connect/disable-host-verification-tabs.rst @@ -20,5 +20,5 @@ .. code-block:: kotlin - val uri = "mongodb://:@:/?"tls=true&tlsAllowInvalidHostnames=true" + val uri = "mongodb://:@:/?"tls=true&tlsAllowInvalidHostnames=true" val mongoClient = MongoClient.create(uri) \ No newline at end of file diff --git a/source/includes/connect/tls-tabs.rst b/source/includes/connect/tls-tabs.rst index 40c0c2a..853c373 100644 --- a/source/includes/connect/tls-tabs.rst +++ b/source/includes/connect/tls-tabs.rst @@ -20,5 +20,5 @@ .. code-block:: kotlin - val uri = "mongodb+srv://:@?tls=true" + val uri = "mongodb+srv://:@?tls=true" val mongoClient = MongoClient.create(uri) \ No newline at end of file diff --git a/source/includes/connect/zlib-level-tabs.rst b/source/includes/connect/zlib-level-tabs.rst index 0d85448..e96c454 100644 --- a/source/includes/connect/zlib-level-tabs.rst +++ b/source/includes/connect/zlib-level-tabs.rst @@ -19,7 +19,7 @@ .. code-block:: kotlin - val uri = "mongodb://:@:/?" + + val uri = "mongodb://:@:/?" + "compressors=zlib" + "zlibCompressionLevel=" From 7ee326196a041af56af06b021750e5cb31c797a9 Mon Sep 17 00:00:00 2001 From: Rea Rustagi <85902999+rustagir@users.noreply.github.com> Date: Wed, 14 Aug 2024 09:43:35 -0400 Subject: [PATCH 09/14] DOCSP-41146: agg exp operations (#32) * DOCSP-41146: agg exp operations * wip * vale * vale * vale * MM PR fixes 1 * vale * indentation fix * MM PR fixes 2 * entry to index * wip --- source/agg-exp-ops.txt | 1180 +++++++++++++++++ source/includes/aggregation/aggExpressions.kt | 529 ++++++++ source/index.txt | 4 + 3 files changed, 1713 insertions(+) create mode 100644 source/agg-exp-ops.txt create mode 100644 source/includes/aggregation/aggExpressions.kt diff --git a/source/agg-exp-ops.txt b/source/agg-exp-ops.txt new file mode 100644 index 0000000..ed87ef0 --- /dev/null +++ b/source/agg-exp-ops.txt @@ -0,0 +1,1180 @@ +.. _kotlin-sync-aggregation-expression-operations: + +================================= +Aggregation Expression Operations +================================= + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: transform data, fluent interface + +Overview +-------- + +In this guide, you can learn how to use the {+driver-short+} to construct +expressions for use in aggregation pipelines. You can perform +expression operations with discoverable, typesafe {+language+} methods rather +than by using BSON documents. Because these methods follow the fluent interface +pattern, you can chain aggregation operations together to create code +that is compact and naturally readable. + +The operations in this guide use methods from the +`com.mongodb.client.model.mql <{+core-api+}/com/mongodb/client/model/mql/package-summary.html>`__ package. +These methods provide an idiomatic way to use the Query API, +the mechanism by which the driver interacts with a MongoDB deployment. To learn more +about the Query API, see the :manual:`Server manual documentation `. + +How to Use Operations +--------------------- + +The examples in this guide assume that you include the following imports +in your code: + +.. code-block:: kotlin + :copyable: true + + import com.mongodb.client.model.Aggregates + import com.mongodb.client.model.Accumulators + import com.mongodb.client.model.Projections + import com.mongodb.client.model.Filters + import com.mongodb.client.model.mql.MqlValues + +To access document fields in an expression, you must reference the +current document being processed by the aggregation pipeline by using the +``current()`` method. To access the value of a +field, you must use the appropriately typed method, such as +``getString()`` or ``getDate()``. When you specify the type for a field, +you ensure that the driver provides only those methods which are +compatible with that type. The following code shows how to reference a +string field called ``name``: + +.. code-block:: kotlin + :copyable: true + + current().getString("name") + +To specify a value in an operation, pass it to the ``of()`` constructor method to +convert it to a valid type. The following code shows how to reference a +value of ``1.0``: + +.. code-block:: kotlin + :copyable: true + + of(1.0) + +To create an operation, chain a method to your field or value reference. +You can build more complex operations by chaining multiple methods. + +The following example creates an operation to find patients in New +Mexico who have visited the doctor’s office at least once. The operation +performs the following actions: + +- Checks if the size of the ``visitDates`` array value is greater than ``0`` + by using the ``gt()`` method +- Checks if the ``state`` field value is “New Mexico” by using the + ``eq()`` method + +The ``and()`` method links these operations so that the pipeline stage +matches only documents that meet both criteria. + +.. code-block:: kotlin + :copyable: true + + current() + .getArray("visitDates") + .size() + .gt(of(0)) + .and(current() + .getString("state") + .eq(of("New Mexico"))) + +While some aggregation stages, such as ``group()``, accept operations +directly, other stages expect that you first include your operation in a +method such as ``computed()`` or ``expr()``. These methods, which take +values of type ``TExpression``, allow you to use your expressions in +certain aggregations. + +To complete your aggregation pipeline stage, include your expression +in an aggregates builder method. The following list provides examples of +how to include your expression in common aggregates builder methods: + +- ``match(expr())`` +- ``project(fields(computed("", )))`` +- ``group()`` + +.. TODO To learn more about these methods, see the +.. :ref:`kotlin-sync-aggregation`. + +Constructor Methods +------------------- + +You can use these constructor methods to define values for use in {+language+} aggregation +expressions. + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Method + - Description + + * - `current() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#current()>`__ + - References the current document being processed by the aggregation pipeline. + + * - `currentAsMap() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#currentAsMap()>`__ + - References the current document being processed by the aggregation pipeline as a map value. + + * - | `of() for MqlBoolean <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(boolean)>`__ + | `of() for MqlNumber (double) <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(double)>`__ + | `of() for MqlNumber (Decimal128) <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(org.bson.types.Decimal128)>`__ + | `of() for MqlInteger (int) <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(int)>`__ + | `of() for MqlInteger (long) <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(long)>`__ + | `of() for MqlString <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(java.lang.String)>`__ + | `of() for MqlDate <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(java.time.Instant)>`__ + | `of() for MqlDocument <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#of(org.bson.conversions.Bson)>`__ + + - Returns an ``MqlValue`` type corresponding to the provided primitive. + + * - `ofArray() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofArray(T...)>`__ + + | **Typed Variants**: + | `ofBooleanArray() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofBooleanArray(boolean...)>`__ + | `ofDateArray() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofDateArray(java.time.Instant...)>`__ + | `ofIntegerArray() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofIntegerArray(int...)>`__ + | `ofNumberArray() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofNumberArray(double...)>`__ + | `ofStringArray() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofStringArray(java.lang.String...)>`__ + + - Returns an array of ``MqlValue`` types corresponding to the provided array of primitives. + + * - `ofEntry() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofEntry(com.mongodb.client.model.mql.MqlString,T)>`__ + - Returns an entry value. + + * - `ofMap() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofMap()>`__ + - Returns an empty map value. + + * - `ofNull() <{+core-api+}/com/mongodb/client/model/mql/MqlValues.html#ofNull()>`__ + - Returns the null value as exists in the Query API. + +.. important:: + + When you provide a value to one of these methods, the driver treats + it literally. For example, ``of("$x")`` represents the string value + ``"$x"``, rather than a field named ``x``. + +Refer to any of the sections under :ref:`Operations +` for examples using these +methods. + +.. _kotlin-sync-aggregation-expression-ops-section: + +Operations +---------- + +The following sections provide information and examples for +aggregation expression operations available in the driver. +The operations are categorized by purpose and functionality. + +Each section has a table that describes aggregation methods +available in the driver and corresponding expression operators in the +Query API. The method names link to API documentation and the +aggregation pipeline operator names link to descriptions and examples in +the Server manual documentation. While each method is effectively +equivalent to the corresponding aggregation operator, they may differ in +expected parameters and implementation. + +The example in each section uses the ``listOf()`` method to create a +pipeline from the aggregation stage. Then, each example passes the +pipeline to the ``aggregate()`` method of ``MongoCollection``. + +.. note:: + + The driver generates a Query API expression that may be different + from the Query API expression provided in each example. However, + both expressions will produce the same aggregation result. + +.. important:: + + The driver does not provide methods for all aggregation pipeline operators in + the Query API. To use an unsupported operation in an + aggregation, you must define the entire expression using the BSON ``Document`` + type. + +.. TODO add to note To learn more about the ``Document`` type, see :ref:``. + +Arithmetic Operations +~~~~~~~~~~~~~~~~~~~~~ + +You can perform an arithmetic operation on a value of type ``MqlInteger`` or +``MqlNumber`` using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - | `abs() for MqlInteger <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#abs()>`__ + | `abs() for MqlNumber <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#abs()>`__ + + - :manual:`$abs ` + + * - | `add() for MqlInteger <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#add(int)>`__ + | `add() for MqlNumber <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#add(com.mongodb.client.model.mql.MqlNumber)>`__ + + - :manual:`$add ` + + * - `divide() <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#divide(com.mongodb.client.model.mql.MqlNumber)>`__ + - :manual:`$divide ` + + * - | `multiply() for MqlInteger <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#multiply(int)>`__ + | `multiply() for MqlNumber <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#multiply(com.mongodb.client.model.mql.MqlNumber)>`__ + + - :manual:`$multiply ` + + * - `round() <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#round()>`__ + - :manual:`$round ` + + * - | `subtract() for MqlInteger <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#subtract(int)>`__ + | `subtract() for MqlNumber <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#subtract(com.mongodb.client.model.mql.MqlNumber)>`__ + + - :manual:`$subtract ` + +Suppose you have weather data for a specific year that includes the +precipitation measurement (in inches) for each day. You want to find the +average precipitation, in millimeters, for each month. + +The ``multiply()`` operator multiplies the ``precipitation`` field by +``25.4`` to convert the field value to millimeters. The ``avg()`` accumulator method +returns the average as the ``avgPrecipMM`` field. The ``group()`` method +groups the values by month given in each document's ``date`` field. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-arithmetic-aggregation + :end-before: end-arithmetic-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in the +Query API: + +.. code-block:: javascript + :copyable: true + + [ { $group: { + _id: { $month: "$date" }, + avgPrecipMM: { + $avg: { $multiply: ["$precipitation", 25.4] } } + } } ] + +Array Operations +~~~~~~~~~~~~~~~~ + +You can perform an array operation on a value of type ``MqlArray`` +using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `all() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#all(java.util.function.Function)>`__ + - :manual:`$allElementsTrue ` + + * - `any() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#any(java.util.function.Function)>`__ + - :manual:`$anyElementTrue ` + + * - `concat() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#concat(com.mongodb.client.model.mql.MqlArray)>`__ + - :manual:`$concatArrays ` + + * - `concatArrays() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#concatArrays(java.util.function.Function)>`__ + - :manual:`$concatArrays ` + + * - `contains() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#contains(T)>`__ + - :manual:`$in ` + + * - `distinct() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#distinct()>`__ + - :manual:`$setUnion ` + + * - `elementAt() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#elementAt(int)>`__ + - :manual:`$arrayElemAt ` + + * - `filter() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#filter(java.util.function.Function)>`__ + - :manual:`$filter ` + + * - `first() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#first()>`__ + - :manual:`$first ` + + * - `joinStrings() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#joinStrings(java.util.function.Function)>`__ + - :manual:`$concat ` + + * - `last() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#last()>`__ + - :manual:`$last ` + + * - `map() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#map(java.util.function.Function)>`__ + - :manual:`$map ` + + * - `max() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#max(T)>`__ + - :manual:`$max ` + + * - `maxN() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#maxN(com.mongodb.client.model.mql.MqlInteger)>`__ + - :manual:`$maxN ` + + * - `min() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#min(T)>`__ + - :manual:`$min ` + + * - `minN() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#minN(com.mongodb.client.model.mql.MqlInteger)>`__ + - :manual:`$minN ` + + * - `multiply() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#multiply(java.util.function.Function)>`__ + - :manual:`$multiply ` + + * - `size() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#size()>`__ + - :manual:`$size ` + + * - `slice() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#slice(int,int)>`__ + - :manual:`$slice ` + + * - `sum() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#sum(java.util.function.Function)>`__ + - :manual:`$sum ` + + * - `union() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#union(com.mongodb.client.model.mql.MqlArray)>`__ + - :manual:`$setUnion ` + + * - `unionArrays() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#unionArrays(java.util.function.Function)>`__ + - :manual:`$setUnion ` + +Suppose you have a collection of movies, each of which contains an array +of nested documents for upcoming showtimes. Each nested document +contains an array that represents the total number of seats in the +theater, where the first array entry is the number of premium seats and +the second entry is the number of regular seats. Each nested document +also contains the number of tickets that have already been bought for +the showtime. A document in this collection might resemble the +following: + +.. code-block:: json + :copyable: false + + { + "_id": ..., + "movie": "Hamlet", + "showtimes": [ + { + "date": "May 14, 2023, 12:00 PM", + "seats": [ 20, 80 ], + "ticketsBought": 100 + }, + { + "date": "May 20, 2023, 08:00 PM", + "seats": [ 10, 40 ], + "ticketsBought": 34 + }] + } + +The ``filter()`` method displays only the results matching the provided +predicate. In this case, the predicate uses ``sum()`` to calculate the +total number of seats and compares that value to the number of ``ticketsBought`` +by using the ``lt()`` method. The ``project()`` method stores these +filtered results as a new ``availableShowtimes`` array field. + +.. tip:: + + You must specify the type of values that an array contains when using + the ``getArray()`` method to work with the values as any specific + type. For example, you must specify that an array contains integers + to perform calculations with those integers elsewhere in + your application. + + The example in this section specifies that the ``seats`` array + contains values of type ``MqlDocument`` so that it can extract nested + fields from each array entry. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-array-aggregation + :end-before: end-array-aggregation + :language: kotlin + :copyable: + :dedent: + +.. note:: + + To improve readability, the previous example assigns intermediary values to + the ``totalSeats`` and ``isAvailable`` variables. If you don't assign + these intermediary values to variables, the code still produces + equivalent results. + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $project: { + availableShowtimes: { + $filter: { + input: "$showtimes", + as: "showtime", + cond: { $lt: [ "$$showtime.ticketsBought", { $sum: "$$showtime.seats" } ] } + } } + } } ] + +Boolean Operations +~~~~~~~~~~~~~~~~~~ + +You can perform a boolean operation on a value of type ``MqlBoolean`` +using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `and() <{+core-api+}/com/mongodb/client/model/mql/MqlBoolean.html#and(com.mongodb.client.model.mql.MqlBoolean)>`__ + - :manual:`$and ` + + * - `not() <{+core-api+}/com/mongodb/client/model/mql/MqlBoolean.html#not()>`__ + - :manual:`$not ` + + * - `or() <{+core-api+}/com/mongodb/client/model/mql/MqlBoolean.html#or(com.mongodb.client.model.mql.MqlBoolean)>`__ + - :manual:`$or ` + +Suppose you want to classify very low or high weather temperature +readings (in degrees Fahrenheit) as extreme. + +The ``or()`` operator checks to see if temperatures are extreme by comparing +the ``temperature`` field to predefined values by using the ``lt()`` and +``gt()`` methods. The ``project()`` method records this result in the +``extremeTemp`` field. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-boolean-aggregation + :end-before: end-boolean-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $project: { + extremeTemp: { $or: [ { $lt: ["$temperature", 10] }, + { $gt: ["$temperature", 95] } ] } + } } ] + +Comparison Operations +~~~~~~~~~~~~~~~~~~~~~ + +You can perform a comparison operation on a value of type ``MqlValue`` +using the methods described in this section. + +.. tip:: + + The ``cond()`` method is similar to the ternary operator in {+language+} and you + can use it for simple branches based on boolean values. Use + the ``switchOn()`` methods for more complex comparisons such as performing + pattern matching on the value type or other arbitrary checks on the value. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `eq() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#eq(com.mongodb.client.model.mql.MqlValue)>`__ + - :manual:`$eq ` + + * - `gt() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#gt(com.mongodb.client.model.mql.MqlValue)>`__ + - :manual:`$gt ` + + * - `gte() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#gte(com.mongodb.client.model.mql.MqlValue)>`__ + - :manual:`$gte ` + + * - `lt() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#lt(com.mongodb.client.model.mql.MqlValue)>`__ + - :manual:`$lt ` + + * - `lte() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#lte(com.mongodb.client.model.mql.MqlValue)>`__ + - :manual:`$lte ` + + * - | `max() for MqlInteger <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#max(com.mongodb.client.model.mql.MqlInteger)>`__ + | `max() for MqlNumber <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#max(com.mongodb.client.model.mql.MqlNumber)>`__ + + - :manual:`$max ` + + * - | `min() for MqlInteger <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#min(com.mongodb.client.model.mql.MqlInteger)>`__ + | `min() for MqlNumber <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#min(com.mongodb.client.model.mql.MqlNumber)>`__ + + - :manual:`$min ` + + * - `ne() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#ne(com.mongodb.client.model.mql.MqlValue)>`__ + - :manual:`$ne ` + +The following example shows a pipeline that matches all the documents +where the ``location`` field has the value ``"California"``: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-comparison-aggregation + :end-before: end-comparison-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $match: { location: { $eq: "California" } } } ] + +Conditional Operations +~~~~~~~~~~~~~~~~~~~~~~ + +You can perform a conditional operation using the methods described in +this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `cond() <{+core-api+}/com/mongodb/client/model/mql/MqlBoolean.html#cond(T,T)>`__ + - :manual:`$cond ` + + * - `switchOn() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#switchOn(java.util.function.Function)>`__ + + | **Typed Variants**: + | `switchArrayOn() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#switchArrayOn(java.util.function.Function)>`__ + | `switchBooleanOn() <{+core-api+}/com/mongodb/client/model/mql/MqlBoolean.html#switchBooleanOn(java.util.function.Function)>`__ + | `switchDateOn() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#switchDateOn(java.util.function.Function)>`__ + | `switchDocumentOn() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#switchDocumentOn(java.util.function.Function)>`__ + | `switchIntegerOn() <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#switchIntegerOn(java.util.function.Function)>`__ + | `switchMapOn() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#switchMapOn(java.util.function.Function)>`__ + | `switchNumberOn() <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#switchNumberOn(java.util.function.Function)>`__ + | `switchStringOn() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#switchStringOn(java.util.function.Function)>`__ + + - :manual:`$switch ` + +Suppose you have a collection of customers with their membership information. +Originally, customers were either members or not. Over time, membership levels +were introduced and used the same field. The information stored in this field +can be one of a few different types, and you want to create a standardized value +indicating their membership level. + +The ``switchOn()`` method checks each clause in order. If the value matches the +type indicated by the clause, then the clause determines the string value +corresponding to the membership level. If the original value is a string, it +represents the membership level and that value is used. If the data type is a +boolean, it returns either ``Gold`` or ``Guest`` for the membership level. If +the data type is an array, it returns the most recent string in the array which +matches the most recent membership level. If the ``member`` field is an +unknown type, the ``switchOn()`` method provides a default value of ``Guest``. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-conditional-aggregation + :end-before: end-conditional-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $project: { + membershipLevel: { + $switch: { + branches: [ + { case: { $eq: [ { $type: "$member" }, "string" ] }, then: "$member" }, + { case: { $eq: [ { $type: "$member" }, "bool" ] }, then: { $cond: { + if: "$member", + then: "Gold", + else: "Guest" } } }, + { case: { $eq: [ { $type: "$member" }, "array" ] }, then: { $last: "$member" } } + ], + default: "Guest" } } + } } ] + +Convenience Operations +~~~~~~~~~~~~~~~~~~~~~~ + +You can apply custom functions to values of type +``MqlValue`` using the methods described in this section. + +To improve readability and allow for code reuse, you can move redundant +code into static methods. However, you cannot directly chain +static methods in {+language+}. The ``passTo()`` method lets you chain values +into custom static methods. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `passTo() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#passTo(java.util.function.Function)>`__ + + | **Typed Variants**: + | `passArrayTo() <{+core-api+}/com/mongodb/client/model/mql/MqlArray.html#passArrayTo(java.util.function.Function)>`__ + | `passBooleanTo() <{+core-api+}/com/mongodb/client/model/mql/MqlBoolean.html#passBooleanTo(java.util.function.Function)>`__ + | `passDateTo() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#passDateTo(java.util.function.Function)>`__ + | `passDocumentTo() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#passDocumentTo(java.util.function.Function)>`__ + | `passIntegerTo() <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#passIntegerTo(java.util.function.Function)>`__ + | `passMapTo() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#passMapTo(java.util.function.Function)>`__ + | `passNumberTo() <{+core-api+}/com/mongodb/client/model/mql/MqlNumber.html#passNumberTo(java.util.function.Function)>`__ + | `passStringTo() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#passStringTo(java.util.function.Function)>`__ + + - *No corresponding operator* + +Suppose you want to determine how a class is performing against some +benchmarks. You want to find the average final grade for each class and +compare it against the benchmark values. + +The following custom method ``gradeAverage()`` takes an array of documents and +the name of an integer field shared across those documents. It calculates the +average of that field across all the documents in the provided array and +determines the average of that field across all the elements in +the provided array. The ``evaluate()`` method compares a provided value to +two provided range limits and generates a response string based on +how the values compare: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-convenience-aggregation-methods + :end-before: end-convenience-aggregation-methods + :language: kotlin + :copyable: + :dedent: + +.. tip:: + + Using the ``passTo()`` method allows you to reuse + your custom methods for other aggregations. For example, you can + use the ``gradeAverage()`` method to find the average of grades for + groups of students filtered by entry year or district, not just their + class. Similarly, you could use the ``evaluate()`` method to evaluate + an individual student's performance or an entire school's performance. + +The ``passArrayTo()`` method takes an array of all students and calculates the +average score by using the ``gradeAverage()`` method. Then, the +``passNumberTo()`` method uses the ``evaluate()`` method to determine how the +classes are performing. This example stores the result as the ``evaluation`` +field using the ``project()`` method. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-convenience-aggregation + :end-before: end-convenience-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $project: { + evaluation: { $switch: { + branches: [ + { case: { $lte: [ { $avg: "$students.finalGrade" }, 70 ] }, + then: "Needs improvement" + }, + { case: { $lte: [ { $avg: "$students.finalGrade" }, 85 ] }, + then: "Meets expectations" + } + ], + default: "Exceeds expectations" } } + } } ] + +Conversion Operations +~~~~~~~~~~~~~~~~~~~~~ + +You can perform a conversion operation to convert between certain ``MqlValue`` +types using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `asDocument() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#asDocument()>`__ + - *No corresponding operator* + + * - `asMap() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#asMap()>`__ + - *No corresponding operator* + + * - `asString() for MqlDate <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#asString(com.mongodb.client.model.mql.MqlString,com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$dateToString ` + + * - `asString() for MqlValue <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#asString()>`__ + - :manual:`$toString ` + + * - `millisecondsAsDate() <{+core-api+}/com/mongodb/client/model/mql/MqlInteger.html#millisecondsAsDate()>`__ + - :manual:`$toDate ` + + * - `parseDate() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#parseDate()>`__ + - :manual:`$dateFromString ` + + * - `parseInteger() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#parseInteger()>`__ + - :manual:`$toInt ` + +Suppose you want to have a collection of student data that includes +their graduation years, which are stored as strings. You want to +calculate the year of their five-year reunion and store this value in a +new field. + +The ``parseInteger()`` method converts the ``graduationYear`` to an integer +so that ``add()`` can calculate the reunion year. The ``addFields()`` method +stores this result as a new ``reunionYear`` field. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-convenience-aggregation + :end-before: end-convenience-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $addFields: { + reunionYear: { + $add: [ { $toInt: "$graduationYear" }, 5 ] } + } } ] + +Date Operations +~~~~~~~~~~~~~~~ + +You can perform a date operation on a value of type ``MqlDate`` +using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `dayOfMonth() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#dayOfMonth(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$dayOfMonth ` + + * - `dayOfWeek() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#dayOfWeek(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$dayOfWeek ` + + * - `dayOfYear() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#dayOfYear(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$dayOfYear ` + + * - `hour() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#hour(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$hour ` + + * - `millisecond() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#millisecond(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$millisecond ` + + * - `minute() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#minute(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$minute ` + + * - `month() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#month(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$month ` + + * - `second() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#second(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$second ` + + * - `week() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#week(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$week ` + + * - `year() <{+core-api+}/com/mongodb/client/model/mql/MqlDate.html#year(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$year ` + +Suppose you have data about package deliveries and want to match +deliveries that occurred on any Monday in the ``"America/New_York"`` time +zone. + +If the ``deliveryDate`` field contains any string values representing +valid dates, such as ``"2018-01-15T16:00:00Z"`` or ``"Jan 15, 2018, 12:00 +PM EST"``, you can use the ``parseDate()`` method to convert the strings +into date types. + +The ``dayOfWeek()`` method determines which day of the week that a date +is, then converts it to a number. The number assignment uses ``0`` to mean +Sunday when using the ``"America/New_York"`` timezone. The ``eq()`` +method compares this value to ``2``, or Monday. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-date-aggregation + :end-before: end-date-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $match: { + $expr: { + $eq: [ { + $dayOfWeek: { + date: { $dateFromString: { dateString: "$deliveryDate" } }, + timezone: "America/New_York" }}, + 2 + ] } + } } ] + +Document Operations +~~~~~~~~~~~~~~~~~~~ + +You can perform a document operation on a value of type ``MqlDocument`` +using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - | `getArray() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getArray(java.lang.String)>`__ + | `getBoolean() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getBoolean(java.lang.String)>`__ + | `getDate() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getDate(java.lang.String)>`__ + | `getDocument() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getDocument(java.lang.String)>`__ + | `getField() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getField(java.lang.String)>`__ + | `getInteger() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getInteger(java.lang.String)>`__ + | `getMap() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getMap(java.lang.String)>`__ + | `getNumber() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getNumber(java.lang.String)>`__ + | `getString() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#getString(java.lang.String)>`__ + - :manual:`$getField ` + + * - `hasField() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#hasField(java.lang.String)>`__ + - *No corresponding operator* + + * - `merge() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#merge(com.mongodb.client.model.mql.MqlDocument)>`__ + - :manual:`$mergeObjects ` + + * - `setField() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#setField(java.lang.String,com.mongodb.client.model.mql.MqlValue)>`__ + - :manual:`$setField ` + + * - `unsetField() <{+core-api+}/com/mongodb/client/model/mql/MqlDocument.html#unsetField(java.lang.String)>`__ + - :manual:`$unsetField ` + +Suppose you have a collection of legacy customer data which includes +addresses as child documents under the ``mailing.address`` field. You want +to find all the customers who live in Washington state. A +document in this collection might resemble the following: + +.. code-block:: json + :copyable: false + + { + "_id": ..., + "customer.name": "Mary Kenneth Keller", + "mailing.address": + { + "street": "601 Mongo Drive", + "city": "Vasqueztown", + "state": "CO", + "zip": 27017 + } + } + +The ``getDocument()`` method retrieves the ``mailing.address`` field as a +document so the nested ``state`` field can be retrieved with the +``getString()`` method. The ``eq()`` method checks if the value of the +``state`` field is ``"WA"``. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-document-aggregation + :end-before: end-document-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ + { $match: { + $expr: { + $eq: [{ + $getField: { + input: { $getField: { input: "$$CURRENT", field: "mailing.address"}}, + field: "state" }}, + "WA" ] + }}}] + +Map Operations +~~~~~~~~~~~~~~ + +You can perform a map operation on a value of either type ``MqlMap`` or +``MqlEntry`` using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `entries() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#entries()>`__ + - :manual:`$objectToArray ` + + * - `get() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#get(com.mongodb.client.model.mql.MqlString)>`__ + - *No corresponding operator* + + * - `getKey() <{+core-api+}/com/mongodb/client/model/mql/MqlEntry.html#getKey()>`__ + - *No corresponding operator* + + * - `getValue() <{+core-api+}/com/mongodb/client/model/mql/MqlEntry.html#getValue()>`__ + - *No corresponding operator* + + * - `has() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#has(com.mongodb.client.model.mql.MqlString)>`__ + - *No corresponding operator* + + * - `merge() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#merge(com.mongodb.client.model.mql.MqlMap)>`__ + - *No corresponding operator* + + * - `set() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#set(com.mongodb.client.model.mql.MqlString,T)>`__ + - *No corresponding operator* + + * - `setKey() <{+core-api+}/com/mongodb/client/model/mql/MqlEntry.html#setKey(com.mongodb.client.model.mql.MqlString)>`__ + - *No corresponding operator* + + * - `setValue() <{+core-api+}/com/mongodb/client/model/mql/MqlEntry.html#setValue(T)>`__ + - *No corresponding operator* + + * - `unset() <{+core-api+}/com/mongodb/client/model/mql/MqlMap.html#unset(com.mongodb.client.model.mql.MqlString)>`__ + - *No corresponding operator* + +Suppose you have a collection of inventory data where each document represents +an individual item you're responsible for supplying. Each document contains a +field that is a map of all your warehouses and how many copies they +have in their inventory of the item. You want to determine the total number of +copies of items you have across all warehouses. A document in this +collection might resemble the following: + +.. code-block:: json + :copyable: false + + { + "_id": ..., + "item": "notebook" + "warehouses": [ + { "Atlanta", 50 }, + { "Chicago", 0 }, + { "Portland", 120 }, + { "Dallas", 6 } + ] + } + +The ``entries()`` method returns the map entries in the ``warehouses`` +field as an array. The ``sum()`` method calculates the total value of items +based on the values in the array retrieved with the ``getValue()`` method. +This example stores the result as the new ``totalInventory`` field using the +``project()`` method. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-map-aggregation + :end-before: end-map-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $project: { + totalInventory: { + $sum: { + $getField: { $objectToArray: "$warehouses" }, + } } + } } ] + +String Operations +~~~~~~~~~~~~~~~~~ + +You can perform a string operation on a value of type ``MqlString`` +using the methods described in this section. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `append() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#append(com.mongodb.client.model.mql.MqlString)>`__ + - :manual:`$concat ` + + * - `length() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#length()>`__ + - :manual:`$strLenCP ` + + * - `lengthBytes() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#lengthBytes()>`__ + - :manual:`$strLenBytes ` + + * - `substr() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#substr(int,int)>`__ + - :manual:`$substrCP ` + + * - `substrBytes() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#substrBytes(int,int)>`__ + - :manual:`$substrBytes ` + + * - `toLower() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#toLower()>`__ + - :manual:`$toLower ` + + * - `toUpper() <{+core-api+}/com/mongodb/client/model/mql/MqlString.html#toUpper()>`__ + - :manual:`$toUpper ` + +Suppose you want to generate lowercase usernames for employees of a +company from the employees' last names and employee IDs. + +The ``append()`` method combines the ``lastName`` and ``employeeID`` fields into +a single username, while the ``toLower()`` method makes the entire username +lowercase. This example stores the result as a new ``username`` field using +the ``project()`` method. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-string-aggregation + :end-before: end-string-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $project: { + username: { + $toLower: { $concat: ["$lastName", "$employeeID"] } } + } } ] + +Type-Checking Operations +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can perform a type-check operation on a value of type ``MqlValue`` +using the methods described in this section. + +These methods do not return boolean values. Instead, you provide a default value +that matches the type specified by the method. If the checked value +matches the method type, the checked value is returned. Otherwise, the supplied +default value is returned. To program branching logic based on the +data type, see ``switchOn()``. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Method + - Aggregation Pipeline Operator + + * - `isArrayOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isArrayOr(com.mongodb.client.model.mql.MqlArray)>`__ + - *No corresponding operator* + + * - `isBooleanOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isBooleanOr(com.mongodb.client.model.mql.MqlBoolean)>`__ + - *No corresponding operator* + + * - `isDateOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isDateOr(com.mongodb.client.model.mql.MqlDate)>`__ + - *No corresponding operator* + + * - `isDocumentOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isDocumentOr(T)>`__ + - *No corresponding operator* + + * - `isIntegerOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isIntegerOr(com.mongodb.client.model.mql.MqlInteger)>`__ + - *No corresponding operator* + + * - `isMapOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isMapOr(com.mongodb.client.model.mql.MqlMap)>`__ + - *No corresponding operator* + + * - `isNumberOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isNumberOr(com.mongodb.client.model.mql.MqlNumber)>`__ + - *No corresponding operator* + + * - `isStringOr() <{+core-api+}/com/mongodb/client/model/mql/MqlValue.html#isStringOr(com.mongodb.client.model.mql.MqlString)>`__ + - *No corresponding operator* + +Suppose you have a collection of rating data. An early version of the review +schema allowed users to submit negative reviews without a star rating. You want +convert any of these negative reviews without a star rating to have the minimum +value of 1 star. + +The ``isNumberOr()`` method returns either the value of ``rating``, or +a value of ``1`` if ``rating`` is not a number or is null. The +``project()`` method returns this value as a new ``numericalRating`` field. + +The following code shows the pipeline for this aggregation: + +.. literalinclude:: /includes/aggregation/aggExpressions.kt + :start-after: start-type-aggregation + :end-before: end-type-aggregation + :language: kotlin + :copyable: + :dedent: + +The following code provides an equivalent aggregation pipeline in +the Query API: + +.. code-block:: javascript + :copyable: true + + [ { $project: { + numericalRating: { + $cond: { if: { $isNumber: "$rating" }, + then: "$rating", + else: 1 + } } + } } ] diff --git a/source/includes/aggregation/aggExpressions.kt b/source/includes/aggregation/aggExpressions.kt new file mode 100644 index 0000000..b58a2b3 --- /dev/null +++ b/source/includes/aggregation/aggExpressions.kt @@ -0,0 +1,529 @@ +import com.mongodb.client.model.* +import com.mongodb.client.model.mql.* +import com.mongodb.client.model.mql.MqlValues.current +import com.mongodb.client.model.mql.MqlValues.of +import com.mongodb.kotlin.client.MongoClient +import org.bson.Document +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import java.time.LocalDate + +class AggExpressionsTest { + companion object { + val uri = "" + val mongoClient = MongoClient.create(uri) + private val database = mongoClient.getDatabase("aggExpressions") + + @AfterAll + @JvmStatic + fun afterAll() { + mongoClient.close() + } + } + + @Test + fun arithmeticOpsTest() { + + data class Entry(val date: LocalDate, val precipitation: Double) + + val collection = database.getCollection("weatherData") + + val entries = listOf( + Entry(LocalDate.of(2021, 6, 24), 5.0), + Entry(LocalDate.of(2021, 6, 25), 1.4) + ) + + collection.insertMany(entries) + + // start-arithmetic-aggregation + val month = current().getDate("date").month(of("UTC")) + val precip = current().getInteger("precipitation") + + val results = collection.aggregate( + listOf( + Aggregates.group( + month, + Accumulators.avg("avgPrecipMM", precip.multiply(25.4)) + ) + ) + ) + // end-arithmetic-aggregation + + val result = results.toList() + assertEquals(1, result.size) + assertEquals(6, result[0].getInteger("_id")) + assertEquals(81.28, result[0].getDouble("avgPrecipMM")) + + collection.drop() + } + + @Test + fun arrayOpsTest() { + + data class Showtime( + val date: String, + val seats: List, + val ticketsBought: Int + ) + + data class Movie( + val movie: String, + val showtimes: List, + ) + + val collection = database.getCollection("movies") + + val entries = listOf( + Movie( + "Hamlet", + listOf( + Showtime("May 14, 2023, 12:00 PM", listOf(20, 80), 100), + Showtime("May 20, 2023, 08:00 PM", listOf(10, 40), 34) + ) + ) + ) + + collection.insertMany(entries) + + // start-array-aggregation + val showtimes = current().getArray("showtimes") + + val results = collection.aggregate( + listOf( + Aggregates.project( + Projections.fields( + Projections.computed("availableShowtimes", showtimes + .filter { showtime -> + val seats = showtime.getArray("seats") + val totalSeats = seats.sum { n -> n } + val ticketsBought = showtime.getInteger("ticketsBought") + val isAvailable = ticketsBought.lt(totalSeats) + isAvailable + }) + ) + ) + ) + ) + // end-array-aggregation + + val result = results.toList() + assertEquals(1, result.size) + + collection.drop() + } + + @Test + fun booleanOpsTest() { + + data class Entry(val location: String, val temperature: Int) + + val collection = database.getCollection("tempData") + + val entries = listOf( + Entry("Randolph, NJ", 100), + Entry("Seward, AK", 1), + Entry("Lincoln, NE", 45) + ) + + collection.insertMany(entries) + + // start-boolean-aggregation + val temperature = current().getInteger("temperature") + + val results = collection.aggregate( + listOf( + Aggregates.project( + Projections.fields( + Projections.computed( + "extremeTemp", temperature + .lt(of(10)) + .or(temperature.gt(of(95))) + ) + ) + ) + ) + ) + // end-boolean-aggregation + + val result = results.toList() + assertEquals(3, result.size) + assertEquals(true, result[0].getBoolean("extremeTemp")) + assertEquals(true, result[1].getBoolean("extremeTemp")) + assertEquals(false, result[2].getBoolean("extremeTemp")) + + collection.drop() + } + + @Test + fun comparisonOpsTest() { + + data class Place(val location: String) + + val collection = database.getCollection("placesData") + + val entries = listOf( + Place("Delaware"), + Place("California") + ) + + collection.insertMany(entries) + + // start-comparison-aggregation + val location = current().getString("location") + + val results = collection.aggregate( + listOf( + Aggregates.match( + Filters.expr(location.eq(of("California"))) + ) + ) + ) + // end-comparison-aggregation + + val result = results.toList() + assertEquals(1, result.size) + assertEquals("California", result[0].getString("location")) + + collection.drop() + } + + @Test + fun conditionalOpsTest() { + + val collection = database.getCollection("memberData") + + val entries = listOf( + Document("name", "Sandra K").append("member", "Gold"), + Document("name", "Darren I").append("member", true), + Document("name", "Corey P").append("member", false), + Document("name", "Francine D").append("member", 7), + Document("name", "Lily A").append("member", listOf("None", "Gold", "Premium")) + ) + + collection.insertMany(entries) + + // start-conditional-aggregation + val member = current().getField("member") + + val results = collection.aggregate( + listOf( + Aggregates.project( + Projections.fields( + Projections.computed("membershipLevel", + member.switchOn { field -> + field + .isString { s -> s } + .isBoolean { b -> b.cond(of("Gold"), of("Guest")) } + .isArray { a -> a.last() } + .defaults { d -> of("Guest") } + }) + ) + ) + ) + ) + // end-conditional-aggregation + + val result = results.toList() + assertEquals(5, result.size) + assertEquals("Gold", result[0].getString("membershipLevel")) + assertEquals("Gold", result[1].getString("membershipLevel")) + assertEquals("Guest", result[2].getString("membershipLevel")) + assertEquals("Guest", result[3].getString("membershipLevel")) + assertEquals("Premium", result[4].getString("membershipLevel")) + + collection.drop() + } + + @Test + fun convenienceOpsTest() { + + data class Student(val name: String, val finalGrade: Int) + data class Class(val title: String, val students: List) + + val collection = database.getCollection("classData") + + val entries = listOf( + Class("History", listOf(Student("Kaley", 99), Student("Kevin", 80))), + Class("Math", listOf(Student("Dan", 68), Student("Shelley", 45))), + Class("Language Arts", listOf(Student("Kaley", 83), Student("Shelley", 86))) + ) + + collection.insertMany(entries) + + // start-convenience-aggregation-methods + fun gradeAverage(students: MqlArray, fieldName: String): MqlNumber { + val sum = students.sum { student -> student.getInteger(fieldName) } + val avg = sum.divide(students.size()) + return avg + } + + fun evaluate(grade: MqlNumber, cutoff1: MqlNumber, cutoff2: MqlNumber): MqlString { + val message = grade.switchOn { on -> + on + .lte(cutoff1) { g -> of("Needs improvement") } + .lte(cutoff2) { g -> of("Meets expectations") } + .defaults { g -> of("Exceeds expectations") } + } + return message + } + // end-convenience-aggregation-methods + + // start-convenience-aggregation + val students = current().getArray("students") + + val results = collection.aggregate( + listOf( + Aggregates.project( + Projections.fields( + Projections.computed("evaluation", students + .passArrayTo { s -> gradeAverage(s, "finalGrade") } + .passNumberTo { grade -> evaluate(grade, of(70), of(85)) }) + ) + ) + ) + ) + // end-convenience-aggregation + + val result = results.toList() + assertEquals(3, result.size) + assertEquals("Exceeds expectations", result[0].getString("evaluation")) + assertEquals("Needs improvement", result[1].getString("evaluation")) + assertEquals("Meets expectations", result[2].getString("evaluation")) + + collection.drop() + } + + @Test + fun conversionOpsTest() { + + data class Alumnus(val name: String, val graduationYear: String) + + val collection = database.getCollection("alumniData") + + val entries = listOf( + Alumnus("Shelley E", "2009"), + Alumnus("Courtney S", "1994") + ) + + collection.insertMany(entries) + + // start-conversion-aggregation + val graduationYear = current().getString("graduationYear") + + val results = collection.aggregate( + listOf( + Aggregates.addFields( + Field( + "reunionYear", + graduationYear + .parseInteger() + .add(5) + ) + ) + ) + ) + // end-conversion-aggregation + + val result = results.toList() + assertEquals(2, result.size) + assertEquals(2014, result[0].getInteger("reunionYear")) + assertEquals(1999, result[1].getInteger("reunionYear")) + + collection.drop() + } + + @Test + fun dateOpsTest() { + + data class Delivery(val desc: String, val deliveryDate: String) + + val collection = database.getCollection("deliveryData") + + val entries = listOf( + Delivery("Order #1234", "2018-01-15T16:00:00Z"), + Delivery("Order #4397", "Jan 15, 2018, 12:00 PM EST"), + Delivery("Order #4397", "Jun 8, 2023, 12:00 PM EST") + ) + + collection.insertMany(entries) + + // start-date-aggregation + val deliveryDate = current().getString("deliveryDate") + + val results = collection.aggregate( + listOf( + Aggregates.match( + Filters.expr( + deliveryDate + .parseDate() + .dayOfWeek(of("America/New_York")) + .eq(of(2)) + ) + ) + ) + ) + // end-date-aggregation + + val result = results.toList() + assertEquals(2, result.size) + assertEquals("Order #1234", result[0].getString("desc")) + assertEquals("Order #4397", result[1].getString("desc")) + + collection.drop() + } + + @Test + fun documentOpsTest() { + + val collection = database.getCollection("addressData") + + val address1 = Document("street", "601 Mongo Drive").append("city", "Vasqueztown").append("state", "CO") + .append("zip", 27017) + val address2 = + Document("street", "533 Maple Ave").append("city", "Bellevue").append("state", "WA").append("zip", 98004) + + val entries = listOf( + Document("customer.name", "Mary Kenneth Keller").append("mailing.address", address1), + Document("customer.name", "Surathi Raj").append("mailing.address", address2) + ) + + collection.insertMany(entries) + + // start-document-aggregation + val address = current().getDocument("mailing.address") + + val results = collection.aggregate( + listOf( + Aggregates.match( + Filters.expr( + address + .getString("state") + .eq(of("WA")) + ) + ) + ) + ) + // end-document-aggregation + + val result = results.toList() + assertEquals(1, result.size) + assertEquals("Surathi Raj", result[0].getString("customer.name")) + + collection.drop() + } + + @Test + fun mapOpsTest() { + + data class Item(val item: String, val warehouses: Map) + + val collection = database.getCollection("stockData") + + val doc = Item("notebook", mapOf("Atlanta" to 50, "Chicago" to 0, "Portland" to 120, "Dallas" to 6)) + + collection.insertOne(doc) + + // start-map-aggregation + val warehouses = current().getMap("warehouses") + + val results = collection.aggregate( + listOf( + Aggregates.project( + Projections.fields( + Projections.computed("totalInventory", warehouses + .entries() + .sum { v -> v.getValue() }) + ) + ) + ) + ) + // end-map-aggregation + + val result = results.toList() + assertEquals(1, result.size) + assertEquals(176, result[0].getInteger("totalInventory")) + + collection.drop() + } + + @Test + fun stringOpsTest() { + + data class Employee(val lastName: String, val employeeID: String) + + val collection = database.getCollection("employeeData") + + val entries = listOf( + Employee("Carter", "12w2"), + Employee("Derry", "32rj") + ) + + collection.insertMany(entries) + + // start-string-aggregation + val lastName = current().getString("lastName") + val employeeID = current().getString("employeeID") + + val results = collection.aggregate( + listOf( + Aggregates.project( + Projections.fields( + Projections.computed( + "username", lastName + .append(employeeID) + .toLower() + ) + ) + ) + ) + ) + // end-string-aggregation + + val result = results.toList() + assertEquals(2, result.size) + assertEquals("carter12w2", result[0].getString("username")) + assertEquals("derry32rj", result[1].getString("username")) + + collection.drop() + } + + @Test + fun typeOpsTest() { + + val collection = database.getCollection("movieRatingData") + + val entries = listOf( + Document("movie", "Narnia").append("rating", 4), + Document("movie", "Jaws").append("rating", null), + Document("movie", "Phantom Thread").append("rating", "hate!!") + ) + + collection.insertMany(entries) + + // start-type-aggregation + val rating = current().getField("rating") + + val results = collection.aggregate( + listOf( + Aggregates.project( + Projections.fields( + Projections.computed( + "numericalRating", rating + .isNumberOr(of(1)) + ) + ) + ) + ) + ) + // end-type-aggregation + + val result = results.toList() + assertEquals(3, result.size) + assertEquals(4, result[0].getInteger("numericalRating")) + assertEquals(1, result[1].getInteger("numericalRating")) + assertEquals(1, result[2].getInteger("numericalRating")) + + collection.drop() + } +} \ No newline at end of file diff --git a/source/index.txt b/source/index.txt index 55df6e4..46f7867 100644 --- a/source/index.txt +++ b/source/index.txt @@ -19,6 +19,7 @@ /read /indexes /aggregation + Use Aggregation Expression Operations /data-formats /faq /connection-troubleshooting @@ -86,6 +87,9 @@ Transform Your Data with Aggregation Learn how to use the {+driver-short+} to perform aggregation operations in the :ref:`kotlin-sync-aggregation` section. +Learn how to use aggregation expression operations to build +aggregation stages in the :ref:`kotlin-sync-aggregation-expression-operations` section. + Specialized Data Formats ------------------------ From 38971747b027518b5fef8878d9ba2aed24223a63 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Wed, 14 Aug 2024 11:51:48 -0400 Subject: [PATCH 10/14] DOCSP-41161: Builders landing page (#28) --- snooty.toml | 1 + source/builders.txt | 115 +++++++++++++++++++++++++++ source/includes/builders/builders.kt | 57 +++++++++++++ source/index.txt | 6 ++ 4 files changed, 179 insertions(+) create mode 100644 source/builders.txt create mode 100644 source/includes/builders/builders.kt diff --git a/snooty.toml b/snooty.toml index 301fa39..314aed4 100644 --- a/snooty.toml +++ b/snooty.toml @@ -14,6 +14,7 @@ toc_landing_pages = [ "/indexes", "work-with-indexes", "/data-formats", + "/builders", "/aggregation" ] diff --git a/source/builders.txt b/source/builders.txt new file mode 100644 index 0000000..11c66f9 --- /dev/null +++ b/source/builders.txt @@ -0,0 +1,115 @@ +.. _kotlin-sync-builders: + +========================= +Use Builders Code Pattern +========================= + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. .. toctree:: + +.. /fundamentals/builders/aggregates +.. /fundamentals/builders/filters +.. /fundamentals/builders/indexes +.. /fundamentals/builders/projections +.. /fundamentals/builders/sort +.. /fundamentals/builders/updates + +Overview +-------- + +This page describes how to use the various available builders in your code and describes +benefits of using the provided builders. + +The {+driver-short+} provides type-safe builder classes and methods that enable developers to +efficiently build queries and aggregations. + +Why Use Builders? +----------------- + +If you use only plain {+language+} to construct BSON query documents, you are not +able to identify syntax errors until runtime. The builders help ensure the corretness of +syntax and can be less verbose than constructing BSON documents. + +Example +------- + +This section provides three equivalent ways to fetch the ``email`` field values of documents +in the ``users`` collection that meet the following criteria: + +- ``gender`` value is ``"female"`` +- ``age`` value is greater than ``29`` + +The following data class models the documents in the ``users`` collection: + +.. literalinclude:: /includes/builders/builders.kt + :start-after: start-user-class + :end-before: end-user-class + :language: kotlin + :copyable: + :dedent: + +The following data class models the results returned by our query: + +.. literalinclude:: /includes/builders/builders.kt + :start-after: start-result-class + :end-before: end-result-class + :language: kotlin + :copyable: + :dedent: + +MongoDB Query API +~~~~~~~~~~~~~~~~~ + +The following sample performs the query by using the MongoDB Query API: + +.. code-block:: js + + collection.find( + { "gender": "female", "age" : { "$gt": 29 }}, + { "_id": 0, "email": 1 } + ) + +Document Class Filter +~~~~~~~~~~~~~~~~~~~~~ + +The following example performs the query by using the ``Document`` class to construct the +query filter: + +.. literalinclude:: /includes/builders/builders.kt + :start-after: start-find + :end-before: end-find + :language: kotlin + :copyable: + :dedent: + +Builders +~~~~~~~~ + +The following example performs the query by using the builder helpers: + +.. literalinclude:: /includes/builders/builders.kt + :start-after: start-find-builders + :end-before: end-find-builders + :language: kotlin + :copyable: + :dedent: + +.. TODO: Uncomment as pages get built + +.. Available Builders +.. ------------------ + +.. The following pages describe how to implement the different classes of builders +.. available in the {+driver-short+}. + +.. - :ref:`Aggregates ` for building aggregation pipelines. +.. - :ref:`Filters ` for building query filters. +.. - :ref:`Indexes ` for creating index keys. +.. - :ref:`Projections ` for building projections. +.. - :ref:`Sorts ` for building sort criteria. +.. - :ref:`Updates ` for building updates. \ No newline at end of file diff --git a/source/includes/builders/builders.kt b/source/includes/builders/builders.kt new file mode 100644 index 0000000..24aeb2f --- /dev/null +++ b/source/includes/builders/builders.kt @@ -0,0 +1,57 @@ +import com.mongodb.ConnectionString +import com.mongodb.MongoClientSettings +import com.mongodb.client.model.Filters +import com.mongodb.client.model.Projections +import com.mongodb.kotlin.client.MongoClient +import org.bson.Document +import org.bson.codecs.pojo.annotations.BsonId +import org.bson.types.ObjectId + +// start-user-class +data class User( + @BsonId val id: ObjectId, + val gender: String, + val age: Int, + val email: String +) +// end-user-class + +// start-result-class +data class Email( + val email: String +) +// end-result-class + +fun main() { + val uri = "" + + val settings = MongoClientSettings.builder() + .applyConnectionString(ConnectionString(uri)) + .retryWrites(true) + .build() + + val mongoClient = MongoClient.create(settings) + val database = mongoClient.getDatabase("sample_restaurants") + val collection = database.getCollection("restaurants") + + // start-find + val filter = Document("gender", "female").append("age", Document("\$gt", 29)) + val projection = Document("_id", 0).append("email", 1) + val results = collection.find(filter).projection(projection) + // end-find + + // start-find-builders + val filter = Filters.and( + Filters.eq(User::gender.name, "female"), + Filters.gt(User::age.name, 29) + ) + + val projection = Projections.fields( + Projections.excludeId(), + Projections.include("email") + ) + + val results = collection.find(filter).projection(projection) + // end-find-builders +} + diff --git a/source/index.txt b/source/index.txt index 46f7867..e4159fb 100644 --- a/source/index.txt +++ b/source/index.txt @@ -21,6 +21,7 @@ /aggregation Use Aggregation Expression Operations /data-formats + /builders /faq /connection-troubleshooting /issues-and-help @@ -96,6 +97,11 @@ Specialized Data Formats Learn how to work with specialized data formats and custom types in the :ref:`kotlin-sync-data-formats` section. +Use Builders API +---------------- + +Learn how to work with the builder operation helpers in the :ref:`kotlin-sync-builders` section. + FAQ --- From 7f9793425d825da074b942e32fd382cb33d0cdfa Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Thu, 15 Aug 2024 09:26:40 -0400 Subject: [PATCH 11/14] DOCSP-41140: Change Streams (#31) --- source/includes/read/change-streams.kt | 56 +++++ source/read.txt | 9 +- source/read/change-streams.txt | 269 +++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 4 deletions(-) create mode 100644 source/includes/read/change-streams.kt create mode 100644 source/read/change-streams.txt diff --git a/source/includes/read/change-streams.kt b/source/includes/read/change-streams.kt new file mode 100644 index 0000000..2a07971 --- /dev/null +++ b/source/includes/read/change-streams.kt @@ -0,0 +1,56 @@ +import com.mongodb.ConnectionString +import com.mongodb.MongoClientSettings +import com.mongodb.client.model.Aggregates +import com.mongodb.client.model.Filters +import com.mongodb.client.model.Updates +import com.mongodb.client.model.changestream.FullDocument +import com.mongodb.kotlin.client.MongoClient + +// start-data-class +data class Restaurant( + val name: String, + val cuisine: String, +) +// end-data-class + +fun main() { + val uri = "" + + val settings = MongoClientSettings.builder() + .applyConnectionString(ConnectionString(uri)) + .build() + + val mongoClient = MongoClient.create(settings) + val database = mongoClient.getDatabase("sample_restaurants") + val collection = database.getCollection("restaurants") + + // start-open-change-stream + collection.watch().forEach { change -> + println(change) + } + // end-open-change-stream + + // start-update-for-change-stream + val filter = Filters.eq(Restaurant::name.name, "Blarney Castle") + val update = Updates.set(Restaurant::cuisine.name, "Irish") + + val result = collection.updateOne(filter, update) + // end-update-for-change-stream + + // start-change-stream-pipeline + val pipeline = listOf( + Aggregates.match(Filters.eq("operationType", "update")) + ) + + collection.watch(pipeline).forEach { change -> + println(change) + } + // end-change-stream-pipeline + + // start-change-stream-post-image + collection.watch().fullDocument(FullDocument.UPDATE_LOOKUP).forEach { change -> + println("Received a change: $change") + } + // end-change-stream-post-image +} + diff --git a/source/read.txt b/source/read.txt index 4800b25..83c0b70 100644 --- a/source/read.txt +++ b/source/read.txt @@ -28,6 +28,7 @@ Read Data from MongoDB /read/specify-documents-to-return /read/count /read/distinct + /read/change-streams Overview -------- @@ -55,9 +56,9 @@ the relevant values for your MongoDB deployment. :linenos: :emphasize-lines: 20-22 -.. .. tip:: +.. tip:: -.. For instructions about how to install the {+driver-short+}, see :ref:``. + For instructions about how to install the {+driver-short+}, see :ref:``. Find Documents -------------- @@ -150,5 +151,5 @@ subsequent change events in that collection: :copyable: :dedent: -.. TODO: To learn more about the ``watch()`` method, see the -.. :ref:`kotlin-sync-change-streams` guide. \ No newline at end of file +To learn more about the ``watch()`` method, see the +:ref:`kotlin-sync-change-streams` guide. \ No newline at end of file diff --git a/source/read/change-streams.txt b/source/read/change-streams.txt new file mode 100644 index 0000000..3cd7fb5 --- /dev/null +++ b/source/read/change-streams.txt @@ -0,0 +1,269 @@ +.. _kotlin-sync-change-streams: + +==================== +Monitor Data Changes +==================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: watch, code example + +Overview +-------- + +In this guide, you can learn how to use the {+driver-short+} to monitor a **change stream**, +allowing you to view real-time changes to your database. A change stream is a {+mdb-server+} feature that +publishes data changes on a collection, database, or deployment. Your application can +subscribe to a change stream and use events to perform other actions. + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``restaurants`` collection in the ``sample_restaurants`` +database from the :atlas:`Atlas sample datasets `. To learn how to create a +free MongoDB Atlas cluster and load the sample datasets, see the +:atlas:`Get Started with Atlas ` guide. + +The following {+language+} data class models the documents in this collection: + +.. literalinclude:: /includes/read/change-streams.kt + :start-after: start-data-class + :end-before: end-data-class + :language: kotlin + :copyable: + +Open a Change Stream +-------------------- + +To open a change stream, call the ``watch()`` method. The instance on which you +call the ``watch()`` method on determines the scope of events that the change +stream listens for. You can call the ``watch()`` method on instances of the following +classes: + +- ``MongoClient``: To monitor all changes in the MongoDB deployment +- ``MongoDatabase``: To monitor changes in all collections in the database +- ``MongoCollection``: To monitor changes in the collection + +Open a Change Stream Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example opens a change stream on the ``restaurants`` collection +and prints changes as they occur: + +.. literalinclude:: /includes/read/change-streams.kt + :start-after: start-open-change-stream + :end-before: end-open-change-stream + :language: kotlin + :copyable: + :dedent: + +To begin watching for changes, run the application. Then, in a separate +application or shell, perform a write operation on the ``restaurants`` collection. The +following example updates a document in which the value of the ``name`` is ``"Blarney Castle"``: + +.. _kotlin-sync-change-stream-update: + +.. literalinclude:: /includes/read/change-streams.kt + :start-after: start-update-for-change-stream + :end-before: end-update-for-change-stream + :language: kotlin + :copyable: + :dedent: + +When you update the collection, the change stream application prints the change +as it occurs. The printed change event resembles the +following: + +.. code-block:: json + + { + "_id": { ... }, + "operationType": "update", + "clusterTime": { ... }, + "ns": { + "db": "sample_restaurants", + "coll": "restaurants" + }, + "updateDescription": { + "updatedFields": { + "cuisine": "Irish" + }, + "removedFields": [], + "truncatedArrays": [] + } + ... + } + +Modify the Change Stream Output +------------------------------- + +You can pass the ``pipeline`` parameter to the ``watch()`` method to modify the +change stream output. This parameter allows you to watch for only specified +change events. Format the parameter as a list of objects that each represents an +aggregation stage. + +You can specify the following stages in the ``pipeline`` parameter: + +- ``$addFields`` +- ``$match`` +- ``$project`` +- ``$replaceRoot`` +- ``$replaceWith`` +- ``$redact`` +- ``$set`` +- ``$unset`` + +Match Specific events Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example uses the ``pipeline`` parameter that includes a ``$match`` +to open a change stream that only records update operations: + +.. literalinclude:: /includes/read/change-streams.kt + :start-after: start-change-stream-pipeline + :end-before: end-change-stream-pipeline + :language: kotlin + :copyable: + :dedent: + +To learn more about modifying your change stream output, see the +:manual:`Modify Change Stream Output +` section in the {+mdb-server+} +manual. + +Modify watch() Behavior +----------------------- + +You can modify the ``watch()`` by chaining methods to the ``ChangeStreamIterable`` +object returned by the ``watch()`` method call. If you don't specify any options, the +driver does not customize the operation. + +The following table describes methods you can use to customize the behavior +of ``watch()``: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Method + - Description + + * - ``batchSize()`` + - | Sets the number of documents to return per batch. + + * - ``collation()`` + - | Specifies the kind of language collation to use when sorting + results. For more information, see :manual:`Collation ` + in the {+mdb-server+} manual. + + * - ``comment()`` + - | Specifies a comment to attach to the operation. + + * - ``fullDocument()`` + - | Sets the ``fullDocument`` value. To learn more, see the + :ref:`` section of this document. + + * - ``fullDocumentBeforeChange()`` + - | Sets the ``fullDocumentBeforeChange`` value. To learn more, see the + :ref:`` section of this document. + + * - ``maxAwaitTime()`` + - | Sets the maximum await execution time on the server for this operation, in + milliseconds. + +For a complete list of methods you can use to configure the ``watch()`` method, see +the `ChangeStreamIterable <{+api+}/com.mongodb.kotlin.client/-change-stream-iterable/index.html>`__ +API documentation. + +.. _kotlin-sync-change-stream-pre-post-image: + +Include Pre-Images and Post-Images +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. important:: + + You can enable pre-images and post-images on collections only if your + deployment uses MongoDB v6.0 or later. + +By default, when you perform an operation on a collection, the +corresponding change event includes only the delta of the fields +modified by that operation. To see the full document before or after a +change, chain the ``fullDocumentBeforeChange()`` or the ``fullDocument()`` +methods to the ``watch()`` method. + +The **pre-image** is the full version of a document *before* a change. To include the +pre-image in the change stream event, pass one of the following options to the +``fullDocumentBeforeChange()`` method: + +- ``FullDocumentBeforeChange.WHEN_AVAILABLE``: The change event includes a pre-image of the + modified document for change events only if the pre-image is available. +- ``FullDocumentBeforeChange.REQUIRED``: The change event includes a pre-image of the + modified document for change events. If the pre-image is not available, the + driver raises an error. + +The **post-image** is the full version of a document *after* a change. To include the +post-image in the change stream event, pass one of the following options to the +``fullDocument()`` method: + +- ``FullDocument.UPDATE_LOOKUP``: The change event includes a copy of the entire changed + document from some time after the change. +- ``FullDocument.WHEN_AVAILABLE``: The change event includes a post-image of the + modified document for change events only if the post-image is available. +- ``FullDocument.REQUIRED``: The change event includes a post-image of the + modified document for change events. If the post-image is not available, the + driver raises an error. + +The following example calls the ``watch()`` method on a collection and includes the post-image +of updated documents in the results by specifying the ``fullDocument`` parameter: + +.. literalinclude:: /includes/read/change-streams.kt + :start-after: start-change-stream-post-image + :end-before: end-change-stream-post-image + :language: kotlin + :copyable: + :dedent: + +With the change stream application running, updating a document in the +``restaurants`` collection by using the :ref:`preceding update example +` prints a change event resembling the following: + +.. code-block:: none + + ChangeStreamDocument{ operationType=update, resumeToken={"_data": "..."}, + namespace=sample_restaurants.restaurants, destinationNamespace=null, fullDocument=Restaurant(name=Blarney Castle, cuisine=Irish), + fullDocumentBeforeChange=null, documentKey={"_id": {"$oid": "..."}}, + clusterTime=Timestamp{value=..., seconds=..., inc=...}, + updateDescription=UpdateDescription{removedFields=[], updatedFields={"cuisine": "Irish"}, + truncatedArrays=[], disambiguatedPaths=null}, txnNumber=null, lsid=null, splitEvent=null, + wallTime=BsonDateTime{value=...}} + + +To learn more about pre-images and post-images, see +:manual:`Change Streams with Document Pre- and Post-Images ` +in the {+mdb-server+} manual. + +Additional Information +---------------------- + +To learn more about change streams, see :manual:`Change Streams +` in the {+mdb-server+} manual. + +API Documentation +~~~~~~~~~~~~~~~~~ + +To learn more about any of the methods or types discussed in this +guide, see the following API documentation: + +- `MongoClient.watch() <{+api+}/com.mongodb.kotlin.client/-mongo-client/watch.html>`__ +- `MongoDatabase.watch() <{+api+}/com.mongodb.kotlin.client/-mongo-database/watch.html>`__ +- `MongoCollection.watch() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/watch.html>`__ +- `ChangeStreamIterable <{+api+}/com.mongodb.kotlin.client/-change-stream-iterable/index.html>`__ \ No newline at end of file From ebfdafefc330e121b271dc21e77eddd7db17ee71 Mon Sep 17 00:00:00 2001 From: lindseymoore <71525840+lindseymoore@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:54:19 -0400 Subject: [PATCH 12/14] DOCSP-41121 Connect to Mongo Client (#34) * DOCSP-41121 Connect to MongoDB * reword * fix markdown errors * fix * change title * review comments * fix * fix pt 2 * add api doc section * fix connection string link * new option for connecting * revision * connection string link * fix toc * fix toc --- source/connect.txt | 6 +- source/connect/connection-options.txt | 21 ++++ source/connect/mongoclient.txt | 131 ++++++++++++++++++++++++ source/includes/connect/mongoclient.kt | 37 +++++++ source/includes/connect/mongoclient2.kt | 23 +++++ 5 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 source/connect/connection-options.txt create mode 100644 source/connect/mongoclient.txt create mode 100644 source/includes/connect/mongoclient.kt create mode 100644 source/includes/connect/mongoclient2.kt diff --git a/source/connect.txt b/source/connect.txt index 098d3e8..18e164d 100644 --- a/source/connect.txt +++ b/source/connect.txt @@ -22,11 +22,11 @@ Connect to MongoDB :titlesonly: :maxdepth: 1 - /connect/stable-api + /connect/mongoclient /connect/connection-targets + /connect/connection-options + /connect/stable-api -.. /connect/mongoclient -.. /connect/connection-options .. /connect/tls .. /connect/network-compression .. /connect/server-selection diff --git a/source/connect/connection-options.txt b/source/connect/connection-options.txt new file mode 100644 index 0000000..dc1dee1 --- /dev/null +++ b/source/connect/connection-options.txt @@ -0,0 +1,21 @@ +.. _kotlin-sync-connection-options: + +========================== +Specify Connection Options +========================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: connection string, URI, server, Atlas, settings, configure + +Overview +-------- \ No newline at end of file diff --git a/source/connect/mongoclient.txt b/source/connect/mongoclient.txt new file mode 100644 index 0000000..7c8da8d --- /dev/null +++ b/source/connect/mongoclient.txt @@ -0,0 +1,131 @@ +.. _kotlin-sync-mongoclient: + +==================== +Create a MongoClient +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: connection string, URI, server, Atlas, settings, client + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +To connect to a MongoDB deployment, you need two things: + +- A **connection URI**, also known as a *connection string*, which tells the {+driver-short+} + which MongoDB deployment to connect to. +- A **MongoClient** object, which creates the connection to and performs + operations on the MongoDB deployment. + +You can also use ``MongoClientSettings`` to customize the way the {+driver-short+} behaves +while connected to MongoDB. + +This guide shows you how to create a connection string and use a ``MongoClient`` object +to connect to MongoDB. + +.. _kotlin-sync-connection-uri: + +Connection URI +-------------- + +A standard connection string includes the following components: + +.. TODO, add this as last sentence for ``username:password`` description once a kotlin auth page is made: +.. For more information about the ``authSource`` connection option, see :ref:`kotlin-sync-auth`. + +.. list-table:: + :widths: 20 80 + :header-rows: 1 + + * - Component + - Description + + * - ``mongodb://`` + + - Required. A prefix that identifies this as a string in the + standard connection format. + + * - ``username:password`` + + - Optional. Authentication credentials. If you include these, the client + authenticates the user against the database specified in ``authSource``. + + * - ``host[:port]`` + + - Required. The host and optional port number where MongoDB is running. If you don't + include the port number, the driver uses the default port, ``27017``. + + * - ``/defaultauthdb`` + + - Optional. The authentication database to use if the + connection string includes ``username:password@`` + authentication credentials but not the ``authSource`` option. If you don't include + this component, the client authenticates the user against the ``admin`` database. + + * - ``?`` + + - Optional. A query string that specifies connection-specific + options as ``=`` pairs. See + :ref:`kotlin-sync-connection-options` for a full description of + these options. + +For more information about creating a connection string, see +:manual:`Connection Strings ` in the +MongoDB Server documentation. + +Atlas Connection Example +------------------------ + +To connect to a MongoDB deployment on Atlas, you must first create a client. + +You can pass a connection URI as a string to the ``MongoClient.create()`` method +to connect to a MongoDB instance: + +.. literalinclude:: /includes/connect/mongoclient2.kt + :start-after: start-connect-to-atlas-w-uri + :end-before: end-connect-to-atlas-w-uri + :language: kotlin + :copyable: + :dedent: + +You can also create a client with your desired configurations by passing a +``MongoClientSettings`` object to the ``MongoClient.create()`` method. + +To instantiate a ``MongoClientSettings`` object, use the builder method to +specify your connection string, using the ``applyConnectionString()`` method, +and any other client options. Once you have your desired configuration, +call the ``build()`` method. + +You can set the Stable API version client option to avoid breaking changes when +you upgrade to a new server version. To learn more about the Stable API feature, +see the :ref:`Stable API page `. + +The following code shows how you can specify the connection string and the +Stable API client option when connecting to a MongoDB deployment on Atlas +and verify that the connection is successful: + +.. literalinclude:: /includes/connect/mongoclient.kt + :start-after: start-connect-to-atlas + :end-before: end-connect-to-atlas + :language: kotlin + :copyable: + :dedent: + +API Documentation +----------------- + +For more information about creating a ``MongoClient`` object with the +{+driver-short+}, see the following API documentation: + +- `MongoClient <{+api+}/mongodb-driver-kotlin-sync/com.mongodb.kotlin.client/-mongo-client/index.html>`__ +- `MongoClientSettings <{+core-api+}com/mongodb/MongoClientSettings.html>`__ diff --git a/source/includes/connect/mongoclient.kt b/source/includes/connect/mongoclient.kt new file mode 100644 index 0000000..de08d1d --- /dev/null +++ b/source/includes/connect/mongoclient.kt @@ -0,0 +1,37 @@ +import com.mongodb.* +import com.mongodb.kotlin.client.MongoClient +import org.bson.BsonInt64 +import org.bson.Document + +fun main() { + + // start-connect-to-atlas + // start-connect-to-atlas-w-uri + // Replace the placeholder with your Atlas connection string + val uri = "" + val mongoClient1 = MongoClient.create(uri) + // end-connect-to-atlas-w-uri + + // Construct a ServerApi instance using the ServerApi.builder() method + val serverApi = ServerApi.builder() + .version(ServerApiVersion.V1) + .build() + val settings = MongoClientSettings.builder() + .applyConnectionString(ConnectionString(uri)) + .serverApi(serverApi) + .build() + + // Create a new client and connect to the server + val mongoClient = MongoClient.create(settings) + val database = mongoClient.getDatabase("sample_mflix") + + try { + // Send a ping to confirm a successful connection + val command = Document("ping", BsonInt64(1)) + val commandResult = database.runCommand(command) + println("Pinged your deployment. You successfully connected to MongoDB!") + } catch (me: MongoException) { + System.err.println(me) + } + // end-connect-to-atlas +} diff --git a/source/includes/connect/mongoclient2.kt b/source/includes/connect/mongoclient2.kt new file mode 100644 index 0000000..299be06 --- /dev/null +++ b/source/includes/connect/mongoclient2.kt @@ -0,0 +1,23 @@ +import com.mongodb.* +import com.mongodb.kotlin.client.MongoClient + +fun main() { + + // start-connect-to-atlas-w-uri + // Replace the placeholder with your Atlas connection string + val uri = "" + + // Create a new client and connect to the server + val mongoClient = MongoClient.create(uri) + val database = mongoClient.getDatabase("sample_mflix") + // end-connect-to-atlas-w-uri + + try { + // Send a ping to confirm a successful connection + val command = Document("ping", BsonInt64(1)) + val commandResult = database.runCommand(command) + println("Pinged your deployment. You successfully connected to MongoDB!") + } catch (me: MongoException) { + System.err.println(me) + } +} From d45f795fdf99b798d819941de4dabd0cc56d0ab0 Mon Sep 17 00:00:00 2001 From: lindseymoore <71525840+lindseymoore@users.noreply.github.com> Date: Fri, 16 Aug 2024 16:44:16 -0400 Subject: [PATCH 13/14] DOCSP-41116 What's New (#38) * what's new * go through links * add to toc * review comments * oidc todo --- source/index.txt | 1 + source/whats-new.txt | 142 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 1 deletion(-) diff --git a/source/index.txt b/source/index.txt index e4159fb..b65d44f 100644 --- a/source/index.txt +++ b/source/index.txt @@ -27,6 +27,7 @@ /issues-and-help /compatibility Validate Driver Artifact Signatures + /whats-new View the Source API Documentation <{+api+}/com.mongodb.kotlin.client/index.html> diff --git a/source/whats-new.txt b/source/whats-new.txt index 8a5cb77..a5743df 100644 --- a/source/whats-new.txt +++ b/source/whats-new.txt @@ -4,4 +4,144 @@ What's New ========== -.. TODO \ No newline at end of file +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +Learn what's new in: + +* :ref:`Version 5.1.3 ` +* :ref:`Version 5.1.2 ` +* :ref:`Version 5.1.1 ` +* :ref:`Version 5.1 ` +* :ref:`Version 5.0 ` + +.. _kotlin-sync-version-5.1.3: + +What's New in 5.1.3 +------------------- + +The 5.1.3 driver patch release includes the following changes: + +.. sharedinclude:: dbx/jvm/v5.1.3-wn-items.rst + +.. _kotlin-sync-version-5.1.2: + +What's New in 5.1.2 +------------------- + +The 5.1.2 driver patch release includes the following changes: + +- Support for encoding Kotlin data classes with nullable + generic parameter types. For example, you can encode the ``Container`` class + in the following code: + + .. code-block:: kotlin + + @Serializable + data class Box( + val boxed: T + ) + + @Serializable + data class Container( + val box: Box + ) + +.. _kotlin-sync-version-5.1.1: + +What's New in 5.1.1 +------------------- + +The 5.1.1 driver patch release includes the following changes: + +- When using the ``MONGODB-OIDC`` authentication mechanism, you must not + include comma characters in the ``authMechanismProperties`` connection + string value. + +.. TODO: Link to OIDC content once completed +.. ex. To learn more about this behavior, see the + :ref:`MONGODB-OIDC ` section of the Enterprise + Authentication guide. + +.. _kotlin-sync-version-5.1: + +What's New in 5.1 +----------------- + +.. warning:: Deprecations in this release + + To avoid breaking changes in future major releases of the driver, + replace any application code that depends on deprecated program elements. + +This section includes the following information: + +- :ref:`kotlin-sync-deprecations-5.1` +- :ref:`kotlin-sync-improvements-5.1` +- :ref:`kotlin-sync-new-features-5.1` + +.. _kotlin-sync-deprecations-5.1: + +Deprecations in 5.1 +~~~~~~~~~~~~~~~~~~~ + +- Support for {+mdb-server+} v3.6 is deprecated and will be removed in the + next driver version release. To learn how to upgrade your {+mdb-server+} + deployment, see :manual:`Release Notes ` in the {+mdb-server+} + manual. + +.. _kotlin-sync-improvements-5.1: + +Improvements in 5.1 +~~~~~~~~~~~~~~~~~~~ + +- Internal testing of GraalVM native image technology. These tests involve building + native applications by using the GraalVM native-image tool. + +- Enhanced support for the ``MONGODB-OIDC`` authentication mechanism. + +.. TODO: Link to OIDC content once completed +.. ex. To learn more about OIDC, see the + :ref:`MONGODB-OIDC ` section of the Enterprise + Authentication guide. + +- Fixes an issue in which operations used the incorrect codec when using + a polymorphic ``MongoCollection`` instance. This ensures that + discriminator information is not lost when using ``bson-kotlinx``. + +- Fixes an issue in which the class discriminator was the first field + when decoding, resulting in field type errors when using a polymorphic + ``MongoCollection`` instance. + +.. _kotlin-sync-new-features-5.1: + +New Features in 5.1 +~~~~~~~~~~~~~~~~~~~ + +- Support for polymorphic serialization. + +.. TODO: Add link to Kotlin Serialization page when ready: https://jira.mongodb.org/browse/DOCSP-42666 . +.. ex. To learn more, see the :ref:`kotlin-sync-polymorphic` section of the Kotlin Sync Serialization guide. + +- Introduces the ``serverMonitoringMode`` connection URI option. To + learn more, see the :ref:`kotlin-sync-connection-options` guide. + +.. _kotlin-sync-version-5.0: + +What's New in 5.0 +----------------- + +New features of the 5.0 driver release include: + +- The ``KotlinSerializerCodecProvider`` constructor now accepts + ``serializersModule`` and ``bsonConfiguration`` objects: + + .. code-block:: kotlin + + KotlinSerializerCodec.create(clazz.kotlin, serializersModule=serializersModule, bsonConfiguration=bsonConfiguration) + + This makes it easier to customize your configuration. + +- Fixes a Kotlin reflection bug that resulted in container type erasure. From 626ef2e2e28776c4f54c5c8bd9027c23dd156c23 Mon Sep 17 00:00:00 2001 From: Michael Morisi Date: Wed, 21 Aug 2024 08:59:45 -0400 Subject: [PATCH 14/14] DOCSP-41508: TLS (#37) --- source/connect.txt | 9 +- source/connect/tls.txt | 329 +++++++++++++++++++++++++++++++++ source/includes/connect/tls.kt | 43 +++++ 3 files changed, 377 insertions(+), 4 deletions(-) create mode 100644 source/connect/tls.txt create mode 100644 source/includes/connect/tls.kt diff --git a/source/connect.txt b/source/connect.txt index 18e164d..be07e6c 100644 --- a/source/connect.txt +++ b/source/connect.txt @@ -25,9 +25,9 @@ Connect to MongoDB /connect/mongoclient /connect/connection-targets /connect/connection-options + /connect/tls /connect/stable-api -.. /connect/tls .. /connect/network-compression .. /connect/server-selection .. /connect/csot @@ -102,7 +102,8 @@ Transport Layer Security (TLS) ------------------------------ The following sections describe how to connect to MongoDB -while enabling the TLS protocol. +while enabling the TLS protocol. To learn more about using TLS with the {+driver-short+}, +see :ref:`kotlin-sync-tls`. Enable TLS ~~~~~~~~~~ @@ -111,8 +112,8 @@ The following tabs demonstrate how to enable TLS on a connection: .. include:: /includes/connect/tls-tabs.rst -.. To learn more about enabling TLS, see :ref:`kotlin-sync-enable-tls` in -.. the TLS configuration guide. +To learn more about enabling TLS, see :ref:`kotlin-sync-tls-enable` in +the TLS configuration guide. Disable Hostname Verification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/source/connect/tls.txt b/source/connect/tls.txt new file mode 100644 index 0000000..9e9556c --- /dev/null +++ b/source/connect/tls.txt @@ -0,0 +1,329 @@ +.. _kotlin-sync-tls: + +========================== +Enable TLS on a Connection +========================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, security, authentication + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to use the +:wikipedia:`TLS ` +security protocol when connecting to MongoDB by using the {+driver-short+}. + +.. _kotlin-sync-tls-enable: + +Enable TLS +---------- + +You can enable TLS on a connection to your MongoDB instance +in the following ways: + +- Setting the ``tls`` parameter in your connection string +- Using the ``enabled()`` method from the ``SslSettings.Builder`` class when creating a + ``MongoClientSettings`` instance + +.. note:: DNS Seedlist Protocol Enables TLS + + If you connect by using the DNS seedlist protocol, indicated by the + ``mongodb+srv`` prefix in your connection string, the driver + automatically enables TLS. + + To learn more about connection behavior when you use a DNS seedlist, + see the :manual:`SRV Connection Format ` + section of the Connection Strings guide in the Server manual. + +.. tabs:: + + .. tab:: Connection String + :tabid: connectionstring + + To enable TLS on a connection by using a connection string, set the + ``tls`` option to ``true`` in the options parameter and pass the string to + ``MongoClient.create()``, as shown in the following code: + + .. literalinclude:: /includes/connect/tls.kt + :start-after: start-tls-connection-string + :end-before: end-tls-connection-string + :language: kotlin + :copyable: + :dedent: + + .. tab:: MongoClientSettings + :tabid: mongoclientsettings + + To enable TLS within a ``MongoClientSettings`` instance, use the + ``applyToSslSettings()`` builder method. Set the ``enabled`` property to ``true`` + in the ``SslSettings.Builder`` block, as shown in the following code: + + .. literalinclude:: /includes/connect/tls.kt + :start-after: start-tls-mongo-client-settings + :end-before: end-tls-mongo-client-settings + :language: kotlin + :copyable: + :dedent: + +.. note:: Debugging TLS + + If you experience trouble setting up your TLS connection, you can + use the ``-Djavax.net.debug=all`` system property to view helpful + log statements. See `Debugging SSL/TLS connections + `__ + in the Java language documentation for more information. + +.. _tls_configure-certificates: + +Configure Certificates +---------------------- + +{+language+} applications that initiate TLS requests require access to +cryptographic certificates that prove the application's identity and verify +other applications with which the {+language+} application interacts. You can configure +access to these certificates in your application in the following ways: + +- Using a JVM trust store and JVM key store +- Using a client-specific trust store and key store + +.. _kotlin-sync-tls-configure-jvm-truststore: + +Configure the JVM Trust Store +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + By default, the JRE includes many commonly used public certificates + from signing authorities such as `Let's Encrypt + `__. As a result, you can enable TLS when connecting to a + :atlas:`MongoDB Atlas ` instance, or any other + server whose certificate is signed by an authority in the JRE's default + certificate store, with TLS enabled without configuring the trust store. + +The JVM trust store saves certificates that securely identify other +applications with which your {+language+} application interacts. By using these +certificates, your application can prove that the connection to another +application is genuine and secure from tampering by third parties. + +If your MongoDB instance uses a certificate that is signed by an +authority that is not present in the JRE's default certificate store, +your application must configure the following system properties to initiate +TLS requests. + +- ``javax.net.ssl.trustStore``: Path to a trust store containing the client's TLS + certificates + +- ``javax.net.ssl.trustStorePassword``: Password to access the trust + store defined in ``javax.net.ssl.trustStore`` + +These properties ensure that your application can +validate the TLS certificate presented by a connected MongoDB instance. + +You can create a trust store by using the `keytool `__ +command line tool from the JDK as shown in the following terminal command: + +.. code-block:: console + + keytool -importcert -trustcacerts -file + -keystore -storepass + +Configure the JVM Key Store +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + By default, MongoDB instances do not perform client certificate + validation. You must configure the key store if you configured your MongoDB + instance to validate client certificates. + +An application that initiates TLS requests must set the following JVM system +properties to ensure that the client presents a TLS certificate to +the MongoDB server: + +- ``javax.net.ssl.keyStore``: Path to a key store containing the client's + TLS/SSL certificates + +- ``javax.net.ssl.keyStorePassword``: Password to access the key store + defined in ``javax.net.ssl.keyStore`` + +You can create a key store by using the `keytool +`__ +or `openssl `__ +command line tool. + +To learn more about configuring a {+language+} application to use TLS, +see the `JSSE Reference Guide `__ +in the Java language documentation. + +.. _tls-disable-hostname-verification: + +Configure a Client-Specific Trust Store and Key Store +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can configure a client-specific trust store and key store by using the +``init()`` method of the ``SSLContext`` class. + +Find an example showing how to configure a client to use an ``SSLContext`` +instance in the :ref:`` section of this guide. + +Disable Hostname Verification +----------------------------- + +By default, the driver ensures that the hostname included in the server's +TLS certificates matches the hostnames provided when constructing +a ``MongoClient``. To disable hostname verification for your +application, set the ``invalidHostNameAllowed`` property of the builder to ``true`` in the +``applytoSslSettings()`` builder block: + +.. literalinclude:: /includes/connect/tls.kt + :start-after: start-disable-hostname-verification + :end-before: end-disable-hostname-verification + :language: kotlin + :copyable: + :dedent: + +.. warning:: + + Disabling hostname verification makes your application insecure and potentially + vulnerable to expired certificates and foreign processes posing as valid client + instances. + +.. _kotlin-sync-tls-restrict-tls-1.2: + +Restrict Connections to TLS 1.2 Only +------------------------------------ + +To restrict your application to use only the TLS 1.2 protocol, set the +``jdk.tls.client.protocols`` system property to ``"TLSv1.2"``. + +.. note:: + + Java Runtime Environments (JREs) before Java 8 only enabled + the TLS 1.2 protocol in update releases. If your JRE has not enabled + the TLS 1.2 protocol, upgrade to a later release to connect by using + TLS 1.2. + +.. _kotlin-sync-tls-custom-sslContext: + +Customize TLS Configuration through the Java SE SSLContext +---------------------------------------------------------- + +If your TLS configuration requires customization, you can +set the ``sslContext`` property of your ``MongoClient`` by +passing an `SSLContext +`__ +object to the ``context()`` method builder in the ``applyToSslSettings()`` block: + +.. literalinclude:: /includes/connect/tls.kt + :start-after: start-ssl-context + :end-before: end-ssl-context + :language: kotlin + :copyable: + :dedent: + +For more information on the ``SSLContext`` class, see the API +documentation for `SSL Context `__. + +Online Certificate Status Protocol (OCSP) +----------------------------------------- + +OCSP is a standard used to check whether X.509 certificates have been +revoked. A certificate authority can add an X.509 certificate to the +Certificate Revocation List (CRL) before the expiry time to invalidate +the certificate. When a client sends an X.509 certificate during the TLS +handshake, the CA's revocation server checks the CRL and returns a status +of ``good``, ``revoked``, or ``unknown``. + +The driver supports the following variations of OCSP: + +- Client-Driven OCSP +- OCSP Stapling + +The following sections describe the differences between them and how to enable +them for your application. + +.. note:: + + The {+driver-short+} uses the JVM arguments configured for the application + and cannot be overridden for a specific ``MongoClient`` instance. + +Client-Driven OCSP +~~~~~~~~~~~~~~~~~~ + +In client-driven OCSP, the client sends the certificate in an OCSP request to +an OCSP responder after receiving the certificate from the server. The OCSP +responder checks the status of the certificate with a certificate +authority (CA) and reports whether it's valid in a response sent to the +client. + +To enable client-driven OCSP for your application, set the following JVM +system properties: + +.. list-table:: + :header-rows: 1 + :widths: 35 65 + + * - Property + - Value + + * - ``com.sun.net.ssl.checkRevocation`` + - Set this property to ``true`` to enable revocation checking. + + * - ``ocsp.enable`` + - Set this property to ``true`` to enable client-driven OCSP. + +.. warning:: + + If the OCSP responder is unavailable, the TLS support provided by the + JDK reports a "hard fail". This differs from the "soft fail" behavior of + the MongoDB Shell and some other drivers. + +OCSP Stapling +~~~~~~~~~~~~~ + +OCSP stapling is a mechanism in which the server must obtain the signed +certificate from the certificate authority (CA) and include it in a +time-stamped OCSP response to the client. + +To enable OCSP stapling for your application, set the following JVM system +properties: + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Property + - Description + + * - ``com.sun.net.ssl.checkRevocation`` + - Set this property to ``true`` to enable revocation checking. + + * - ``jdk.tls.client.enableStatusRequestExtension`` + - | Set this property to ``true`` to enable OCSP stapling. + | + | If unset or set to ``false``, the connection can proceed regardless of the presence or status of the certificate revocation response. + +For more information about OCSP, check out the following resources: + +- Oracle JDK 8 Documentation on `how to enable OCSP for an application `__ +- :rfc:`Official IETF specification for OCSP (RFC 6960) <6960>` + +API Documentation +----------------- + +For more information about any of the methods or types discussed in this guide, +see the following API documentation: + +- `ConnectionString <{+core-api+}/com/mongodb/ConnectionString.html>`__ +- `MongoClientSettings <{+core-api+}/com/mongodb/MongoClientSettings.html>`__ \ No newline at end of file diff --git a/source/includes/connect/tls.kt b/source/includes/connect/tls.kt new file mode 100644 index 0000000..31ca517 --- /dev/null +++ b/source/includes/connect/tls.kt @@ -0,0 +1,43 @@ +import com.mongodb.ConnectionString +import com.mongodb.MongoClientSettings +import com.mongodb.kotlin.client.MongoClient +import javax.net.ssl.SSLContext + +fun main() { + // start-tls-connection-string + val mongoClient = MongoClient.create("mongodb+srv://:@/?tls=true") + // end-tls-connection-string + + // start-tls-mongo-client-settings + val settings = MongoClientSettings.builder() + .applyConnectionString(ConnectionString("")) + .applyToSslSettings { builder -> + builder.enabled(true) + } + .build() + val mongoClient = MongoClient.create(settings) + // end-tls-mongo-client-settings + + // start-disable-hostname-verification + val settings = MongoClientSettings.builder() + .applyConnectionString(ConnectionString("")) + .applyToSslSettings { builder -> + builder.enabled(true) + builder.invalidHostNameAllowed(true) + } + .build() + val mongoClient = MongoClient.create(settings); + // end-disable-hostname-verification + + // start-ssl-context + val sslContext = SSLContext.getDefault() + + val settings = MongoClientSettings.builder() + .applyToSslSettings { builder -> + builder.enabled(true) + builder.context(sslContext) + } + .build() + val mongoClient = MongoClient.create(settings); + // end-ssl-context +} \ No newline at end of file