diff --git a/driver-api/src/commonMain/kotlin/MongoCollection.kt b/driver-api/src/commonMain/kotlin/MongoCollection.kt index c45e55b6..5e1731c1 100644 --- a/driver-api/src/commonMain/kotlin/MongoCollection.kt +++ b/driver-api/src/commonMain/kotlin/MongoCollection.kt @@ -47,6 +47,31 @@ import kotlin.reflect.KType * The maximum nesting is 100 levels. * Each document or array adds a level. * + * ### Operations + * + * The following lists the available operations using the `mongosh` equivalent: + * + * - [aggregate][AggregationOperations.aggregate] + * - [bulkWrite][UpdateOperations.bulkWrite] + * - [countDocuments][CountOperations.count] + * - [deleteOne][DeleteOperations.deleteOne] + * - [deleteMany][DeleteOperations.deleteMany] + * - [drop][CollectionOperations.drop] + * - [estimatedDocumentCount][CountOperations.countEstimated] + * - [find][FindOperations.find] + * - [findOne][FindOperations.findOne] + * - [findOneAndUpdate][UpdateOperations.findOneAndUpdate] + * - [insertOne][InsertOperations.insertOne] + * - [insertMany][InsertOperations.insertMany] + * - [updateOne][UpdateOperations.updateOne] + * - [updateOne][UpdatePipelineOperations.updateOneWithPipeline] with an aggregation pipeline + * - [updateOne][UpdateOperations.upsertOne] with `upsert: true` + * - [updateOne][UpdatePipelineOperations.upsertOneWithPipeline] with `upsert: true` and an aggregation pipeline + * - [updateMany][UpdateOperations.updateMany] + * - [updateMany][UpdatePipelineOperations.updateManyWithPipeline] with an aggregation pipeline + * - [replaceOne][UpdateOperations.replaceOne] + * - [replaceOne][UpdateOperations.repsertOne] with `upsert: true` + * * ### External resources * * - [Official documentation](https://www.mongodb.com/docs/manual/core/databases-and-collections/) -- 2.54.0 From c753689d21df82eaf96c3453e10e146423bb2131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Mon, 10 Aug 2026 22:26:30 +0200 Subject: [PATCH 2/6] feat(driver-api): Missing specialization of aggregate() --- .../src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt | 3 +-- .../src/jvmMain/kotlin/CoroutineMongoCollection.kt | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt index 42ccc10e..93183cdf 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt @@ -16,7 +16,6 @@ package opensavvy.ktmongo.coroutines -import opensavvy.ktmongo.api.MongoAggregationPipeline import opensavvy.ktmongo.api.MongoCollection import opensavvy.ktmongo.api.MongoIterable import opensavvy.ktmongo.api.operations.UpdateOperations @@ -85,7 +84,7 @@ private class CoroutineFilteredMongoCollectionImpl( update = update ) - override fun aggregate(): MongoAggregationPipeline = + override fun aggregate(): CoroutineMongoAggregationPipeline = upstream.aggregate() .match { globalFilter() } diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt index 3a048e5a..6e2e1f59 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt @@ -92,4 +92,5 @@ interface CoroutineMongoCollection : MongoCollection { update: UpdateWithPipelineQuery.() -> Unit, ): UpsertResult + override fun aggregate(): CoroutineMongoAggregationPipeline } -- 2.54.0 From 5ba9699764b5f6d17828b781d670052c41c13a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Mon, 10 Aug 2026 22:29:21 +0200 Subject: [PATCH 3/6] feat(driver-api): Missing specialization of lookup() --- .../jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt | 7 +++---- .../kotlin/CoroutineMongoAggregationPipelineImpl.kt | 9 +++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt index d13b4325..7c21b6bc 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt @@ -22,10 +22,7 @@ package opensavvy.ktmongo.coroutines import opensavvy.ktmongo.api.MongoAggregationPipeline import opensavvy.ktmongo.dsl.LowLevelApi import opensavvy.ktmongo.dsl.aggregation.AccumulationOperators -import opensavvy.ktmongo.dsl.aggregation.stages.HasUnionWithCompatibility -import opensavvy.ktmongo.dsl.aggregation.stages.ProjectStageOperators -import opensavvy.ktmongo.dsl.aggregation.stages.SetStageOperators -import opensavvy.ktmongo.dsl.aggregation.stages.UnsetStageOperators +import opensavvy.ktmongo.dsl.aggregation.stages.* import opensavvy.ktmongo.dsl.options.SortOptionDsl import opensavvy.ktmongo.dsl.path.Field import opensavvy.ktmongo.dsl.query.FilterQuery @@ -70,6 +67,8 @@ interface CoroutineMongoAggregationPipeline : MongoAggregationPi override fun unionWith(other: HasUnionWithCompatibility): CoroutineMongoAggregationPipeline + override fun lookup(block: LookupStageOperators.() -> Unit): CoroutineMongoAggregationPipeline + override fun group(block: AccumulationOperators.() -> Unit): CoroutineMongoAggregationPipeline override fun countTo(field: Field): CoroutineMongoAggregationPipeline diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt index 50757f44..62a0d175 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt @@ -29,10 +29,7 @@ import opensavvy.ktmongo.dsl.aggregation.AbstractPipeline import opensavvy.ktmongo.dsl.aggregation.AccumulationOperators import opensavvy.ktmongo.dsl.aggregation.AggregationPipeline import opensavvy.ktmongo.dsl.aggregation.PipelineChainLink -import opensavvy.ktmongo.dsl.aggregation.stages.HasUnionWithCompatibility -import opensavvy.ktmongo.dsl.aggregation.stages.ProjectStageOperators -import opensavvy.ktmongo.dsl.aggregation.stages.SetStageOperators -import opensavvy.ktmongo.dsl.aggregation.stages.UnsetStageOperators +import opensavvy.ktmongo.dsl.aggregation.stages.* import opensavvy.ktmongo.dsl.options.SortOptionDsl import opensavvy.ktmongo.dsl.path.Field import opensavvy.ktmongo.dsl.query.FilterQuery @@ -120,6 +117,10 @@ private class CoroutineMongoAggregationPipelineImpl @OptIn(LowLe override fun unionWith(other: HasUnionWithCompatibility): CoroutineMongoAggregationPipelineImpl = super.unionWith(other) as CoroutineMongoAggregationPipelineImpl + @KtMongoDsl + override fun lookup(block: LookupStageOperators.() -> Unit): CoroutineMongoAggregationPipelineImpl = + super.lookup(block) as CoroutineMongoAggregationPipelineImpl + @KtMongoDsl override fun group(block: AccumulationOperators.() -> Unit): CoroutineMongoAggregationPipelineImpl = super.group(block) as CoroutineMongoAggregationPipelineImpl -- 2.54.0 From a75a15bf84982c32f1e55289c82559f993bf0d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Mon, 10 Aug 2026 22:33:43 +0200 Subject: [PATCH 4/6] feat(driver-api): Missing specialization of matchExpr() --- .../jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt | 4 ++++ .../kotlin/CoroutineMongoAggregationPipelineImpl.kt | 9 +++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt index 7c21b6bc..a26eb8bf 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipeline.kt @@ -22,6 +22,8 @@ package opensavvy.ktmongo.coroutines import opensavvy.ktmongo.api.MongoAggregationPipeline import opensavvy.ktmongo.dsl.LowLevelApi import opensavvy.ktmongo.dsl.aggregation.AccumulationOperators +import opensavvy.ktmongo.dsl.aggregation.AggregationOperators +import opensavvy.ktmongo.dsl.aggregation.Value import opensavvy.ktmongo.dsl.aggregation.stages.* import opensavvy.ktmongo.dsl.options.SortOptionDsl import opensavvy.ktmongo.dsl.path.Field @@ -51,6 +53,8 @@ interface CoroutineMongoAggregationPipeline : MongoAggregationPi override fun match(filter: FilterQuery.() -> Unit): CoroutineMongoAggregationPipeline + override fun matchExpr(filter: AggregationOperators.() -> Value): CoroutineMongoAggregationPipeline + override fun sample(size: Int): CoroutineMongoAggregationPipeline override fun set(block: SetStageOperators.() -> Unit): CoroutineMongoAggregationPipeline diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt index 62a0d175..f13a651f 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt @@ -25,10 +25,7 @@ import opensavvy.ktmongo.dsl.BsonContext import opensavvy.ktmongo.dsl.DangerousMongoApi import opensavvy.ktmongo.dsl.KtMongoDsl import opensavvy.ktmongo.dsl.LowLevelApi -import opensavvy.ktmongo.dsl.aggregation.AbstractPipeline -import opensavvy.ktmongo.dsl.aggregation.AccumulationOperators -import opensavvy.ktmongo.dsl.aggregation.AggregationPipeline -import opensavvy.ktmongo.dsl.aggregation.PipelineChainLink +import opensavvy.ktmongo.dsl.aggregation.* import opensavvy.ktmongo.dsl.aggregation.stages.* import opensavvy.ktmongo.dsl.options.SortOptionDsl import opensavvy.ktmongo.dsl.path.Field @@ -85,6 +82,10 @@ private class CoroutineMongoAggregationPipelineImpl @OptIn(LowLe override fun match(filter: FilterQuery.() -> Unit): CoroutineMongoAggregationPipelineImpl = super.match(filter) as CoroutineMongoAggregationPipelineImpl + @KtMongoDsl + override fun matchExpr(filter: AggregationOperators.() -> Value): CoroutineMongoAggregationPipelineImpl = + super.matchExpr(filter) as CoroutineMongoAggregationPipelineImpl + @KtMongoDsl override fun sample(size: Int): CoroutineMongoAggregationPipelineImpl = super.sample(size) as CoroutineMongoAggregationPipelineImpl -- 2.54.0 From 70db58e5e6a10b88b0d13e15e7e02b4f116c4e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Mon, 10 Aug 2026 22:34:59 +0200 Subject: [PATCH 5/6] test(test-legacy): Migrate to the unified API --- driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt | 8 ++++---- test-legacy/src/commonMain/kotlin/TestDatabase.kt | 6 +++--- test-legacy/src/commonTest/kotlin/AggregationTests.kt | 5 ++--- .../src/commonTest/kotlin/FilteredCollectionTest.kt | 1 - .../src/commonTest/kotlin/studies/AggregationLookup.kt | 2 +- .../kotlin/studies/AggregationProjectUnionWith.kt | 8 ++++---- test-legacy/src/jvmMain/kotlin/TestDatabase.jvm.kt | 8 ++++---- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt b/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt index 9c07e73b..1bb057d3 100644 --- a/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt +++ b/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt @@ -17,8 +17,8 @@ package opensavvy.ktmongo.coroutines.kmongo import com.mongodb.reactivestreams.client.MongoCollection -import opensavvy.ktmongo.coroutines.JvmMongoCollection -import opensavvy.ktmongo.coroutines.asKtMongoLegacy +import opensavvy.ktmongo.coroutines.CoroutineMongoCollection +import opensavvy.ktmongo.coroutines.asKtMongo import opensavvy.ktmongo.dsl.path.PropertyNameStrategy import opensavvy.ktmongo.utils.kmongo.KMongoNameStrategy @@ -27,5 +27,5 @@ import opensavvy.ktmongo.utils.kmongo.KMongoNameStrategy */ inline fun MongoCollection.asKtMongo( nameStrategy: PropertyNameStrategy = KMongoNameStrategy(), -): JvmMongoCollection = - com.mongodb.kotlin.client.coroutine.MongoCollection(this).asKtMongoLegacy(nameStrategy) +): CoroutineMongoCollection = + com.mongodb.kotlin.client.coroutine.MongoCollection(this).asKtMongo(propertyNameStrategy = nameStrategy) diff --git a/test-legacy/src/commonMain/kotlin/TestDatabase.kt b/test-legacy/src/commonMain/kotlin/TestDatabase.kt index 2028f6da..8bcf9bc1 100644 --- a/test-legacy/src/commonMain/kotlin/TestDatabase.kt +++ b/test-legacy/src/commonMain/kotlin/TestDatabase.kt @@ -17,17 +17,17 @@ package opensavvy.ktmongo.test import kotlinx.coroutines.ensureActive -import opensavvy.ktmongo.coroutines.MongoCollection +import opensavvy.ktmongo.coroutines.CoroutineMongoCollection import opensavvy.prepared.suite.PreparedProvider import opensavvy.prepared.suite.cleanUp import opensavvy.prepared.suite.prepared import opensavvy.prepared.suite.random.randomInt import kotlin.coroutines.coroutineContext -expect inline fun testCollectionExact(name: String): PreparedProvider> +expect inline fun testCollectionExact(name: String): PreparedProvider> val collectionPostfix by randomInt(0, Int.MAX_VALUE) -inline fun testCollection(name: String): PreparedProvider> = prepared { +inline fun testCollection(name: String): PreparedProvider> = prepared { val name = "$name-${collectionPostfix()}" val realCollection by testCollectionExact(name) diff --git a/test-legacy/src/commonTest/kotlin/AggregationTests.kt b/test-legacy/src/commonTest/kotlin/AggregationTests.kt index ceb347f9..a45bd6d3 100644 --- a/test-legacy/src/commonTest/kotlin/AggregationTests.kt +++ b/test-legacy/src/commonTest/kotlin/AggregationTests.kt @@ -19,9 +19,8 @@ package opensavvy.ktmongo.sync import kotlinx.serialization.Serializable -import opensavvy.ktmongo.coroutines.filter -import opensavvy.ktmongo.coroutines.first -import opensavvy.ktmongo.coroutines.toList +import opensavvy.ktmongo.api.first +import opensavvy.ktmongo.api.toList import opensavvy.ktmongo.dsl.DangerousMongoApi import opensavvy.ktmongo.dsl.LowLevelApi import opensavvy.ktmongo.test.testCollection diff --git a/test-legacy/src/commonTest/kotlin/FilteredCollectionTest.kt b/test-legacy/src/commonTest/kotlin/FilteredCollectionTest.kt index 6a68b016..814ee5f4 100644 --- a/test-legacy/src/commonTest/kotlin/FilteredCollectionTest.kt +++ b/test-legacy/src/commonTest/kotlin/FilteredCollectionTest.kt @@ -17,7 +17,6 @@ package opensavvy.ktmongo.sync import kotlinx.serialization.Serializable -import opensavvy.ktmongo.coroutines.filter import opensavvy.ktmongo.test.testCollection import opensavvy.prepared.runner.testballoon.preparedSuite import opensavvy.prepared.suite.config.CoroutineTimeout diff --git a/test-legacy/src/commonTest/kotlin/studies/AggregationLookup.kt b/test-legacy/src/commonTest/kotlin/studies/AggregationLookup.kt index 3b8fa30a..f92e9923 100644 --- a/test-legacy/src/commonTest/kotlin/studies/AggregationLookup.kt +++ b/test-legacy/src/commonTest/kotlin/studies/AggregationLookup.kt @@ -19,8 +19,8 @@ package opensavvy.ktmongo.sync.studies import kotlinx.serialization.Serializable +import opensavvy.ktmongo.api.toList import opensavvy.ktmongo.bson.types.ObjectId -import opensavvy.ktmongo.coroutines.toList import opensavvy.ktmongo.test.testCollection import opensavvy.prepared.runner.testballoon.preparedSuite import kotlin.time.Instant diff --git a/test-legacy/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt b/test-legacy/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt index 0706820c..cc6f05e5 100644 --- a/test-legacy/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt +++ b/test-legacy/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt @@ -17,8 +17,8 @@ package opensavvy.ktmongo.sync.studies import kotlinx.serialization.Serializable -import opensavvy.ktmongo.coroutines.MongoAggregationPipeline -import opensavvy.ktmongo.coroutines.toList +import opensavvy.ktmongo.api.toList +import opensavvy.ktmongo.coroutines.CoroutineMongoAggregationPipeline import opensavvy.ktmongo.dsl.aggregation.stages.ProjectStageOperators import opensavvy.ktmongo.dsl.query.FilterQuery import opensavvy.ktmongo.test.testCollection @@ -201,7 +201,7 @@ val AggregationProjectUnionWith by preparedSuite { * const commonSort = {$sort: {'customer.name': -1}}; * ``` */ - fun MongoAggregationPipeline.commonSort() = sort { + fun CoroutineMongoAggregationPipeline.commonSort() = sort { descending(Invoice::customer / Customer::name) } @@ -212,7 +212,7 @@ val AggregationProjectUnionWith by preparedSuite { * const commonLimit = {$limit: 20}; * ``` */ - fun MongoAggregationPipeline.commonLimit() = limit(20) + fun CoroutineMongoAggregationPipeline.commonLimit() = limit(20) test("Case study with \$unionWith") { drafts().insertOne( diff --git a/test-legacy/src/jvmMain/kotlin/TestDatabase.jvm.kt b/test-legacy/src/jvmMain/kotlin/TestDatabase.jvm.kt index 690e9a6e..55d909b5 100644 --- a/test-legacy/src/jvmMain/kotlin/TestDatabase.jvm.kt +++ b/test-legacy/src/jvmMain/kotlin/TestDatabase.jvm.kt @@ -19,8 +19,8 @@ package opensavvy.ktmongo.test import com.mongodb.MongoTimeoutException import com.mongodb.kotlin.client.coroutine.MongoClient import kotlinx.coroutines.CoroutineName -import opensavvy.ktmongo.coroutines.MongoCollection -import opensavvy.ktmongo.coroutines.asKtMongoLegacy +import opensavvy.ktmongo.coroutines.CoroutineMongoCollection +import opensavvy.ktmongo.coroutines.asKtMongo import opensavvy.prepared.suite.PreparedProvider import opensavvy.prepared.suite.prepared import opensavvy.prepared.suite.shared @@ -38,7 +38,7 @@ internal val database by shared(CoroutineName("mongodb-establish-connection")) { client.getDatabase("ktmongo-sync-tests") } -actual inline fun testCollectionExact(name: String): PreparedProvider> = prepared(CoroutineName("mongodb-create-collection-$name")) { +actual inline fun testCollectionExact(name: String): PreparedProvider> = prepared(CoroutineName("mongodb-create-collection-$name")) { val collection = database().getCollection(name) - collection.asKtMongoLegacy() + collection.asKtMongo() } -- 2.54.0 From 3e42ecf6bcae2cdc8e15841058680cdb5d2cd58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Mon, 10 Aug 2026 22:35:21 +0200 Subject: [PATCH 6/6] breaking(driver-coroutines): Remove the non-unified API --- .../commonMain/kotlin/FilteredCollection.kt | 297 ----------- .../kotlin/MongoAggregationPipeline.kt | 144 ----- .../src/commonMain/kotlin/MongoCollection.kt | 56 -- .../src/commonMain/kotlin/MongoIterable.kt | 118 ----- .../operations/AggregationOperations.kt | 48 -- .../kotlin/operations/BaseOperations.kt | 26 - .../kotlin/operations/CollectionOperations.kt | 48 -- .../kotlin/operations/CountOperations.kt | 115 ---- .../kotlin/operations/DeleteOperations.kt | 78 --- .../kotlin/operations/FindOperations.kt | 97 ---- .../kotlin/operations/InsertOperations.kt | 117 ----- .../kotlin/operations/UpdateOperations.kt | 484 ----------------- .../operations/UpdatePipelineOperations.kt | 146 ------ .../src/jvmMain/kotlin/JvmExt.kt | 22 - .../src/jvmMain/kotlin/JvmMongoCollection.kt | 492 ------------------ .../src/jvmMain/kotlin/JvmMongoIterable.kt | 68 --- 16 files changed, 2356 deletions(-) delete mode 100644 driver-coroutines/src/commonMain/kotlin/FilteredCollection.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/MongoCollection.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/MongoIterable.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/AggregationOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/BaseOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/CollectionOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/CountOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/DeleteOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/FindOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/InsertOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/UpdateOperations.kt delete mode 100644 driver-coroutines/src/commonMain/kotlin/operations/UpdatePipelineOperations.kt delete mode 100644 driver-coroutines/src/jvmMain/kotlin/JvmExt.kt delete mode 100644 driver-coroutines/src/jvmMain/kotlin/JvmMongoCollection.kt delete mode 100644 driver-coroutines/src/jvmMain/kotlin/JvmMongoIterable.kt diff --git a/driver-coroutines/src/commonMain/kotlin/FilteredCollection.kt b/driver-coroutines/src/commonMain/kotlin/FilteredCollection.kt deleted file mode 100644 index 31569081..00000000 --- a/driver-coroutines/src/commonMain/kotlin/FilteredCollection.kt +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (c) 2024-2026, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines - -import opensavvy.ktmongo.bson.types.ObjectId -import opensavvy.ktmongo.coroutines.operations.UpdateOperations.UpdateResult -import opensavvy.ktmongo.coroutines.operations.UpdateOperations.UpsertResult -import opensavvy.ktmongo.dsl.BsonContext -import opensavvy.ktmongo.dsl.LowLevelApi -import opensavvy.ktmongo.dsl.command.* -import opensavvy.ktmongo.dsl.query.FilterQuery -import opensavvy.ktmongo.dsl.query.UpdateQuery -import opensavvy.ktmongo.dsl.query.UpdateWithPipelineQuery -import opensavvy.ktmongo.dsl.query.UpsertQuery - -private class FilteredCollection( - private val upstream: MongoCollection, - private val globalFilter: FilterQuery.() -> Unit, -) : MongoCollection { - - override fun find(): MongoIterable = - upstream.find(filter = globalFilter) - - override fun find( - options: FindOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - ): MongoIterable = - upstream.find(options) { - globalFilter() - filter() - } - - @LowLevelApi - override val context: BsonContext - get() = upstream.context - - override suspend fun count(): Long = - upstream.count(predicate = globalFilter) - - override suspend fun count( - options: CountOptions.() -> Unit, - predicate: FilterQuery.() -> Unit, - ): Long = - upstream.count( - options = options, - predicate = { - globalFilter() - predicate() - } - ) - - override suspend fun countEstimated(): Long = - count() - - override suspend fun updateMany( - options: UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateQuery.() -> Unit, - ): UpdateResult { - return upstream.updateMany( - options = options, - filter = { - globalFilter() - filter() - }, - update = update, - ) - } - - override suspend fun updateManyWithPipeline( - options: UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpdateResult { - return upstream.updateManyWithPipeline( - options = options, - filter = { - globalFilter() - filter() - }, - update = update, - ) - } - - override suspend fun updateOne( - options: UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateQuery.() -> Unit, - ): UpdateResult { - return upstream.updateOne( - options = options, - filter = { - globalFilter() - filter() - }, - update = update, - ) - } - - override suspend fun replaceOne( - options: ReplaceOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - document: Document, - ) { - upstream.replaceOne( - options = options, - filter = { - globalFilter() - filter() - }, - document = document, - ) - } - - override suspend fun repsertOne( - options: ReplaceOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - document: Document, - ) { - upstream.repsertOne( - options = options, - filter = { - globalFilter() - filter() - }, - document = document, - ) - } - - override suspend fun updateOneWithPipeline( - options: UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpdateResult { - return upstream.updateOneWithPipeline( - options = options, - filter = { - globalFilter() - filter() - }, - update = update, - ) - } - - override suspend fun upsertOne( - options: UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpsertQuery.() -> Unit, - ): UpsertResult { - return upstream.upsertOne( - options = options, - filter = { - globalFilter() - filter() - }, - update = update, - ) - } - - override suspend fun upsertOneWithPipeline( - options: UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpsertResult { - return upstream.upsertOneWithPipeline( - options = options, - filter = { - globalFilter() - filter() - }, - update = update, - ) - } - - override suspend fun findOneAndUpdate( - options: UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateQuery.() -> Unit, - ): Document? = - upstream.findOneAndUpdate( - options = options, - filter = { - globalFilter() - filter() - }, - update = update, - ) - - override suspend fun bulkWrite( - options: BulkWriteOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - operations: BulkWrite.() -> Unit, - ) = upstream.bulkWrite( - options = options, - filter = { - globalFilter() - filter() - }, - operations = operations, - ) - - override suspend fun deleteOne(options: DeleteOneOptions.() -> Unit, filter: FilterQuery.() -> Unit) { - upstream.deleteOne( - options = options, - filter = { - globalFilter() - filter() - } - ) - } - - override suspend fun deleteMany(options: DeleteManyOptions.() -> Unit, filter: FilterQuery.() -> Unit) { - upstream.deleteMany( - options = options, - filter = { - globalFilter() - filter() - } - ) - } - - override suspend fun drop(options: DropOptions.() -> Unit) { - deleteMany( - options = { - }, - filter = { - globalFilter() - } - ) - } - - override suspend fun insertOne(document: Document, options: InsertOneOptions.() -> Unit) = - upstream.insertOne(document, options) - - override suspend fun insertMany(documents: Iterable, options: InsertManyOptions.() -> Unit) = - upstream.insertMany(documents, options) - - override fun aggregate(): MongoAggregationPipeline = - upstream.aggregate().match(globalFilter) - - @OptIn(LowLevelApi::class) - override fun toString(): String { - val filter = FilterQuery(context) - .apply(globalFilter) - .let(context::buildDocument) - - return "$upstream.filter $filter" - } - - override fun newId(): ObjectId = - upstream.newId() -} - -/** - * Returns a filtered collection that only contains the elements that match [filter]. - * - * This function creates a logical view of the collection: by itself, this function does nothing, and MongoDB is never - * aware of the existence of this logical view. However, operations invoked on the returned collection will only affect - * elements from the original that match the [filter]. - * - * Unlike actual MongoDB views, which are read-only, collections returned by this function can also be used for write operations. - * - * ### Example - * - * A typical usage of this function is to reuse filters for multiple operations. - * For example, if you have a concept of logical deletion, this function can be used to hide deleted values. - * - * ```kotlin - * class Order( - * val id: String, - * val date: Instant, - * val deleted: Boolean, - * ) - * - * val allOrders = database.getCollection("orders").asKtMongo() - * val activeOrders = allOrders.filter { Order::deleted ne true } - * - * allOrders.find() // Returns all orders, deleted or not - * activeOrders.find() // Only returns orders that are not logically deleted - * ``` - */ -fun MongoCollection.filter(filter: FilterQuery.() -> Unit): MongoCollection = - FilteredCollection(this, filter) diff --git a/driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt b/driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt deleted file mode 100644 index 5baec8e5..00000000 --- a/driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2025-2026, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines - -import opensavvy.ktmongo.bson.BsonFieldWriter -import opensavvy.ktmongo.dsl.BsonContext -import opensavvy.ktmongo.dsl.DangerousMongoApi -import opensavvy.ktmongo.dsl.KtMongoDsl -import opensavvy.ktmongo.dsl.LowLevelApi -import opensavvy.ktmongo.dsl.aggregation.* -import opensavvy.ktmongo.dsl.aggregation.stages.* -import opensavvy.ktmongo.dsl.options.SortOptionDsl -import opensavvy.ktmongo.dsl.query.FilterQuery -import opensavvy.ktmongo.dsl.tree.BsonNode - -class MongoAggregationPipeline @OptIn(LowLevelApi::class) internal constructor( - private val collection: String, - context: BsonContext, - chain: PipelineChainLink, - private val iterableBuilder: (MongoAggregationPipeline<*>, documentType: Class) -> MongoIterable<*>, -) : AbstractPipeline(context, chain), AggregationPipeline, LazyMongoIterable { - - // region Pipeline methods - - @LowLevelApi - @DangerousMongoApi - override fun withStage(stage: BsonNode): MongoAggregationPipeline = - MongoAggregationPipeline(collection, context, chain.withStage(stage), iterableBuilder) - - @Suppress("UNCHECKED_CAST") // The type is phantom, the cast is guaranteed to succeed - @LowLevelApi - @DangerousMongoApi - override fun reinterpret(): MongoAggregationPipeline = - this as MongoAggregationPipeline - - // endregion - // region Lazy iterable - - @Suppress("UNCHECKED_CAST") - override fun asIterable(documentType: Class): MongoIterable = - iterableBuilder(this, documentType) as MongoIterable - - // endregion - // region Stages - - @KtMongoDsl - override fun limit(amount: Long): MongoAggregationPipeline = - super.limit(amount) as MongoAggregationPipeline - - @KtMongoDsl - override fun limit(amount: Int): MongoAggregationPipeline = - super.limit(amount) as MongoAggregationPipeline - - @KtMongoDsl - override fun match(filter: FilterQuery.() -> Unit): MongoAggregationPipeline = - super.match(filter) as MongoAggregationPipeline - - @KtMongoDsl - override fun matchExpr(filter: AggregationOperators.() -> Value): MongoAggregationPipeline = - super.matchExpr(filter) as MongoAggregationPipeline - - @KtMongoDsl - override fun sample(size: Int): MongoAggregationPipeline = - super.sample(size) as MongoAggregationPipeline - - @KtMongoDsl - override fun set(block: SetStageOperators.() -> Unit): MongoAggregationPipeline = - super.set(block) as MongoAggregationPipeline - - @KtMongoDsl - override fun skip(amount: Long): MongoAggregationPipeline = - super.skip(amount) as MongoAggregationPipeline - - @KtMongoDsl - override fun skip(amount: Int): MongoAggregationPipeline = - super.skip(amount) as MongoAggregationPipeline - - @KtMongoDsl - override fun sort(block: SortOptionDsl.() -> Unit): MongoAggregationPipeline = - super.sort(block) as MongoAggregationPipeline - - @KtMongoDsl - override fun unset(block: UnsetStageOperators.() -> Unit): MongoAggregationPipeline = - super.unset(block) as MongoAggregationPipeline - - @KtMongoDsl - override fun project(block: ProjectStageOperators.() -> Unit): MongoAggregationPipeline = - super.project(block) as MongoAggregationPipeline - - override fun lookup( - block: LookupStageOperators.() -> Unit, - ): MongoAggregationPipeline = - super.lookup(block) as MongoAggregationPipeline - - @KtMongoDsl - override fun unionWith(other: HasUnionWithCompatibility): MongoAggregationPipeline = - super.unionWith(other) as MongoAggregationPipeline - - @KtMongoDsl - override fun group(block: AccumulationOperators.() -> Unit): MongoAggregationPipeline = - super.group(block) as MongoAggregationPipeline - - // endregion - // region $lookup compatibility - - @LowLevelApi - override fun embedInLookup(writer: BsonFieldWriter): Unit = with(writer) { - writeString("from", collection) - - if (chain.isNotEmpty()) { - writeArray("pipeline") { - this@MongoAggregationPipeline.writeTo(this) - } - } - } - - // endregion - // region $unionWith support - - @OptIn(LowLevelApi::class) - override fun embedInUnionWith(writer: BsonFieldWriter): Unit = with(writer) { - writeString("coll", collection) - writeArray("pipeline") { - this@MongoAggregationPipeline.writeTo(this) - } - } - - // endregion - -} diff --git a/driver-coroutines/src/commonMain/kotlin/MongoCollection.kt b/driver-coroutines/src/commonMain/kotlin/MongoCollection.kt deleted file mode 100644 index 8ceb1495..00000000 --- a/driver-coroutines/src/commonMain/kotlin/MongoCollection.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2024-2026, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines - -import opensavvy.ktmongo.bson.types.ObjectIdGenerator -import opensavvy.ktmongo.coroutines.operations.* - -/** - * Methods to interact with a MongoDB collection. - * - * ### Operations - * - * - [aggregate][AggregationOperations.aggregate] - * - [bulkWrite][UpdateOperations.bulkWrite] - * - [count][CountOperations.count] - * - [countEstimated][CountOperations.countEstimated] - * - [deleteOne][DeleteOperations.deleteOne] - * - [deleteMany][DeleteOperations.deleteMany] - * - [drop][CollectionOperations.drop] - * - [find][FindOperations.find] - * - [findOne][FindOperations.findOne] - * - [findOneAndUpdate][UpdateOperations.findOneAndUpdate] - * - [insertOne][InsertOperations.insertOne] - * - [insertMany][InsertOperations.insertMany] - * - [updateOne][UpdateOperations.updateOne] - * - [updateMany][UpdateOperations.updateMany] - * - [upsertOne][UpdateOperations.upsertOne] - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/tutorial/query-documents) - */ -interface MongoCollection : - ObjectIdGenerator, - FindOperations, - CountOperations, - UpdateOperations, - DeleteOperations, - CollectionOperations, - InsertOperations, - AggregationOperations, - UpdatePipelineOperations diff --git a/driver-coroutines/src/commonMain/kotlin/MongoIterable.kt b/driver-coroutines/src/commonMain/kotlin/MongoIterable.kt deleted file mode 100644 index fb3b128e..00000000 --- a/driver-coroutines/src/commonMain/kotlin/MongoIterable.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2024-2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines - -import kotlinx.coroutines.flow.Flow - -/** - * Streaming-ready iterable client to read data from the database. - */ -interface MongoIterable { - - // region Extract data - - /** - * Returns the first document found by this query, or throws an exception. - * - * @throws NoSuchElementException If this query returned no results. - * @see firstOrNull - */ - suspend fun first(): Document = - firstOrNull() ?: throw NoSuchElementException("No element was returned by this query") - - /** - * Returns the first document found by this query, or `null` if none were found. - * - * @see first - */ - suspend fun firstOrNull(): Document? - - /** - * Executes [action] for each document returned by this query. - * - * This method streams all returned elements into the [action] function. - * The entire response is not loaded at once into memory. - */ - suspend fun forEach(action: suspend (Document) -> Unit) - - // endregion - // region To another data structure - - /** - * Reads the entirety of this response into a [List]. - * - * Since lists are in-memory, this will load the entirety of the results of this query into memory. - * - * @see toSet - */ - suspend fun toList(): List { - val list = ArrayList() - forEach { list.add(it) } - return list - } - - /** - * Reads the entirety of this response into a [Set]. - * - * Since sets are in-memory, this will load the entirety of the results of this query into memory. - * - * @see toList - */ - suspend fun toSet(): Set { - val set = LinkedHashSet() - forEach { set.add(it) } - return set - } - - /** - * Streams the results of this response into a [Flow]. - */ - fun asFlow(): Flow - - @Deprecated("Kotlin Sequences are not capable of closing a resource after they are done. Using sequences with a MongoIterable will create memory leaks. Instead, use toList, forEach, asStream (Java only) or the coroutines driver's asFlow", ReplaceWith("this.toList().asSequence()"), level = DeprecationLevel.ERROR) - fun asSequence(): Sequence = throw UnsupportedOperationException("Sequences are not supported because they create memory lists. Use lists, streams, flows, or simply forEach instead.") - - @Deprecated("Kotlin Sequences are not capable of closing a resource after they are done. Using sequences with a MongoIterable will create memory leaks. Instead, use toList, forEach, asStream (Java only) or the coroutines driver's asFlow", ReplaceWith("this.toList().asSequence()"), level = DeprecationLevel.ERROR) - fun toSequence(): Sequence = throw UnsupportedOperationException("Sequences are not supported because they create memory lists. Use lists, streams, flows, or simply forEach instead.") - - // endregion -} - -interface LazyMongoIterable { - fun asIterable(documentType: Class): MongoIterable -} - -inline fun LazyMongoIterable.asIterable(): MongoIterable = - asIterable(Document::class.java) - -suspend inline fun LazyMongoIterable.first(): Document = - asIterable().first() - -suspend inline fun LazyMongoIterable.firstOrNull(): Document? = - asIterable().firstOrNull() - -suspend inline fun LazyMongoIterable.forEach(noinline action: suspend (Document) -> Unit): Unit = - asIterable().forEach(action) - -inline fun LazyMongoIterable.asFlow(): Flow = - asIterable().asFlow() - -suspend inline fun LazyMongoIterable.toList(): List = - asIterable().toList() - -suspend inline fun LazyMongoIterable.toSet(): Set = - asIterable().toSet() diff --git a/driver-coroutines/src/commonMain/kotlin/operations/AggregationOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/AggregationOperations.kt deleted file mode 100644 index 824e67a5..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/AggregationOperations.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.coroutines.MongoAggregationPipeline - -/** - * Interface grouping MongoDB operations relating to aggregation pipelines - */ -interface AggregationOperations : BaseOperations { - - /** - * Start an aggregation pipeline. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * users.aggregate() - * .match { User::age gt 18 } - * .toList() - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/core/aggregation-pipeline/) - */ - fun aggregate(): MongoAggregationPipeline - -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/BaseOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/BaseOperations.kt deleted file mode 100644 index f105e8f9..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/BaseOperations.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2024-2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.dsl.BsonContext -import opensavvy.ktmongo.dsl.LowLevelApi - -interface BaseOperations { - - @LowLevelApi - val context: BsonContext -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/CollectionOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/CollectionOperations.kt deleted file mode 100644 index 9741d15d..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/CollectionOperations.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024-2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.dsl.command.DropOptions - -/** - * Interface grouping MongoDB operations relating to collection administration. - */ -interface CollectionOperations : BaseOperations { - - /** - * Removes an entire collection from the database. - * - * ### Example - * - * ```kotlin - * collection.drop() - * ``` - * - * ### Using with filtered collections - * - * When using [filtered collections][opensavvy.ktmongo.sync.MongoCollection.filter], all elements matching the filter - * are removed. Other documents are not impacted, and the collection is not deleted. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.drop/) - */ - suspend fun drop( - options: DropOptions.() -> Unit = {} - ) - -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/CountOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/CountOperations.kt deleted file mode 100644 index 96acd4f1..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/CountOperations.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2024-2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.dsl.command.CountOptions -import opensavvy.ktmongo.dsl.query.FilterQuery - -/** - * Interface grouping MongoDB operations relating to counting documents. - */ -interface CountOperations : BaseOperations { - - /** - * Counts how many documents exist in the collection. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.countDocuments/) - * - * @see countEstimated Faster alternative when the result doesn't need to be exact. - */ - suspend fun count(): Long - - /** - * Counts how many documents match [predicate] in the collection. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.count { - * User::name eq "foo" - * User::age eq 10 - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.countDocuments/) - */ - suspend fun count( - options: CountOptions.() -> Unit = {}, - predicate: FilterQuery.() -> Unit, - ): Long - - /** - * Tests if there exists a document that matches [predicate] in the collection. - * - * This method is a convenience function for calling [count] with a [limit][CountOptions.limit] of 1. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.exists { - * User::name eq "foo" - * User::age eq 10 - * } - * ``` - */ - suspend fun exists( - options: CountOptions.() -> Unit = {}, - predicate: FilterQuery.() -> Unit, - ): Boolean = count( - options = { - options() - limit(1) - }, - predicate = predicate, - ) == 1L - - /** - * Counts all documents in the collection. - * - * This function reads collection metadata instead of actually counting through all documents. - * This makes it much more performant (almost no CPU nor RAM usage), but the count may be slightly out of date. - * - * In particular, it may become inaccurate when: - * - there are orphaned documents in a shared cluster, - * - an unclean shutdown happened. - * - * Views do not possess the required metadata. - * When this function is called on a view (either a MongoDB view or a [filter] logical view), a regular [count] is executed instead. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.estimatedDocumentCount/) - * - * @see count Perform the count for real. - */ - suspend fun countEstimated(): Long - -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/DeleteOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/DeleteOperations.kt deleted file mode 100644 index 654bfc83..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/DeleteOperations.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2024-2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.dsl.command.DeleteManyOptions -import opensavvy.ktmongo.dsl.command.DeleteOneOptions -import opensavvy.ktmongo.dsl.query.FilterQuery - -/** - * Interface grouping MongoDB operations relating to deleting documents. - */ -interface DeleteOperations : BaseOperations { - - /** - * Deletes the first document found that matches [filter]. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int - * ) - * - * collection.deleteOne { - * User::name eq "Bob" - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.deleteOne) - */ - suspend fun deleteOne( - options: DeleteOneOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit, - ) - - /** - * Deletes all documents that match [filter]. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int - * ) - * - * collection.deleteMany { - * User::age lt 18 - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.deleteMany/) - */ - suspend fun deleteMany( - options: DeleteManyOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit, - ) - -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/FindOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/FindOperations.kt deleted file mode 100644 index 0dac066c..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/FindOperations.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2024-2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.coroutines.MongoIterable -import opensavvy.ktmongo.dsl.command.FindOptions -import opensavvy.ktmongo.dsl.query.FilterQuery - -/** - * Interface grouping MongoDB operations allowing to search for information. - */ -interface FindOperations : BaseOperations { - - /** - * Finds all documents in this collection. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.find/) - */ - fun find(): MongoIterable - - /** - * Finds all documents in this collection that satisfy [filter]. - * - * If multiple predicates are specified, an [and][FilterQuery.and] operator is implied. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.find { - * User::name eq "foo" - * User::age eq 10 - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.find/) - * - * @see findOne When only one result is expected. - */ - fun find( - options: FindOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit, - ): MongoIterable - - /** - * Finds a document in this collection that satisfies [filter]. - * - * If multiple predicates are specified, and [and][FilterQuery.and] operator is implied. - * - * This function doesn't check that there is exactly one value in the collection. - * It simply returns the first matching document it finds. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.findOne { - * User::name eq "foo" - * User::age eq 10 - * } - * ``` - * - * @see find When multiple results are expected. - */ - suspend fun findOne( - options: FindOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit, - ): Document? = - find(options, filter).firstOrNull() - -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/InsertOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/InsertOperations.kt deleted file mode 100644 index cd216ade..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/InsertOperations.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2024-2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.coroutines.filter -import opensavvy.ktmongo.dsl.command.InsertManyOptions -import opensavvy.ktmongo.dsl.command.InsertOneOptions - -interface InsertOperations : BaseOperations { - - /** - * Inserts a [document]. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.insertOne(User(name = "Bob", age = 18)) - * ``` - * - * Note that `insertOne` ignores [filtered collection][opensavvy.ktmongo.coroutines.MongoCollection.filter]. - * That is, `insertOne` on a filtered collection behaves exactly the same as the same `insertOne` on the underlying - * real collection. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.insertOne/) - * - * @see insertMany Insert multiple documents. - */ - suspend fun insertOne( - document: Document, - options: InsertOneOptions.() -> Unit = {}, - ) - - /** - * Inserts multiple [documents] in a single operation. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.insertMany(users) - * ``` - * - * Note that `insertOne` ignores [filtered collection][opensavvy.ktmongo.coroutines.MongoCollection.filter]. - * That is, `insertOne` on a filtered collection behaves exactly the same as the same `insertOne` on the underlying - * real collection. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#insertone) - * - * @see insertOne Insert a single document. - */ - suspend fun insertMany( - documents: Iterable, - options: InsertManyOptions.() -> Unit = {}, - ) - - /** - * Inserts multiple [documents] in a single operation. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.insertMany( - * User(name = "Bob", age = 18), - * User(name = "Alice", age = 17) - * ) - * ``` - * - * Note that `insertOne` ignores [filtered collection][opensavvy.ktmongo.coroutines.MongoCollection.filter]. - * That is, `insertOne` on a filtered collection behaves exactly the same as the same `insertOne` on the underlying - * real collection. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#insertone) - * - * @see insertOne Insert a single document. - */ - suspend fun insertMany( - vararg documents: Document, - options: InsertManyOptions.() -> Unit = {}, - ) { - insertMany(documents.asList(), options) - } - -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/UpdateOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/UpdateOperations.kt deleted file mode 100644 index aeebe062..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/UpdateOperations.kt +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (c) 2024-2026, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.bson.BsonValue -import opensavvy.ktmongo.coroutines.MongoCollection -import opensavvy.ktmongo.coroutines.filter -import opensavvy.ktmongo.dsl.LowLevelApi -import opensavvy.ktmongo.dsl.command.BulkWrite -import opensavvy.ktmongo.dsl.command.BulkWriteOptions -import opensavvy.ktmongo.dsl.command.ReplaceOptions -import opensavvy.ktmongo.dsl.command.UpdateOptions -import opensavvy.ktmongo.dsl.query.FilterQuery -import opensavvy.ktmongo.dsl.query.UpdateQuery -import opensavvy.ktmongo.dsl.query.UpsertQuery - -/** - * Interface grouping MongoDB operations allowing to update existing information. - */ -interface UpdateOperations : BaseOperations { - - /** - * Updates all documents that match [filter] according to [update]. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.updateMany( - * filter = { - * User::name eq "Patrick" - * }, - * update = { - * User::age set 15 - * }, - * ) - * ``` - * - * ### Using filtered collections - * - * The following code is equivalent: - * ```kotlin - * collection.filter { - * User::name eq "Patrick" - * }.updateMany { - * User::age set 15 - * } - * ``` - * - * To learn more, see [filter][MongoCollection.filter]. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/update/) - * - * @param filter Optional filter to select which documents are updated. - * If no filter is specified, all documents are updated. - * @see updateOne - */ - @IgnorableReturnValue - suspend fun updateMany( - options: UpdateOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - update: UpdateQuery.() -> Unit, - ): UpdateResult - - /** - * Updates a single document that matches [filter] according to [update]. - * - * If multiple documents match [filter], only the first one found is updated. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.updateOne( - * filter = { - * User::name eq "Patrick" - * }, - * update = { - * User::age set 15 - * }, - * ) - * ``` - * - * ### Using filtered collections - * - * The following code is equivalent: - * ```kotlin - * collection.filter { - * User::name eq "Patrick" - * }.updateOne { - * User::age set 15 - * } - * ``` - * - * To learn more, see [filter][MongoCollection.filter]. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/update/) - * - * @param filter Optional filter to select which document is updated. - * If no filter is specified, the first document found is updated. - * @see updateMany Update more than one document. - * @see findOneAndUpdate Also returns the result of the update. - */ - @IgnorableReturnValue - suspend fun updateOne( - options: UpdateOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - update: UpdateQuery.() -> Unit, - ): UpdateResult - - /** - * Updates a single document that matches [filter] according to [update]. - * - * If multiple documents match [filter], only the first one is updated. - * - * If no documents match [filter], a new one is created. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.upsertOne( - * filter = { - * User::name eq "Patrick" - * }, - * update = { - * User::age set 15 - * }, - * ) - * ``` - * - * If a document exists that has the `name` of "Patrick", its age is set to 15. - * If none exists, a document with `name` "Patrick" and `age` 15 is created. - * - * ### Using filtered collections - * - * The following code is equivalent: - * ```kotlin - * collection.filter { - * User::name eq "Patrick" - * }.upsertOne { - * User::age set 15 - * } - * ``` - * - * To learn more, see [filter][MongoCollection.filter]. - * - * ### External resources - * - * - [The update operation](https://www.mongodb.com/docs/manual/reference/command/update/) - * - [The behavior of upsert functions](https://www.mongodb.com/docs/manual/reference/method/db.collection.update/#insert-a-new-document-if-no-match-exists--upsert-) - * - * @see updateOne - */ - @IgnorableReturnValue - suspend fun upsertOne( - options: UpdateOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - update: UpsertQuery.() -> Unit, - ): UpsertResult - - /** - * Replaces a document that matches [filter] by [document]. - * - * If multiple documents match [filter], only the first one found is updated. - * - * ### Data races - * - * This operator is often used by first reading a document, processing it, and replacing it. - * This can be dangerous in distributed systems because another replica of the server could have updated - * the document between the read and the write. - * - * If this is a concern, it is recommended to use [updateOne] with explicit operators on the data that has changed, - * allowing to do the modification in a single operation. Doing the update that way, MongoDB is responsible - * for ensuring the read and the write are atomic. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.replaceOne( - * filter = { - * User::name eq "Patrick" - * }, - * document = User("Bob", 15) - * ) - * ``` - * - * ### Using filtered collections - * - * The following code is equivalent: - * ```kotlin - * collection.filter { - * User::name eq "Patrick" - * }.replaceOne(User("Patrick", 15)) - * ``` - * - * To learn more, see [filter][MongoCollection.filter]. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/update/) - * - * @param filter Optional filter to select which document is updated. - * If no filter is specified, the first document found is updated. - * @see updateOne Updates an existing document. - * @see updateMany Update more than one document. - * @see repsertOne Replaces a document, or inserts it if it doesn't exist. - * @see findOneAndUpdate Also returns the result of the update. - */ - suspend fun replaceOne( - options: ReplaceOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - document: Document, - ) - - /** - * Replaces a document that matches [filter] by [document]. - * - * If multiple documents match [filter], only the first one found is updated. - * - * If no documents match [filter], [document] is [inserted][InsertOperations.insertOne]. - * - * ### Data races - * - * This operator is often used by first reading a document, processing it, and replacing it. - * This can be dangerous in distributed systems because another replica of the server could have updated - * the document between the read and the write. - * - * If this is a concern, it is recommended to use [updateOne] with explicit operators on the data that has changed, - * allowing to do the modification in a single operation. Doing the update that way, MongoDB is responsible - * for ensuring the read and the write are atomic. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.repsertOne( - * filter = { - * User::name eq "Patrick" - * }, - * document = User("Bob", 15) - * ) - * ``` - * - * ### Using filtered collections - * - * The following code is equivalent: - * ```kotlin - * collection.filter { - * User::name eq "Patrick" - * }.repsertOne(User("Patrick", 15)) - * ``` - * - * To learn more, see [filter][MongoCollection.filter]. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/update/) - * - * @param filter Optional filter to select which document is updated. - * If no filter is specified, the first document found is updated. - * @see updateOne Updates an existing document. - * @see replaceOne Replaces an existing document. - * @see findOneAndUpdate Also returns the result of the update. - */ - suspend fun repsertOne( - options: ReplaceOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - document: Document, - ) - - /** - * Updates one element that matches [filter] according to [update] and returns it, atomically. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.findOneAndUpdate( - * filter = { - * User::name eq "Patrick" - * }, - * update = { - * User::age set 15 - * }, - * ) - * ``` - * - * ### Using filtered collections - * - * The following code is equivalent: - * ```kotlin - * collection.filter { - * User::name eq "Patrick" - * }.findOneAndUpdate { - * User::age set 15 - * } - * ``` - * - * To learn more, see [filter][MongoCollection.filter]. - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/findAndModify/) - * - * @param filter Optional filter to select which document is updated. - * If no filter is specified, the first document found is updated. - * @see updateMany Update more than one document. - * @see updateOne Do not return the value. - */ - suspend fun findOneAndUpdate( - options: UpdateOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - update: UpdateQuery.() -> Unit, - ): Document? - - /** - * Performs multiple update operations in a single request. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.bulkWrite { - * upsertOne( - * filter = { - * User::name eq "Patrick" - * }, - * update = { - * User::age set 15 - * } - * ) - * - * updateMany { - * User::age inc 1 - * } - * } - * ``` - * - * To see which operations are available and their respective syntax, see [BulkWrite]. - * - * ### Using filtered writes - * - * We can group operations by the filter they apply on: - * ```kotlin - * collection.bulkWrite { - * filtered(filter = { User::isAlive eq true }) { - * updateOne(…) - * updateOne(…) - * updateMany(…) - * } - * - * updateOne(…) - * } - * ``` - * - * To learn more, see [filtered][BulkWrite.filtered]. - * - * ### Using filtered collections - * - * If we want all operations to use the same filter, we can declare it before calling - * the operation: - * ```kotlin - * collection.filter { - * User::isAlive eq true - * }.bulkWrite { - * updateOne(…) - * updateOne(…) - * updateMany(…) - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite) - */ - suspend fun bulkWrite( - options: BulkWriteOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - operations: BulkWrite.() -> Unit, - ) - - /** - * The return value of [updateMany] and [updateOne]. - */ - interface UpdateResult { - - /** - * `true` if the update was acknowledged. - * - * To control whether the update is acknowledged, see [UpdateOptions.writeConcern]. - * - * If the update was not acknowledged, this property returns `false` and all properties throw [UnsupportedOperationException]. - */ - val acknowledged: Boolean - - /** - * The number of matched documents. - * - * @throws UnsupportedOperationException If the update was not [acknowledged]. - */ - val matchedCount: Long - - /** - * The number of modified documents. - * - * If this update created new documents (e.g., with [upsertOne]), they are not counted - * by this field: they did not already exist, so they were not modified. - * - * @throws UnsupportedOperationException If the update was not [acknowledged]. - */ - val modifiedCount: Long - } - - /** - * The return value of [upsertOne]. - */ - interface UpsertResult : UpdateResult { - - /** - * The `_id` of the upserted document, if any. - * - * If this request modified an existing document, contains `null`. - * - * @throws UnsupportedOperationException If the update was not [acknowledged]. - */ - @OptIn(LowLevelApi::class) - val upsertedId: BsonValue? - - /** - * The number of upserted documents. - * - * @throws UnsupportedOperationException If the update was not [acknowledged]. - */ - val upsertedCount: Int - } -} diff --git a/driver-coroutines/src/commonMain/kotlin/operations/UpdatePipelineOperations.kt b/driver-coroutines/src/commonMain/kotlin/operations/UpdatePipelineOperations.kt deleted file mode 100644 index cc1d3867..00000000 --- a/driver-coroutines/src/commonMain/kotlin/operations/UpdatePipelineOperations.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2025-2026, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines.operations - -import opensavvy.ktmongo.coroutines.operations.UpdateOperations.UpdateResult -import opensavvy.ktmongo.coroutines.operations.UpdateOperations.UpsertResult -import opensavvy.ktmongo.dsl.command.UpdateOptions -import opensavvy.ktmongo.dsl.query.FilterQuery -import opensavvy.ktmongo.dsl.query.UpdateWithPipelineQuery - -/** - * Interface grouping MongoDB operations allowing to update existing information using aggregation pipelines. - */ -interface UpdatePipelineOperations : BaseOperations { - - /** - * Updates all documents that match [filter] according to the [update] pipeline. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.updateManyWithPipeline( - * filter = { - * User::name eq "Patrick" - * } - * ) { - * set { - * User::age set 15 - * } - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/update/#update-with-an-aggregation-pipeline) - * - * @param filter Optional filter to select which documents are updated. - * If no filter is specified, all documents are updated. - * @see updateOneWithPipeline Update a single document. - */ - @IgnorableReturnValue - suspend fun updateManyWithPipeline( - options: UpdateOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpdateResult - - /** - * Updates a single document that matches [filter] according to the [update] pipeline. - * - * If multiple documents match [filter], only the first one found is updated. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.updateOneWithPipeline( - * filter = { - * User::name eq "Patrick" - * } - * ) { - * set { - * User::age set 15 - * } - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/update/#update-with-an-aggregation-pipeline) - * - * @param filter Optional filter to select which document is updated. - * If no filter is specified, the first document found is updated. - * @see updateManyWithPipeline Update multiple documents. - * @see upsertOneWithPipeline Update a document, creating it if it doesn't exist. - */ - @IgnorableReturnValue - suspend fun updateOneWithPipeline( - options: UpdateOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpdateResult - - /** - * Updates a single document that matches [filter] according to the [update] pipeline. - * - * If multiple documents match [filter], only the first one is updated. - * - * If no documents match [filter], a new one is created. - * - * ### Example - * - * ```kotlin - * class User( - * val name: String, - * val age: Int, - * ) - * - * collection.upsertOneWithPipeline( - * filter = { - * User::name eq "Patrick" - * } - * ) { - * set { - * User::age set 15 - * } - * } - * ``` - * - * ### External resources - * - * - [Official documentation](https://www.mongodb.com/docs/manual/reference/command/update/#update-with-an-aggregation-pipeline) - * - * @see updateOneWithPipeline Do nothing if the document doesn't already exist. - */ - @IgnorableReturnValue - suspend fun upsertOneWithPipeline( - options: UpdateOptions.() -> Unit = {}, - filter: FilterQuery.() -> Unit = {}, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpsertResult - -} diff --git a/driver-coroutines/src/jvmMain/kotlin/JvmExt.kt b/driver-coroutines/src/jvmMain/kotlin/JvmExt.kt deleted file mode 100644 index a61cc31d..00000000 --- a/driver-coroutines/src/jvmMain/kotlin/JvmExt.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2025, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines - -import org.bson.BsonDocument - -internal fun opensavvy.ktmongo.bson.BsonDocument.toJava(): BsonDocument = - (this as opensavvy.ktmongo.bson.official.BsonDocument).raw diff --git a/driver-coroutines/src/jvmMain/kotlin/JvmMongoCollection.kt b/driver-coroutines/src/jvmMain/kotlin/JvmMongoCollection.kt deleted file mode 100644 index 5b8b370a..00000000 --- a/driver-coroutines/src/jvmMain/kotlin/JvmMongoCollection.kt +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright (c) 2024-2026, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines - -import com.mongodb.client.model.* -import com.mongodb.client.model.ReplaceOptions -import com.mongodb.client.model.UpdateOptions -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.firstOrNull -import opensavvy.ktmongo.bson.official.BsonFactory -import opensavvy.ktmongo.bson.official.BsonValue -import opensavvy.ktmongo.bson.official.types.Jvm -import opensavvy.ktmongo.bson.types.ObjectId -import opensavvy.ktmongo.bson.types.ObjectIdGenerator -import opensavvy.ktmongo.coroutines.operations.UpdateOperations.UpdateResult -import opensavvy.ktmongo.coroutines.operations.UpdateOperations.UpsertResult -import opensavvy.ktmongo.dsl.LowLevelApi -import opensavvy.ktmongo.dsl.aggregation.PipelineChainLink -import opensavvy.ktmongo.dsl.command.* -import opensavvy.ktmongo.dsl.command.BulkWriteOptions -import opensavvy.ktmongo.dsl.command.CountOptions -import opensavvy.ktmongo.dsl.command.InsertManyOptions -import opensavvy.ktmongo.dsl.command.InsertOneOptions -import opensavvy.ktmongo.dsl.options.ArrayFiltersOption -import opensavvy.ktmongo.dsl.options.WithWriteConcern -import opensavvy.ktmongo.dsl.options.WriteConcernOption -import opensavvy.ktmongo.dsl.options.option -import opensavvy.ktmongo.dsl.path.PropertyNameStrategy -import opensavvy.ktmongo.dsl.query.FilterQuery -import opensavvy.ktmongo.dsl.query.UpdateQuery -import opensavvy.ktmongo.dsl.query.UpdateWithPipelineQuery -import opensavvy.ktmongo.dsl.query.UpsertQuery -import opensavvy.ktmongo.official.JvmBsonContext -import opensavvy.ktmongo.official.command.toJava -import opensavvy.ktmongo.official.options.* -import opensavvy.ktmongo.official.options.toJava -import opensavvy.ktmongo.official.toJava -import java.util.concurrent.TimeUnit -import kotlin.reflect.KType -import kotlin.reflect.typeOf - -/** - * Implementation of [MongoCollection] based on [MongoDB's MongoCollection][com.mongodb.kotlin.client.coroutine.MongoCollection]. - * - * To access the inner iterable, see [asKotlinClient]. - * - * To convert an existing MongoDB iterable into an instance of this class, see [asKtMongoLegacy]. - */ -class JvmMongoCollection internal constructor( - inner: com.mongodb.kotlin.client.coroutine.MongoCollection, - nameStrategy: PropertyNameStrategy, - private val documentType: KType, -) : MongoCollection { - - @LowLevelApi - fun asKotlinClient() = inner - - @LowLevelApi - override val context = JvmBsonContext( - bsonFactory = BsonFactory(inner.codecRegistry), - objectIdGenerator = ObjectIdGenerator.Jvm(), - nameStrategy = nameStrategy, - ) - - @OptIn(LowLevelApi::class) - private val inner = inner.withCodecRegistry(context.bsonFactory.codecRegistry) - - @OptIn(LowLevelApi::class) - override fun newId(): ObjectId = - context.newId() - - // region Find - - override fun find(): JvmMongoIterable = - JvmMongoIterable(inner.find(), repr = { "$this.find()" }) - - @OptIn(LowLevelApi::class) - override fun find( - options: FindOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - ): JvmMongoIterable { - val model = Find(context) - - model.options.options() - model.filter.filter() - - return JvmMongoIterable( - inner.withReadConcern(model.options.readReadConcern()) - .withReadPreference(model.options.readReadPreference()) - .find(context.bsonFactory.buildDocument(model.filter).raw) - .limit(model.options.readLimit()) - .skip(model.options.readSkip()) - .maxTime(model.options.readMaxTimeMS().toLong(), TimeUnit.MILLISECONDS) - .sort((model.options.readSortDocument())), - repr = { "$this.find($model)" } - ) - } - - // endregion - // region Count - - override suspend fun count(): Long = - inner.countDocuments() - - @OptIn(LowLevelApi::class) - override suspend fun count( - options: CountOptions.() -> Unit, - predicate: FilterQuery.() -> Unit, - ): Long { - val model = Count(context) - - model.options.options() - model.filter.predicate() - - return inner.countDocuments( - context.bsonFactory.buildDocument(model.filter).raw, - model.options.toJava() - ) - } - - override suspend fun countEstimated(): Long = - inner.estimatedDocumentCount() - - // endregion - // region Update - - @OptIn(LowLevelApi::class) - override suspend fun updateMany( - options: opensavvy.ktmongo.dsl.command.UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateQuery.() -> Unit, - ): UpdateResult { - val model = UpdateMany(context) - - model.options.options() - model.filter.filter() - model.update.update() - - val result = inner - .withWriteConcern(model.options) - .updateMany( - context.bsonFactory.buildDocument(model.filter).raw, - context.bsonFactory.buildDocument(model.update).raw, - UpdateOptions() - .arrayFilters(model.options.option()?.filters.orEmpty().map { context.bsonFactory.readDocument(it).raw }) - ) - return JvmUpdateResult(result, context) - } - - @OptIn(LowLevelApi::class) - override suspend fun updateOne( - options: opensavvy.ktmongo.dsl.command.UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateQuery.() -> Unit, - ): UpdateResult { - val model = UpdateOne(context) - - model.options.options() - model.filter.filter() - model.update.update() - - val result = inner - .withWriteConcern(model.options) - .updateOne( - context.bsonFactory.buildDocument(model.filter).raw, - context.bsonFactory.buildDocument(model.update).raw, - UpdateOptions() - .arrayFilters(model.options.option()?.filters.orEmpty().map { context.bsonFactory.readDocument(it).raw }) - ) - return JvmUpdateResult(result, context) - } - - @OptIn(LowLevelApi::class) - override suspend fun replaceOne( - options: opensavvy.ktmongo.dsl.command.ReplaceOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - document: Document, - ) { - val model = ReplaceOne(context, document, documentType) - - model.options.options() - model.filter.filter() - - inner.withWriteConcern(model.options).replaceOne(context.bsonFactory.buildDocument(model.filter).raw, document, ReplaceOptions()) - } - - @OptIn(LowLevelApi::class) - override suspend fun repsertOne( - options: opensavvy.ktmongo.dsl.command.ReplaceOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - document: Document, - ) { - val model = RepsertOne(context, document, documentType) - - model.options.options() - model.filter.filter() - - inner.withWriteConcern(model.options).replaceOne(context.bsonFactory.buildDocument(model.filter).raw, document, ReplaceOptions().upsert(true)) - } - - @OptIn(LowLevelApi::class) - override suspend fun upsertOne( - options: opensavvy.ktmongo.dsl.command.UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpsertQuery.() -> Unit, - ): UpsertResult { - val model = UpsertOne(context) - - model.options.options() - model.filter.filter() - model.update.update() - - val result = inner - .withWriteConcern(model.options) - .updateOne( - context.bsonFactory.buildDocument(model.filter).raw, - context.bsonFactory.buildDocument(model.update).raw, - UpdateOptions() - .upsert(true) - .arrayFilters(model.options.option()?.filters.orEmpty().map { context.bsonFactory.readDocument(it).raw }) - ) - return JvmUpdateResult(result, context) - } - - @OptIn(LowLevelApi::class) - override suspend fun findOneAndUpdate( - options: opensavvy.ktmongo.dsl.command.UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateQuery.() -> Unit, - ): Document? { - val model = UpdateOne(context) - - model.options.options() - model.filter.filter() - model.update.update() - - return inner.withWriteConcern(model.options).findOneAndUpdate(context.bsonFactory.buildDocument(model.filter).raw, context.bsonFactory.buildDocument(model.update).raw, FindOneAndUpdateOptions()) - } - - @OptIn(LowLevelApi::class) - override suspend fun bulkWrite( - options: BulkWriteOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - operations: BulkWrite.() -> Unit, - ) { - val model = BulkWrite(context, documentType, filter) - - model.options.options() - model.operations() - - inner.withWriteConcern(model.options).bulkWrite( - model.operations.map { it.toJava() }.toList(), - options = com.mongodb.client.model.BulkWriteOptions() - ) - } - - // endregion - // region Update with pipeline - - @OptIn(LowLevelApi::class) - override suspend fun updateManyWithPipeline( - options: opensavvy.ktmongo.dsl.command.UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpdateResult { - val model = UpdateManyWithPipeline(context) - - model.options.options() - model.filter.filter() - model.update.update() - - val result = inner.withWriteConcern(model.options).updateMany(context.bsonFactory.buildDocument(model.filter).raw, model.updates.map { it.toJava() }, UpdateOptions()) - return JvmUpdateResult(result, context) - } - - @OptIn(LowLevelApi::class) - override suspend fun updateOneWithPipeline( - options: opensavvy.ktmongo.dsl.command.UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpdateResult { - val model = UpdateOneWithPipeline(context) - - model.options.options() - model.filter.filter() - model.update.update() - - val result = inner.withWriteConcern(model.options).updateOne(context.bsonFactory.buildDocument(model.filter).raw, model.updates.map { it.toJava() }, UpdateOptions()) - return JvmUpdateResult(result, context) - } - - @OptIn(LowLevelApi::class) - override suspend fun upsertOneWithPipeline( - options: opensavvy.ktmongo.dsl.command.UpdateOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - update: UpdateWithPipelineQuery.() -> Unit, - ): UpsertResult { - val model = UpsertOneWithPipeline(context) - - model.options.options() - model.filter.filter() - model.update.update() - - val result = inner.withWriteConcern(model.options).updateOne(context.bsonFactory.buildDocument(model.filter).raw, model.updates.map { it.toJava() }, UpdateOptions().upsert(true)) - return JvmUpdateResult(result, context) - } - - // endregion - // region Insert - - @OptIn(LowLevelApi::class) - override suspend fun insertOne(document: Document, options: InsertOneOptions.() -> Unit) { - val model = InsertOne(context, document, documentType) - - model.options.options() - - inner.withWriteConcern(model.options).insertOne( - model.document, - com.mongodb.client.model.InsertOneOptions() - ) - } - - @OptIn(LowLevelApi::class) - override suspend fun insertMany(documents: Iterable, options: InsertManyOptions.() -> Unit) { - val model = InsertMany(context, documents.toList(), documentType) - - model.options.options() - - inner.withWriteConcern(model.options).insertMany( - model.documents, - com.mongodb.client.model.InsertManyOptions() - ) - } - - // endregion - // region Delete - - @OptIn(LowLevelApi::class) - override suspend fun deleteOne( - options: DeleteOneOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - ) { - val model = DeleteOne(context) - - model.filter.filter() - model.options.options() - - inner.withWriteConcern(model.options).deleteOne( - filter = context.bsonFactory.buildDocument(model.filter).raw, - options = DeleteOptions() - ) - } - - @OptIn(LowLevelApi::class) - override suspend fun deleteMany( - options: DeleteManyOptions.() -> Unit, - filter: FilterQuery.() -> Unit, - ) { - val model = DeleteMany(context) - - model.filter.filter() - model.options.options() - - inner.withWriteConcern(model.options).deleteOne( - filter = context.bsonFactory.buildDocument(model.filter).raw, - options = DeleteOptions() - ) - } - - // endregion - // region Collection administration - - @OptIn(LowLevelApi::class) - override suspend fun drop(options: DropOptions.() -> Unit) { - DropOptions(context).apply(options) - - inner.drop(DropCollectionOptions()) - } - - // endregion - // region Aggregation - - @OptIn(LowLevelApi::class) - override fun aggregate(): MongoAggregationPipeline = - MongoAggregationPipeline( - collection = inner.namespace.collectionName, - context = context, - chain = PipelineChainLink(context), - iterableBuilder = { pipeline, documentType -> - @Suppress("UNCHECKED_CAST") - val flow = inner.aggregate( - pipeline = pipeline.chain.toBsonList().map { it.toJava() }, - resultClass = documentType, - ) - - object : MongoIterable { - override suspend fun firstOrNull(): Document? = - flow.firstOrNull() - - override suspend fun forEach(action: suspend (Document) -> Unit) = - flow.collect(action) - - override fun asFlow(): Flow = - flow - } - } - ) - - // endregion - - override fun toString(): String = - "MongoCollection(${inner.namespace})" - -} - -/** - * Converts a [MongoDB collection][com.mongodb.kotlin.client.coroutine.MongoCollection] into a [KtMongo collection][JvmMongoCollection]. - */ -fun com.mongodb.kotlin.client.coroutine.MongoCollection.asKtMongoLegacy( - nameStrategy: PropertyNameStrategy = PropertyNameStrategy.Default, - documentType: KType, -): JvmMongoCollection = - JvmMongoCollection(this, nameStrategy, documentType) - -/** - * Converts a [MongoDB collection][com.mongodb.kotlin.client.coroutine.MongoCollection] into a [KtMongo collection][JvmMongoCollection]. - */ -inline fun com.mongodb.kotlin.client.coroutine.MongoCollection.asKtMongoLegacy( - nameStrategy: PropertyNameStrategy = PropertyNameStrategy.Default, -): JvmMongoCollection = - asKtMongoLegacy(nameStrategy, typeOf()) - -@LowLevelApi -private fun com.mongodb.kotlin.client.coroutine.MongoCollection.withWriteConcern(option: WithWriteConcern): com.mongodb.kotlin.client.coroutine.MongoCollection { - val concern = option.option()?.concern - ?: return this - - return this.withWriteConcern(concern.toJava()) -} - -private class JvmUpdateResult( - private val inner: com.mongodb.client.result.UpdateResult, - private val context: JvmBsonContext, -) : UpsertResult { // The official driver doesn't differentiate between UpdateResult & UpsertResult - override val acknowledged: Boolean - get() = inner.wasAcknowledged() - override val matchedCount: Long - get() = inner.matchedCount - override val modifiedCount: Long - get() = inner.modifiedCount - - @OptIn(LowLevelApi::class) - override val upsertedId: BsonValue? - get() = inner.upsertedId?.let { context.bsonFactory.readValue(it) } - override val upsertedCount: Int - get() = if (inner.upsertedId == null) 0 else 1 - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is JvmUpdateResult) return false - - if (inner != other.inner) return false - if (context != other.context) return false - - return true - } - - override fun hashCode(): Int { - var result = inner.hashCode() - result = 31 * result + context.hashCode() - return result - } - - @OptIn(LowLevelApi::class) - override fun toString(): String = - if (acknowledged) "UpdateResult(acknowledged=true, matchedCount=$matchedCount, modifiedCount=$modifiedCount, upsertedCount=$upsertedCount, upsertedId=$upsertedId)" - else "UpdateResult(acknowledged=false)" -} diff --git a/driver-coroutines/src/jvmMain/kotlin/JvmMongoIterable.kt b/driver-coroutines/src/jvmMain/kotlin/JvmMongoIterable.kt deleted file mode 100644 index bc222436..00000000 --- a/driver-coroutines/src/jvmMain/kotlin/JvmMongoIterable.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2024-2026, OpenSavvy and contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package opensavvy.ktmongo.coroutines - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.toList -import opensavvy.ktmongo.dsl.LowLevelApi - -/** - * Implementation of [MongoIterable] based on [MongoDB's FindFlow][com.mongodb.kotlin.client.coroutine.FindFlow]. - * - * To access the inner iterable, see [asKotlinMongoIterable]. - * - * To convert an existing MongoDB iterable into an instance of this class, see [asKtMongoLegacy]. - */ -class JvmMongoIterable internal constructor( - private val inner: com.mongodb.kotlin.client.coroutine.FindFlow, - private val repr: (() -> String)? = null, -) : MongoIterable { - - /** - * Converts a KtMongo [MongoIterable] into a [MongoDB FindFlow][com.mongodb.kotlin.client.coroutine.FindFlow]. - */ - @LowLevelApi - fun asKotlinMongoIterable() = inner - - override suspend fun first(): Document = - inner.first() - - override suspend fun firstOrNull(): Document? = - inner.firstOrNull() - - override suspend fun forEach(action: suspend (Document) -> Unit) { - inner.collect(action) - } - - override suspend fun toList(): List = - inner.toList() - - override fun asFlow(): Flow = - inner - - override fun toString(): String = - repr?.invoke() ?: super.toString() -} - -/** - * Converts a [MongoDB FindFlow][com.mongodb.kotlin.client.coroutine.FindFlow] into a - * [KtMongo MongoIterable][JvmMongoIterable]. - */ -fun com.mongodb.kotlin.client.coroutine.FindFlow.asKtMongoLegacy(): JvmMongoIterable = - JvmMongoIterable(this)