From 91023d2fa02c76fba1a4cdba3eb1d1352ea1243a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Thu, 18 Sep 2025 09:36:15 +0200 Subject: [PATCH 1/2] fix(driver-coroutines): Fix AggregationPipeline.reinterpret() not impacting deserialization --- .../kotlin/MongoAggregationPipeline.kt | 11 +++++-- .../src/commonMain/kotlin/MongoIterable.kt | 30 +++++++++++++------ .../src/jvmMain/kotlin/JvmMongoCollection.kt | 6 ++-- .../src/commonTest/kotlin/AggregationTests.kt | 6 ++++ .../studies/AggregationProjectUnionWith.kt | 1 + 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt b/driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt index 341bfba2..b81d9ca8 100644 --- a/driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt +++ b/driver-coroutines/src/commonMain/kotlin/MongoAggregationPipeline.kt @@ -22,6 +22,7 @@ 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.stages.HasUnionWithCompatibility @@ -36,7 +37,7 @@ class MongoAggregationPipeline @OptIn(LowLevelApi::class) internal private val collection: String, context: BsonContext, chain: PipelineChainLink, - private val iterableBuilder: (MongoAggregationPipeline<*>) -> MongoIterable<*>, + private val iterableBuilder: (MongoAggregationPipeline<*>, documentType: Class) -> MongoIterable<*>, ) : AbstractPipeline(context, chain), AggregationPipeline, LazyMongoIterable { // region Pipeline methods @@ -56,8 +57,8 @@ class MongoAggregationPipeline @OptIn(LowLevelApi::class) internal // region Lazy iterable @Suppress("UNCHECKED_CAST") - override fun asIterable(): MongoIterable = - iterableBuilder(this) as MongoIterable + override fun asIterable(documentType: Class): MongoIterable = + iterableBuilder(this, documentType) as MongoIterable // endregion // region Stages @@ -106,6 +107,10 @@ class MongoAggregationPipeline @OptIn(LowLevelApi::class) internal 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 $unionWith support diff --git a/driver-coroutines/src/commonMain/kotlin/MongoIterable.kt b/driver-coroutines/src/commonMain/kotlin/MongoIterable.kt index 8a4a6c51..fb3b128e 100644 --- a/driver-coroutines/src/commonMain/kotlin/MongoIterable.kt +++ b/driver-coroutines/src/commonMain/kotlin/MongoIterable.kt @@ -92,15 +92,27 @@ interface MongoIterable { // endregion } -internal interface LazyMongoIterable : MongoIterable { - fun asIterable(): MongoIterable +interface LazyMongoIterable { + fun asIterable(documentType: Class): MongoIterable +} - override suspend fun firstOrNull(): Document? = - asIterable().firstOrNull() +inline fun LazyMongoIterable.asIterable(): MongoIterable = + asIterable(Document::class.java) - override suspend fun forEach(action: suspend (Document) -> Unit) = - asIterable().forEach(action) +suspend inline fun LazyMongoIterable.first(): Document = + asIterable().first() - override fun asFlow(): Flow = - asIterable().asFlow() -} +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/jvmMain/kotlin/JvmMongoCollection.kt b/driver-coroutines/src/jvmMain/kotlin/JvmMongoCollection.kt index 8c51594e..3319dd23 100644 --- a/driver-coroutines/src/jvmMain/kotlin/JvmMongoCollection.kt +++ b/driver-coroutines/src/jvmMain/kotlin/JvmMongoCollection.kt @@ -318,9 +318,11 @@ class JvmMongoCollection internal constructor( collection = inner.namespace.collectionName, context = context, chain = PipelineChainLink(context), - iterableBuilder = { pipeline -> + iterableBuilder = { pipeline, documentType -> + @Suppress("UNCHECKED_CAST") val flow = inner.aggregate( - pipeline.chain.toBsonList().map { it.toJava() } + pipeline = pipeline.chain.toBsonList().map { it.toJava() }, + resultClass = documentType, ) object : MongoIterable { diff --git a/test/src/commonTest/kotlin/AggregationTests.kt b/test/src/commonTest/kotlin/AggregationTests.kt index 33c053a8..de49bc87 100644 --- a/test/src/commonTest/kotlin/AggregationTests.kt +++ b/test/src/commonTest/kotlin/AggregationTests.kt @@ -14,10 +14,16 @@ * limitations under the License. */ +@file:OptIn(DangerousMongoApi::class, LowLevelApi::class) + 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.dsl.DangerousMongoApi +import opensavvy.ktmongo.dsl.LowLevelApi import opensavvy.ktmongo.test.testCollection import opensavvy.prepared.runner.kotest.PreparedSpec diff --git a/test/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt b/test/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt index 883f3be9..54142918 100644 --- a/test/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt +++ b/test/src/commonTest/kotlin/studies/AggregationProjectUnionWith.kt @@ -18,6 +18,7 @@ package opensavvy.ktmongo.sync.studies import kotlinx.serialization.Serializable import opensavvy.ktmongo.coroutines.MongoAggregationPipeline +import opensavvy.ktmongo.coroutines.toList import opensavvy.ktmongo.dsl.aggregation.stages.ProjectStageOperators import opensavvy.ktmongo.dsl.query.FilterQuery import opensavvy.ktmongo.test.testCollection -- 2.51.2 From 1f7f300b306195377702266fa85428983ba9ee1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Thu, 18 Sep 2025 09:37:14 +0200 Subject: [PATCH 2/2] fix(driver-sync): Fix AggregationPipeline.reinterpret() not impacting deserialization --- .../kotlin/MongoAggregationPipeline.kt | 6 ++--- .../src/commonMain/kotlin/MongoIterable.kt | 26 ++++++++++++++----- .../src/jvmMain/kotlin/JvmMongoCollection.kt | 5 ++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/driver-sync/src/commonMain/kotlin/MongoAggregationPipeline.kt b/driver-sync/src/commonMain/kotlin/MongoAggregationPipeline.kt index 1bd2e506..43f6887d 100644 --- a/driver-sync/src/commonMain/kotlin/MongoAggregationPipeline.kt +++ b/driver-sync/src/commonMain/kotlin/MongoAggregationPipeline.kt @@ -36,7 +36,7 @@ class MongoAggregationPipeline @OptIn(LowLevelApi::class) internal private val collection: String, context: BsonContext, chain: PipelineChainLink, - private val iterableBuilder: (MongoAggregationPipeline<*>) -> MongoIterable<*>, + private val iterableBuilder: (MongoAggregationPipeline<*>, Class) -> MongoIterable<*>, ) : AbstractPipeline(context, chain), AggregationPipeline, LazyMongoIterable { // region Pipeline methods @@ -56,8 +56,8 @@ class MongoAggregationPipeline @OptIn(LowLevelApi::class) internal // region Lazy iterable @Suppress("UNCHECKED_CAST") - override fun asIterable(): MongoIterable = - iterableBuilder(this) as MongoIterable + override fun asIterable(documentType: Class): MongoIterable = + iterableBuilder(this, documentType) as MongoIterable // endregion // region Stages diff --git a/driver-sync/src/commonMain/kotlin/MongoIterable.kt b/driver-sync/src/commonMain/kotlin/MongoIterable.kt index fe83fe8c..06f8a228 100644 --- a/driver-sync/src/commonMain/kotlin/MongoIterable.kt +++ b/driver-sync/src/commonMain/kotlin/MongoIterable.kt @@ -85,12 +85,24 @@ interface MongoIterable { // endregion } -internal interface LazyMongoIterable : MongoIterable { - fun asIterable(): MongoIterable +interface LazyMongoIterable { + fun asIterable(documentType: Class): MongoIterable +} - override fun firstOrNull(): Document? = - asIterable().firstOrNull() +inline fun LazyMongoIterable.asIterable(): MongoIterable = + asIterable(Document::class.java) - override fun forEach(action: (Document) -> Unit) = - asIterable().forEach(action) -} +inline fun LazyMongoIterable.first(): Document = + asIterable().first() + +inline fun LazyMongoIterable.firstOrNull(): Document? = + asIterable().firstOrNull() + +inline fun LazyMongoIterable.forEach(noinline action: (Document) -> Unit): Unit = + asIterable().forEach(action) + +inline fun LazyMongoIterable.toList(): List = + asIterable().toList() + +inline fun LazyMongoIterable.toSet(): Set = + asIterable().toSet() diff --git a/driver-sync/src/jvmMain/kotlin/JvmMongoCollection.kt b/driver-sync/src/jvmMain/kotlin/JvmMongoCollection.kt index 36059556..e8964580 100644 --- a/driver-sync/src/jvmMain/kotlin/JvmMongoCollection.kt +++ b/driver-sync/src/jvmMain/kotlin/JvmMongoCollection.kt @@ -318,9 +318,10 @@ class JvmMongoCollection internal constructor( collection = inner.namespace.collectionName, context = context, chain = PipelineChainLink(context), - iterableBuilder = { pipeline -> + iterableBuilder = { pipeline, documentType -> inner.aggregate( - pipeline.chain.toBsonList().map { it.toJava() } + pipeline = pipeline.chain.toBsonList().map { it.toJava() }, + resultClass = documentType, ).asKtMongo() } ) -- 2.51.2