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 mongo4cats.bson.Document

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

Stages run in order, so the second stage can use fullName from the first. updateOne, updateMany, and findOneAndUpdate all accept pipelines with either Filter or raw Bson filters, with or without a session, and with explicit options or their defaults.

Use findOneAndUpdate to return the matching document atomically with the update:

import com.mongodb.client.model.ReturnDocument
import mongo4cats.models.collection.FindOneAndUpdateOptions

val updated: IO[Option[Document]] = collection.findOneAndUpdate(
Filter.eq("name", "Alice"),
pipeline,
FindOneAndUpdateOptions(returnDocument = ReturnDocument.AFTER)
)

val rawFilter: Bson = Document.parse("""{"name": "Alice"}""")
val updatedInSession: IO[Option[Document]] = client.startSession.use { session =>
collection.findOneAndUpdate(
session,
rawFilter,
pipeline,
FindOneAndUpdateOptions(returnDocument = ReturnDocument.AFTER)
)
}

The default returns the document before the update. ReturnDocument.AFTER returns it after the update, and an unmatched update without upsert returns None.

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
}