All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.models.BulkWrite.kt Maven / Gradle / Ivy

/*
 * Copyright (c) 2024, 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.dsl.models

import opensavvy.ktmongo.bson.BsonContext
import opensavvy.ktmongo.dsl.DangerousMongoApi
import opensavvy.ktmongo.dsl.KtMongoDsl
import opensavvy.ktmongo.dsl.LowLevelApi
import opensavvy.ktmongo.dsl.expr.FilterOperators
import opensavvy.ktmongo.dsl.expr.UpdateOperators
import opensavvy.ktmongo.dsl.expr.UpsertOperators
import opensavvy.ktmongo.dsl.options.BulkWriteOptions
import opensavvy.ktmongo.dsl.options.InsertOneOptions
import opensavvy.ktmongo.dsl.options.UpdateOptions
import opensavvy.ktmongo.dsl.tree.CompoundNode
import opensavvy.ktmongo.dsl.tree.Node
import opensavvy.ktmongo.dsl.tree.acceptAll

sealed interface AvailableInBulkWrite : Node

/**
 * Performing multiple write operations in a single request.
 *
 * ### Example
 *
 * ```kotlin
 * users.bulkWrite {
 *     updateOne({ User::name eq "foo" }) {
 *         User::age set 18
 *     }
 *
 *     upsertOne({ User::name eq "bob" }) {
 *         User::age setOnInsert 18
 *         User::age inc 1
 *     }
 * }
 * ```
 *
 * ### Filtered writes
 *
 * If we have multiple writes that share a similar filter, we can extract it to be common between them.
 *
 * ```kotlin
 * users.bulkWrite {
 *     updateOne({ User::name eq "foo" }) {
 *         User::age set 18
 *     }
 *
 *     filtered({ User::isAlive eq true }) {
 *         updateMany({ User::name eq "bar" }) {
 *             User::age inc 2
 *         }
 *
 *         updateOne({ User::name eq "baz" }) {
 *             User::age inc 1
 *         }
 *     }
 * }
 * ```
 *
 * To learn more, see [filtered].
 *
 * ### External resources
 *
 * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/)
 *
 * @see BulkWriteOptions Options
 */
