Skip to main content

Update

Update operations modify fields and values in existing documents. mongo4cats provides a fluent, chainable Update builder that compiles to the standard MongoDB update operators.

updateOne and updateMany

updateOne modifies the first document matching the filter; updateMany modifies all matching documents.

import cats.effect.IO
import com.mongodb.client.result.UpdateResult
import mongo4cats.operations.{Filter, Update}
import mongo4cats.models.collection.UpdateOptions

val update = Update
.set("status", "active")
.currentDate("updatedAt")

// Update the first matching document
val result1: IO[UpdateResult] = collection.updateOne(Filter.eq("name", "Alice"), update)

// Update all matching documents
val result2: IO[UpdateResult] = collection.updateMany(Filter.eq("status", "inactive"), update)

Upsert

Pass UpdateOptions().upsert(true) to insert a document if no match is found:

val result: IO[UpdateResult] = collection.updateOne(
Filter.eq("name", "Charlie"),
Update.set("name", "Charlie").set("score", 0),
UpdateOptions().upsert(true)
)

Update operator reference

All methods return a new Update instance and can be chained freely:

Field updates

MethodMongoDB operatorDescription
set(field, value)$setSet a field to a value
unset(field)$unsetRemove a field
rename(field, newName)$renameRename a field
setOnInsert(field, value)$setOnInsertSet on upsert-insert only

Numeric updates

MethodMongoDB operatorDescription
inc(field, n)$incIncrement a numeric field by n
mul(field, n)$mulMultiply a numeric field by n
min(field, value)$minSet field to value if value < current
max(field, value)$maxSet field to value if value > current

Date updates

MethodMongoDB operatorDescription
currentDate(field)$currentDateSet field to current date (BSON Date)
currentTimestamp(field)$currentDateSet field to current timestamp

Array updates

MethodMongoDB operatorDescription
push(field, value)$pushAppend a value to an array
pushEach(field, values)$push $eachAppend multiple values
pushEach(field, values, opts)$push $eachAppend with position/slice/sort options
addToSet(field, value)$addToSetAdd value to array if not already present
addEachToSet(field, values)$addToSet $eachAdd multiple values to set
pull(field, value)$pullRemove all occurrences of a value from array
pullAll(field, values)$pullRemove all occurrences of any of the values
pullByFilter(filter)$pullRemove array elements matching a filter
popFirst(field)$popRemove the first element of an array
popLast(field)$popRemove the last element of an array

Bitwise updates

Update.bitwiseAnd("flags", 0x0F)
Update.bitwiseOr("flags", 0x01)
Update.bitwiseXor("flags", 0xFF)

Chaining multiple updates

Multiple update operations are combined automatically:

val update = Update
.set("status", "reviewed")
.inc("viewCount", 1)
.currentDate("lastSeen")
.push("tags", "featured")

collection.updateOne(Filter.eq("_id", docId), update)

You can also merge two Update values:

val base     = Update.set("status", "active")
val extra = Update.inc("loginCount", 1)
val combined = base.combinedWith(extra)

replaceOne

Replace an entire document (except _id) with a new document:

val newDoc = Document("name" := "Alice", "status" := "active")
collection.replaceOne(Filter.eq("_id", docId), newDoc)

// With upsert
collection.replaceOne(
Filter.eq("name", "Alice"),
newDoc,
ReplaceOptions().upsert(true)
)

Pipeline-based updates (MongoDB 4.2+)

For complex updates that reference the document's own fields, you can use an aggregation pipeline as the update specification by passing a Seq[Bson] directly:

import org.bson.conversions.Bson
import com.mongodb.client.model.Aggregates

val pipeline: Seq[Bson] = Seq(
Aggregates.set("fullName", Document("$concat" := List("$firstName", " ", "$lastName")))
)
collection.updateMany(Filter.empty, pipeline)

Inspecting UpdateResult

collection.updateOne(filter, update).flatMap { result =>
IO.println(s"Matched: ${result.getMatchedCount}, Modified: ${result.getModifiedCount}")
}

Using with a client session (transactions)

client.startSession.use { session =>
session.startTransaction *>
collection.updateMany(session, Filter.eq("status", "pending"), Update.set("status", "processed")) *>
session.commitTransaction
}