From 14043b5aa2ca23a7fd8ad4c5c7e51d38e405e5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Sat, 5 Sep 2026 19:21:12 +0200 Subject: [PATCH] feat(driver-multiplatform): Prototype filtered collections --- ...ultiplatformFilteredMongoCollectionImpl.kt | 252 ++++++++++++++++++ .../kotlin/MultiplatformMongoCollection.kt | 5 + .../MultiplatformMongoCollectionImpl.kt | 10 +- 3 files changed, 261 insertions(+), 6 deletions(-) create mode 100644 driver-multiplatform/src/commonMain/kotlin/MultiplatformFilteredMongoCollectionImpl.kt diff --git a/driver-multiplatform/src/commonMain/kotlin/MultiplatformFilteredMongoCollectionImpl.kt b/driver-multiplatform/src/commonMain/kotlin/MultiplatformFilteredMongoCollectionImpl.kt new file mode 100644 index 00000000..0fcd4dcb --- /dev/null +++ b/driver-multiplatform/src/commonMain/kotlin/MultiplatformFilteredMongoCollectionImpl.kt @@ -0,0 +1,252 @@ +/* + * Copyright (c) 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.multiplatform + +import opensavvy.ktmongo.api.operations.UpdateOperations +import opensavvy.ktmongo.bson.multiplatform.BsonFactory +import opensavvy.ktmongo.bson.types.ObjectIdGenerator +import opensavvy.ktmongo.dsl.BsonContext +import opensavvy.ktmongo.dsl.LowLevelApi +import opensavvy.ktmongo.dsl.command.* +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 kotlin.reflect.KType + +private class MultiplatformFilteredMongoCollectionImpl( + private val upstream: MultiplatformMongoCollection, + private val globalFilter: FilterQuery.() -> Unit, +) : MultiplatformMongoCollection { + + override val name: String + get() = upstream.name + + override val fullyQualifiedName: String + get() = upstream.fullyQualifiedName + + override val propertyNameStrategy: PropertyNameStrategy + get() = upstream.propertyNameStrategy + + override val objectIdGenerator: ObjectIdGenerator + get() = upstream.objectIdGenerator + + @LowLevelApi + override val type: KType + get() = upstream.type + + override val database: MultiplatformMongoDatabase + get() = upstream.database + + override fun filter(filter: FilterQuery.() -> Unit): MultiplatformMongoCollection = + upstream.filter { + globalFilter() + filter() + } + + override val factory: BsonFactory + get() = upstream.factory + + override suspend fun upsertOne(options: UpdateOptions.() -> Unit, filter: FilterQuery.() -> Unit, update: UpsertQuery.() -> Unit): UpdateOperations.UpsertResult = + upstream.upsertOne( + options = options, + filter = { + globalFilter() + filter() + }, + update = update + ) + + override suspend fun upsertOneWithPipeline(options: UpdateOptions.() -> Unit, filter: FilterQuery.() -> Unit, update: UpdateWithPipelineQuery.() -> Unit): UpdateOperations.UpsertResult = + upstream.upsertOneWithPipeline( + options = options, + filter = { + globalFilter() + filter() + }, + update = update + ) + + override fun aggregate(): MultiplatformMongoAggregationPipeline = + upstream.aggregate() + .match { globalFilter() } + + @LowLevelApi + override val context: BsonContext + get() = upstream.context + + 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.") + } + + override suspend fun drop(options: DropOptions.() -> Unit) = + error("It is not possible to call 'drop' on the filtered collection $this:\nA filtered collection is a driver-side view, it does not exist in the database itself.") + + override suspend fun count(): Long = + upstream.count { + 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 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 fun find(): MultiplatformMongoIterable = + upstream.find { globalFilter() } + + override fun find(options: FindOptions.() -> Unit, filter: FilterQuery.() -> Unit): MultiplatformMongoIterable = + upstream.find( + options = options, + filter = { + globalFilter() + filter() + } + ) + + override suspend fun insertOne(document: Document, options: InsertOneOptions.() -> Unit) = + upstream.insertOne( + document = document, + options = options, + ) + + override suspend fun insertMany(documents: Iterable, options: InsertManyOptions.() -> Unit) = + upstream.insertMany( + documents = documents, + options = options, + ) + + override suspend fun updateMany(options: UpdateOptions.() -> Unit, filter: FilterQuery.() -> Unit, update: UpdateQuery.() -> Unit): UpdateOperations.UpdateResult = + upstream.updateMany( + options = options, + filter = { + globalFilter() + filter() + }, + update = update, + ) + + override suspend fun updateOne(options: UpdateOptions.() -> Unit, filter: FilterQuery.() -> Unit, update: UpdateQuery.() -> Unit): UpdateOperations.UpdateResult = + 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 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 updateManyWithPipeline(options: UpdateOptions.() -> Unit, filter: FilterQuery.() -> Unit, update: UpdateWithPipelineQuery.() -> Unit): UpdateOperations.UpdateResult = + upstream.updateManyWithPipeline( + options = options, + filter = { + globalFilter() + filter() + }, + update = update, + ) + + override suspend fun updateOneWithPipeline(options: UpdateOptions.() -> Unit, filter: FilterQuery.() -> Unit, update: UpdateWithPipelineQuery.() -> Unit): UpdateOperations.UpdateResult = + upstream.updateOneWithPipeline( + options = options, + filter = { + globalFilter() + filter() + }, + update = update, + ) + + @OptIn(LowLevelApi::class) + override fun toString(): String { + val filter = FilterQuery(context) + globalFilter(filter) + + return "$upstream.filter($filter)" + } +} + +internal fun createFilteredCollection( + upstream: MultiplatformMongoCollection, + globalFilter: FilterQuery.() -> Unit, +): MultiplatformMongoCollection = + MultiplatformFilteredMongoCollectionImpl(upstream, globalFilter) diff --git a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollection.kt b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollection.kt index 22e073b0..1ffd3555 100644 --- a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollection.kt +++ b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollection.kt @@ -17,6 +17,7 @@ package opensavvy.ktmongo.multiplatform import opensavvy.ktmongo.api.MongoCollection +import opensavvy.ktmongo.bson.multiplatform.BsonFactory import opensavvy.ktmongo.bson.types.ObjectId import opensavvy.ktmongo.dsl.command.FindOptions import opensavvy.ktmongo.dsl.query.FilterQuery @@ -63,6 +64,10 @@ interface MultiplatformMongoCollection : MongoCollection + override fun find(): MultiplatformMongoIterable override fun find(options: FindOptions.() -> Unit, filter: FilterQuery.() -> Unit): MultiplatformMongoIterable diff --git a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt index 6318c1c5..3c243339 100644 --- a/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt +++ b/driver-multiplatform/src/commonMain/kotlin/MultiplatformMongoCollectionImpl.kt @@ -16,12 +16,11 @@ package opensavvy.ktmongo.multiplatform -import opensavvy.ktmongo.api.MongoAggregationPipeline import opensavvy.ktmongo.api.firstOrNull import opensavvy.ktmongo.api.operations.UpdateOperations -import opensavvy.ktmongo.bson.BsonFactory import opensavvy.ktmongo.bson.BsonType import opensavvy.ktmongo.bson.multiplatform.BsonDocument +import opensavvy.ktmongo.bson.multiplatform.BsonFactory import opensavvy.ktmongo.bson.multiplatform.BsonValue import opensavvy.ktmongo.bson.types.ObjectIdGenerator import opensavvy.ktmongo.dsl.BsonContext @@ -108,11 +107,10 @@ internal class MultiplatformMongoCollectionImpl( check(message.body.document["writeErrors"] == null) { "Write errors occurred: $message" } } - override fun filter(filter: FilterQuery.() -> Unit): MultiplatformMongoCollection { - TODO("Not yet implemented") - } + override fun filter(filter: FilterQuery.() -> Unit): MultiplatformMongoCollection = + createFilteredCollection(this, filter) - override fun aggregate(): MongoAggregationPipeline = + override fun aggregate(): MultiplatformMongoAggregationPipeline = MultiplatformMongoAggregationPipelineImpl(this, PipelineChainLink(context)) override suspend fun create(options: CreateCollectionOptions.() -> Unit) { -- 2.51.2