/* * 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.sync.api import opensavvy.ktmongo.bson.BsonFactory import opensavvy.ktmongo.bson.types.ObjectId import opensavvy.ktmongo.bson.types.ObjectIdGenerator import opensavvy.ktmongo.dsl.LowLevelApi import opensavvy.ktmongo.dsl.path.PropertyNameStrategy import opensavvy.ktmongo.dsl.query.FilterQuery import opensavvy.ktmongo.sync.api.operations.* import kotlin.reflect.KType import kotlin.reflect.typeOf /** * A collection stores related documents together. * * Usually, all documents in a collection have the same shape (the same fields). * However, heterogeneous structure can be achieved by using: * - Kotlin collections, like [List] and [Set], the embed an arbitrary number of items. * - Polymorphism, for example with `sealed class`, to have different fields based on a discriminator. * * To avoid name collisions, collections are grouped into [databases][MongoDatabase]. * * To obtain a collection, see [MongoDatabase.collection]. * * ### Size limit * * A MongoDB document cannot exceed 16 MiB. * * You can measure the size of a document with [opensavvy.ktmongo.bson.BsonDocument.toByteArray] * followed by [ByteArray.size]. * * 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/) * - [Size limits](https://www.mongodb.com/docs/manual/reference/limits/#bson-documents) */ interface MongoCollection : ObjectIdGenerator, BaseOperations, AggregationOperations, ClientSideViewOperations, CollectionOperations, CountOperations, DeleteOperations, FindOperations, InsertOperations, UpdateOperations, UpdatePipelineOperations { /** * THe name of this collection. * * The collection name must be unique within a single [MongoDatabase] (otherwise, the two instances refer to the same data). * * - The name should begin with a letter or an underscore (`_`). * - The name cannot be empty. * - The name cannot contain the null character nor the `$` character. * - The name cannot being with `system.`. * - The name cannot contain `.system.`. * - It is recommended to avoid names longer than 171 bytes. * * ### External resources * * - [Name restrictions](https://www.mongodb.com/docs/manual/reference/limits/#mongodb-limit-Restriction-on-Collection-Names) */ val name: String /** * The concatenation of the database's [name][MongoDatabase.name] and the collection's [name], * separated by a dot (`.`). */ val fullyQualifiedName: String /** * The [BsonFactory] used to serialize and deserialize values stored in this collection. * * This property stores all serialization configurations and allows creating custom BSON objects. * * For more information, see [BsonFactory]. */ override val factory: BsonFactory /** * The strategy used to convert property names to BSON document keys. * * For more information, see [PropertyNameStrategy]. */ override val propertyNameStrategy: PropertyNameStrategy /** * The algorithm used to generate new [ObjectId] instances for this collection. * * For more information, see [ObjectIdGenerator]. * * You can also directly call [newId] on the collection itself. */ override val objectIdGenerator: ObjectIdGenerator override fun newId(): ObjectId = objectIdGenerator.newId() /** * The [KType] instance that corresponds to the collection's document type. * * This property is used by serialization libraries to know the exact type to deserialize, * especially in the presence of type parameters. * * Everyday users should not need to interact with this property directly. */ @LowLevelApi val type: KType // region Type manipulation /** * 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()) * } * ``` */ fun unsafeCast(type: KType): MongoCollection // endregion // region Specializations override fun filter(filter: FilterQuery.() -> Unit): MongoCollection // endregion } /** * 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()) * } * ``` */ inline fun MongoCollection<*>.unsafeCast(): MongoCollection = unsafeCast(typeOf())