From 30a17288085cf68e7591adbaf88ce4e4613cfe2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Wed, 1 Jul 2026 22:35:38 +0200 Subject: [PATCH 1/2] feat(driver-api): Specify the MongoIterable.toString() implementation for find() --- .../kotlin/CoroutineMongoCollectionImpl.kt | 4 ++-- .../kotlin/CoroutineMongoIterableImpl.find.kt | 12 +++++++++++- .../kotlin/operations/FindOperations.test.kt | 18 ++++++++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt index 423d25ed..9af092d9 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt @@ -107,7 +107,7 @@ private class CoroutineMongoCollectionImpl( // region Find override fun find(): CoroutineMongoFindIterable = - inner.find().asKtMongo() + inner.find().asKtMongo(lazyStringRepresentation = { "$this.find({})" }) @OptIn(LowLevelApi::class) override fun find( @@ -127,7 +127,7 @@ private class CoroutineMongoCollectionImpl( .skip(model.options.readSkip()) .maxTime(model.options.readMaxTimeMS().toLong(), TimeUnit.MILLISECONDS) .sort(model.options.readSortDocument()) - .asKtMongo() + .asKtMongo(lazyStringRepresentation = { "$this.find($model)" }) } // endregion diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoIterableImpl.find.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoIterableImpl.find.kt index 81c1c8a6..9113b003 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoIterableImpl.find.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoIterableImpl.find.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.firstOrNull private class CoroutineMongoFindIterableImpl( private val inner: FindFlow, + private val lazyStringRepresentation: (() -> String)?, ) : CoroutineMongoFindIterable { override fun asOfficial(): FindFlow = @@ -42,10 +43,19 @@ private class CoroutineMongoFindIterableImpl( override fun asFlow(): Flow = inner + + override fun toString(): String = lazyStringRepresentation?.invoke() + ?: super.toString() } /** * Instantiates a KtMongo [CoroutineMongoFindIterable] using an existing flow from the official Kotlin driver. */ fun FindFlow.asKtMongo(): CoroutineMongoFindIterable = - CoroutineMongoFindIterableImpl(this) + CoroutineMongoFindIterableImpl(this, lazyStringRepresentation = { "$this.asKtMongo()" }) + +// Same but allows customizing the toString() +internal fun FindFlow.asKtMongo( + lazyStringRepresentation: (() -> String)?, +): CoroutineMongoFindIterable = + CoroutineMongoFindIterableImpl(this, lazyStringRepresentation) diff --git a/test/src/commonMain/kotlin/operations/FindOperations.test.kt b/test/src/commonMain/kotlin/operations/FindOperations.test.kt index 70850cd7..c1efc758 100644 --- a/test/src/commonMain/kotlin/operations/FindOperations.test.kt +++ b/test/src/commonMain/kotlin/operations/FindOperations.test.kt @@ -24,6 +24,7 @@ import opensavvy.ktmongo.dsl.options.ReadPreference import opensavvy.ktmongo.tests.api.collection import opensavvy.prepared.suite.Prepared import opensavvy.prepared.suite.SuiteDsl +import opensavvy.prepared.suite.assertions.matches import kotlin.time.Duration.Companion.seconds @Serializable @@ -49,6 +50,7 @@ fun SuiteDsl.verifyFindOperations( ), ) + check(collection().find().toString() matches """.+MongoCollection\(.+\).find\(\{\}\)""") check(collection().find().toList().isNotEmpty()) { "Expected at least one result, got none" } } @@ -64,9 +66,13 @@ fun SuiteDsl.verifyFindOperations( ), ) - val results = collection().find { + val request = collection().find { FindOperationsUser::name eq "Alice" - }.toList() + } + + check(request.toString() matches $$""".+MongoCollection\(.+\).find\(\{"filter": \{"name": \{"\$eq": "Alice"\}\}\}\)""") + + val results = request.toList() check(results.size == 1) check(results[0].name == "Alice") @@ -138,13 +144,17 @@ fun SuiteDsl.verifyFindOperations( FindOperationsUser(_id = collection().newId(), name = "Bob"), ) - val results = collection().find({ + val request = collection().find({ sort { ascending(FindOperationsUser::name) } }) { FindOperationsUser::name.exists() - }.toList() + } + + check(request.toString() matches $$""".+MongoCollection\(.+\).find\(\{"filter": \{"name": \{"\$exists": true\}\}, "sort": \{"name": 1\}\}\)""") + + val results = request.toList() check(results.map { it.name } == listOf("Alice", "Bob", "Carol")) } -- 2.51.2 From 66df9dc4192b0b89c37687a4205eeb84926e22ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Wed, 1 Jul 2026 22:57:40 +0200 Subject: [PATCH 2/2] feat(driver-api): Specify the MongoAggregationPipeline.toString() implementation for aggregate() --- .../kotlin/CoroutineMongoAggregationPipelineImpl.kt | 13 ++++++++----- .../jvmMain/kotlin/CoroutineMongoCollectionImpl.kt | 2 +- .../src/commonMain/kotlin/aggregation/Pipeline.kt | 2 +- dsl/src/commonMain/kotlin/aggregation/Pipeline.kt | 2 +- .../kotlin/operations/AggregationOperations.test.kt | 12 ++++++++++++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt index 4dbccbe4..7d3901ba 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoAggregationPipelineImpl.kt @@ -19,6 +19,7 @@ package opensavvy.ktmongo.coroutines +import opensavvy.ktmongo.api.MongoCollection import opensavvy.ktmongo.bson.BsonFieldWriter import opensavvy.ktmongo.dsl.BsonContext import opensavvy.ktmongo.dsl.DangerousMongoApi @@ -43,7 +44,7 @@ import kotlin.reflect.KProperty1 import kotlin.reflect.KType private class CoroutineMongoAggregationPipelineImpl @OptIn(LowLevelApi::class) constructor( - private val collectionName: String, + private val collection: MongoCollection<*>, context: BsonContext, chain: PipelineChainLink, private val executeAggregate: (List, Class) -> CoroutineMongoAggregateIterable, @@ -64,7 +65,7 @@ private class CoroutineMongoAggregationPipelineImpl @OptIn(LowLe @LowLevelApi @DangerousMongoApi override fun withStage(stage: BsonNode): CoroutineMongoAggregationPipelineImpl = - CoroutineMongoAggregationPipelineImpl(collectionName, context, chain.withStage(stage), executeAggregate) + CoroutineMongoAggregationPipelineImpl(collection, context, chain.withStage(stage), executeAggregate) @Suppress("UNCHECKED_CAST") @LowLevelApi @@ -136,7 +137,7 @@ private class CoroutineMongoAggregationPipelineImpl @OptIn(LowLe @OptIn(LowLevelApi::class) override fun embedInUnionWith(writer: BsonFieldWriter) = with(writer) { - writeString("coll", collectionName) + writeString("coll", collection.name) writeArray("pipeline") { this@CoroutineMongoAggregationPipelineImpl.writeTo(this) } @@ -144,13 +145,15 @@ private class CoroutineMongoAggregationPipelineImpl @OptIn(LowLe // endregion + override fun toString(): String = + "$collection.aggregate(${super.toString()})" } @LowLevelApi internal fun CoroutineMongoAggregationPipeline( - collectionName: String, + collection: MongoCollection<*>, context: BsonContext, chain: PipelineChainLink, executeAggregate: (List, Class) -> CoroutineMongoAggregateIterable, ): CoroutineMongoAggregationPipeline = - CoroutineMongoAggregationPipelineImpl(collectionName, context, chain, executeAggregate) + CoroutineMongoAggregationPipelineImpl(collection, context, chain, executeAggregate) diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt index 9af092d9..b76340be 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt @@ -424,7 +424,7 @@ private class CoroutineMongoCollectionImpl( @OptIn(LowLevelApi::class) override fun aggregate(): CoroutineMongoAggregationPipeline = CoroutineMongoAggregationPipeline( - collectionName = name, + collection = this, context = context, chain = PipelineChainLink(context), executeAggregate = { pipeline, documentClass -> diff --git a/dsl-template/src/commonMain/kotlin/aggregation/Pipeline.kt b/dsl-template/src/commonMain/kotlin/aggregation/Pipeline.kt index 4de25686..b47b63df 100644 --- a/dsl-template/src/commonMain/kotlin/aggregation/Pipeline.kt +++ b/dsl-template/src/commonMain/kotlin/aggregation/Pipeline.kt @@ -315,7 +315,7 @@ abstract class AbstractPipeline @OptIn(LowLevelApi::class) constru * JSON representation of this pipeline. */ @OptIn(LowLevelApi::class) - final override fun toString(): String = + override fun toString(): String = chain.toString() } diff --git a/dsl/src/commonMain/kotlin/aggregation/Pipeline.kt b/dsl/src/commonMain/kotlin/aggregation/Pipeline.kt index 7862925f..220cb419 100644 --- a/dsl/src/commonMain/kotlin/aggregation/Pipeline.kt +++ b/dsl/src/commonMain/kotlin/aggregation/Pipeline.kt @@ -318,7 +318,7 @@ abstract class AbstractPipeline @OptIn(LowLevelApi::class) constru * JSON representation of this pipeline. */ @OptIn(LowLevelApi::class) - final override fun toString(): String = + override fun toString(): String = chain.toString() } diff --git a/test/src/commonMain/kotlin/operations/AggregationOperations.test.kt b/test/src/commonMain/kotlin/operations/AggregationOperations.test.kt index afc34bc3..e8e128e8 100644 --- a/test/src/commonMain/kotlin/operations/AggregationOperations.test.kt +++ b/test/src/commonMain/kotlin/operations/AggregationOperations.test.kt @@ -24,6 +24,7 @@ import opensavvy.ktmongo.bson.types.ObjectId import opensavvy.ktmongo.tests.api.collection import opensavvy.prepared.suite.Prepared import opensavvy.prepared.suite.SuiteDsl +import opensavvy.prepared.suite.assertions.matches @Serializable data class AggregationOperationsUser( @@ -60,6 +61,8 @@ fun SuiteDsl.verifyAggregationOperations( ), ) + check(collection().aggregate().toString() matches """.+MongoCollection\(.+\).aggregate\(\[\]\)""") + val results = collection().aggregate().toList() check(results.size == 2) } @@ -199,4 +202,13 @@ fun SuiteDsl.verifyAggregationOperations( .first() check(result.total == 60) } + + test("toString representation of a complex aggregation") { + val request = collection().aggregate() + .sort { ascending(AggregationOperationsUser::age) } + .match { AggregationOperationsUser::age gte 18 } + .sample(1) + + check(request.toString() matches $$""".+MongoCollection\(.+\).aggregate\(\[\{"\$sort": \{"age": 1\}\}, \{"\$match": \{"age": \{"\$gte": 18\}\}\}, \{"\$sample": \{"size": 1\}\}\]\)""") + } } -- 2.51.2