@KtMongoDsl
class BulkWrite private constructor(
	val context: BsonContext,
	private val globalFilter: FilterOperators.() -> Unit,
	val options: BulkWriteOptions,
) : CompoundNode> {

	private val _operations = ArrayList>()
	val operations: Sequence> get() = _operations.asSequence()

	constructor(context: BsonContext, globalFilter: FilterOperators.() -> Unit) : this(context, globalFilter, BulkWriteOptions(context))

	@LowLevelApi
	@DangerousMongoApi
	override fun accept(node: AvailableInBulkWrite) {
		_operations += node
	}

	/**
	 * Declares a [filter] that is shared between all children [operations].
	 *
	 * ### Example
	 *
	 * Sometimes, we have multiple operations in a single bulk write that share the same filter.
	 * This method allows to declare it a single time.
	 *
	 * ```kotlin
	 * users.bulkWrite {
	 *     filtered({ User::isAlive eq true }) {
	 *         updateOne { /* … */ }
	 *         updateOne { /* … */ }
	 *     }
	 * }
	 * ```
	 */
	fun filtered(
		filter: FilterOperators.() -> Unit,
		operations: BulkWrite.() -> Unit,
	) {
		val parent = this

		val child = BulkWrite(
			context = context,
			globalFilter = {
				parent.globalFilter(this)
				filter()
			}
		)

		child.operations()

		@OptIn(LowLevelApi::class, DangerousMongoApi::class)
		acceptAll(child.operations.asIterable())
	}

	/**
	 * Inserts a [document].
	 *
	 * ### Example
	 *
	 * ```kotlin
	 * class User(
	 *     val name: String,
	 *     val age: Int,
	 * )
	 *
	 * collection.bulkWrite {
	 *     insertOne(User(name = "Bob", age = 18))
	 * }
	 * ```
	 *
	 * ### External resources
	 *
	 * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#insertone)
	 *
	 * @see insertMany Insert multiple documents.
	 * @see updateOne Update an existing document.
	 * @see upsertOne Update or insert a document.
	 */
	@OptIn(DangerousMongoApi::class, LowLevelApi::class)
	fun insertOne(
		document: Document,
		options: InsertOneOptions.() -> Unit = {},
	) {
		val model = InsertOne(context, document)

		model.options.options()

		accept(model)
	}

	/**
	 * Inserts multiple [documents] in a single operation.
	 *
	 * ### Example
	 *
	 * ```kotlin
	 * class User(
	 *     val name: String,
	 *     val age: Int,
	 * )
	 *
	 * collection.bulkWrite {
	 *     insertMany(listOf(User(name = "Bob", age = 18), User(name = "Alice", age = 17)))
	 * }
	 * ```
	 *
	 * ### External resources
	 *
	 * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#insertone)
	 *
	 * @see insertOne Insert a single document.
	 * @see updateMany Update multiple documents.
	 */
	fun insertMany(
		documents: Iterable,
		options: InsertOneOptions.() -> Unit = {},
	) {
		for (document in documents) {
			insertOne(document, options)
		}
	}

	/**
	 * Inserts multiple [documents] in a single operation.
	 *
	 * ### Example
	 *
	 * ```kotlin
	 * class User(
	 *     val name: String,
	 *     val age: Int,
	 * )
	 *
	 * collection.bulkWrite {
	 *     insertMany(User(name = "Bob", age = 18), User(name = "Alice", age = 17))
	 * }
	 * ```
	 *
	 * ### External resources
	 *
	 * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#insertone)
	 *
	 * @see insertOne Insert a single document.
	 * @see updateMany Update multiple documents.
	 */
	fun insertMany(
		vararg documents: Document,
		options: InsertOneOptions.() -> Unit = {},
	) {
		insertMany(documents.asList(), options)
	}

	/**
	 * Updates all documents that match [filter] according to [update].
	 *
	 * ### Example
	 *
	 * ```kotlin
	 * class User(
	 *     val name: String,
	 *     val age: Int,
	 * )
	 *
	 * collection.bulkWrite {
	 *     updateMany(
	 *         filter = { User::name eq "Patrick" },
	 *         update = {
	 *             User::age set 15
	 *         }
	 *     )
	 * }
	 * ```
	 *
	 * ### External resources
	 *
	 * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#updateone-and-updatemany)
	 *
	 * @see updateOne Update a single document.
	 */
	@OptIn(DangerousMongoApi::class, LowLevelApi::class)
	fun updateMany(
		options: UpdateOptions.() -> Unit = {},
		filter: FilterOperators.() -> Unit = {},
		update: UpdateOperators.() -> Unit,
	) {
		val model = UpdateMany(context)

		model.options.options()
		model.filter.globalFilter()
		model.filter.filter()
		model.update.update()

		accept(model)
	}

	/**
	 * Updates a single document that matches [filter] according to [update].
	 *
	 * If multiple documents match [filter], only the first one found is updated.
	 *
	 * ### Example
	 *
	 * ```kotlin
	 * class User(
	 *     val name: String,
	 *     val age: Int,
	 * )
	 *
	 * collection.bulkWrite {
	 *     updateOne(
	 *         filter = { User::name eq "Patrick" },
	 *         update = {
	 *             User::age set 15
	 *         }
	 *     )
	 * }
	 * ```
	 *
	 * ### External resources
	 *
	 * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#updateone-and-updatemany)
	 *
	 * @see updateMany Update multiple documents.
	 * @see insertOne Create a new document.
	 * @see upsertOne Create a document if none are found.
	 */
	@OptIn(DangerousMongoApi::class, LowLevelApi::class)
	fun updateOne(
		options: UpdateOptions.() -> Unit = {},
		filter: FilterOperators.() -> Unit = {},
		update: UpdateOperators.() -> Unit,
	) {
		val model = UpdateOne(context)

		model.options.options()
		model.filter.globalFilter()
		model.filter.filter()
		model.update.update()

		accept(model)
	}

	/**
	 * Updates a single document that matches [filter] according to [update].
	 *
	 * If multiple documents match [filter], only the first one found is updated.
	 *
	 * If no documents match [filter], a new one is created.
	 *
	 * ### Example
	 *
	 * ```kotlin
	 * class User(
	 *     val name: String,
	 *     val age: Int,
	 * )
	 *
	 * collection.bulkWrite {
	 *     upsertOne(
	 *         filter = { User::name eq "Patrick" },
	 *         update = {
	 *             User::age set 15
	 *         }
	 *     )
	 * }
	 * ```
	 *
	 * If a document exists that has the `name` of "Patrick", its age is set to 15.
	 * If none exist, a document with `name` "Patrick" and `age` 15 is created.
	 *
	 * ### External resources
	 *
	 * - [Official documentation](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#updateone-and-updatemany)
	 * - [The behavior of upsert functions](https://www.mongodb.com/docs/manual/reference/method/db.collection.update/#insert-a-new-document-if-no-match-exists--upsert-)
	 *
	 * @see insertOne Always create a new document.
	 * @see updateOne Do nothing if no matching documents are found.
	 */
	@OptIn(DangerousMongoApi::class, LowLevelApi::class)
	fun upsertOne(
		options: UpdateOptions.() -> Unit = {},
		filter: FilterOperators.() -> Unit = {},
		update: UpsertOperators.() -> Unit,
	) {
		val model = UpsertOne(context)

		model.options.options()
		model.filter.globalFilter()
		model.filter.filter()
		model.update.update()

		accept(model)
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy