From 2ab93f0bee14450eaaa40295f4a7a68d38db590c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 12 Sep 2026 20:08:30 +0200 Subject: [PATCH] feat(driver-coroutines): Implement MongoCollection.unsafeCast() --- .../CoroutineFilteredMongoCollectionImpl.kt | 4 ++ .../kotlin/CoroutineMongoCollection.kt | 55 +++++++++++++++++++ .../kotlin/CoroutineMongoCollectionImpl.kt | 5 ++ .../MultiplatformMongoCollectionImpl.kt | 3 + 4 files changed, 67 insertions(+) diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt index 1c2e4843..3043a9a6 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineFilteredMongoCollectionImpl.kt @@ -91,6 +91,10 @@ private class CoroutineFilteredMongoCollectionImpl( override val context: BsonContext get() = upstream.context + @Suppress("UNCHECKED_CAST") + override fun unsafeCast(type: KType): CoroutineMongoCollection = + CoroutineFilteredMongoCollectionImpl(upstream.unsafeCast(type), globalFilter as FilterQuery.() -> Unit) + override suspend fun create(options: CreateCollectionOptions.() -> Unit) { error("It is not possible to call 'create' on the filtered collection $this:\nA filtered collection is a driver-side view, it cannot exist in the database itself.") } diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt index 8057bd2c..809ab674 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollection.kt @@ -28,6 +28,8 @@ import opensavvy.ktmongo.dsl.command.UpdateOptions import opensavvy.ktmongo.dsl.query.FilterQuery import opensavvy.ktmongo.dsl.query.UpdateWithPipelineQuery import opensavvy.ktmongo.dsl.query.UpsertQuery +import kotlin.reflect.KType +import kotlin.reflect.typeOf /** * A collection stores related documents together. @@ -95,4 +97,57 @@ interface CoroutineMongoCollection : MongoCollection { override fun aggregate(): CoroutineMongoAggregationPipeline override fun filter(filter: FilterQuery.() -> Unit): CoroutineMongoCollection + + /** + * Overwrites the represented type of this value. + * + * This method can be useful to bypass type checks. + * + * ### Example + * + * If we know there are some documents that have the wrong format, we can use `unsafeCast` to find them + * without needing to add their fields to the production DTO. + * + * ```kotlin + * class User( + * val _id: ObjectId, + * val name: String, + * ) + * + * users.unsafeCast() // Treat all data as arbitrary BSON documents + * .find { BsonDocument.get("oldField") ne null } + * .forEach { + * println(it["oldField"]?.decodeString()) + * } + * ``` + */ + override fun unsafeCast(type: KType): CoroutineMongoCollection + + /** + * Overwrites the represented type of this value. + * + * This method can be useful to bypass type checks. + * + * ### Example + * + * If we know there are some documents that have the wrong format, we can use `unsafeCast` to find them + * without needing to add their fields to the production DTO. + * + * ```kotlin + * class User( + * val _id: ObjectId, + * val name: String, + * ) + * + * users.unsafeCast() // Treat all data as arbitrary BSON documents + * .find { BsonDocument.get("oldField") ne null } + * .forEach { + * println(it["oldField"]?.decodeString()) + * } + * ``` + */ + @Suppress("WRONG_MODIFIER_CONTAINING_DECLARATION") + final inline fun unsafeCast(): CoroutineMongoCollection = + unsafeCast(typeOf()) + } diff --git a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt index 384991ff..88472526 100644 --- a/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt +++ b/driver-coroutines/src/jvmMain/kotlin/CoroutineMongoCollectionImpl.kt @@ -47,6 +47,7 @@ import opensavvy.ktmongo.official.options.toJava import opensavvy.ktmongo.official.toJava import opensavvy.ktmongo.official.toKtMongo import java.util.concurrent.TimeUnit +import kotlin.reflect.KClass import kotlin.reflect.KType import kotlin.reflect.typeOf import com.mongodb.client.model.ReplaceOptions as MongoReplaceOptions @@ -82,6 +83,10 @@ private class CoroutineMongoCollectionImpl( @LowLevelApi override val context: BsonContext = CoroutineBsonContext() + @Suppress("UNCHECKED_CAST") + override fun unsafeCast(type: KType): CoroutineMongoCollection = + CoroutineMongoCollectionImpl(inner.withDocumentClass((type.classifier as KClass).java), factory, propertyNameStrategy, objectIdGenerator, type, innerDatabase) + // region Count override suspend fun count(): Long = diff --git a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt index 16788d63..0625aa29 100644 --- a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt +++ b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt @@ -59,6 +59,9 @@ internal class MultiplatformMongoCollectionImpl( nameStrategy = propertyNameStrategy, ) + override fun unsafeCast(type: KType): MultiplatformMongoCollection = + MultiplatformMongoCollectionImpl(database, name, type, factory, propertyNameStrategy, objectIdGenerator) + override suspend fun insertOne( document: Document, options: InsertOneOptions.() -> Unit, -- 2.51.2