Update Operators
Update operators allow you to perform granular updates on component model data beyond the default $set behavior. Instead of only replacing field values, you can increment counters, push to arrays, remove elements, and more — all within a single update request.
Overview
By default, all fields sent in the data payload of an update request replace the existing field value entirely.
With update operators, you can include $-prefixed keys directly in your data payload to perform different types of updates. Regular fields (without the $ prefix) continue to behave as before.
Supported Operators
| Operator | Description | Example Value |
|---|---|---|
$inc |
Increment a numeric field by the given amount | { "counter": 1, "views": -1 } |
$push |
Append a value to an array field | { "tags": "new-tag" } |
$pull |
Remove elements from an array matching a condition | { "tags": "old-tag" } |
$addToSet |
Add a value to an array only if it doesn't exist | { "categories": "cat-1" } |
$pop |
Remove the first (-1) or last (1) element of an array |
{ "queue": 1 } |
$min |
Update only if the new value is less than current | { "low_score": 50 } |
$max |
Update only if the new value is greater than current | { "high_score": 100 } |
$mul |
Multiply a numeric field by the given factor | { "price": 1.1 } |
Usage
Operators are passed inline within the data object. Any key starting with $ is treated as an operator — its value must be an object mapping field names to their operator-specific values.
Single Update
Concurrent Update
Works the same way — just include version for optimistic locking:
Modify (Filter-Based Update)
Operators work with modify for bulk filter-based updates:
Combining Operators with Regular Fields
You can freely mix regular fields and operators in the same request:
{
"data": {
"name": "John",
"lastname": "Doe",
"$inc": { "login_count": 1 },
"$push": { "login_history": "2026-02-10T12:00:00Z" }
}
}
This will:
- Set
nameto"John"andlastnameto"Doe" - Increment
login_countby1 - Push the timestamp to the
login_historyarray
Operator Details
$inc — Increment
Adds the specified value to a numeric field. Use negative values to decrement.
Note
If the field does not exist, it will be created with the increment value.
$push — Append to Array
Appends a value to the end of an array field.
$pull — Remove from Array
Removes all elements from an array that match the specified condition.
$addToSet — Add Unique to Array
Adds a value to an array only if it is not already present.
$pop — Remove First/Last from Array
Removes the first (-1) or last (1) element from an array.
$min / $max — Conditional Update
Updates the field only if the provided value is less than ($min) or greater than ($max) the current value.
$mul — Multiply
Multiplies the value of a field by the specified number.
Error Handling
| Error | Cause |
|---|---|
unsupported update operator: $foo |
An unrecognized operator was used |
$unset is not allowed: fields defined in the schema cannot be removed |
$unset is banned — use a regular field update to clear a value instead |
operator $inc cannot target protected field: __version__ |
Attempting to modify a protected field |
operator $push value must be an object (map of field→value) |
The operator value is not a valid object |
Aggregate Expression Updates ($aggregate)
v4 only
Aggregate expression updates are supported exclusively by the v4 component model API.
For advanced update scenarios — such as computing a field's new value based on its current value using conditional logic, array transformations, or mathematical aggregation — you can supply a $aggregate array. Each element is a plain object of { fieldName: expression } pairs.
{
"data": {
"$aggregate": [
{
"avg_rating": { "$avg": "$ratings" },
"tier": {
"$cond": {
"if": { "$gte": ["$avg_rating", 4.5] },
"then": "platinum",
"else": {
"$cond": {
"if": { "$gte": ["$avg_rating", 3.0] },
"then": "gold",
"else": "standard"
}
}
}
}
}
]
}
}
Each element maps one or more field names to expressions. Multiple elements become sequential stages, so later elements can reference fields computed by earlier ones.
Usage
import { Component } from "@ptkl/sdk"
const products = new Component("products")
await products.update(uuid, {
$aggregate: [
{
// Recompute average rating from the stored ratings array
avg_rating: { $avg: "$ratings" },
},
{
// Derive a tier label from the avg_rating computed above
tier: {
$cond: {
if: { $gte: ["$avg_rating", 4.5] },
then: "platinum",
else: {
$cond: {
if: { $gte: ["$avg_rating", 3.0] },
then: "gold",
else: "standard",
},
},
},
},
},
],
});
curl -X POST /v4/system/component/products/model/{uuid} \
-H "Content-Type: application/json" \
-d '{
"data": {
"$aggregate": [
{
"avg_rating": { "$avg": "$ratings" }
},
{
"tier": {
"$cond": {
"if": { "$gte": ["$avg_rating", 4.5] },
"then": "platinum",
"else": {
"$cond": {
"if": { "$gte": ["$avg_rating", 3.0] },
"then": "gold",
"else": "standard"
}
}
}
}
}
]
}
}'
Behaviour
- Provide field names and expressions directly — no wrapping required.
- If the resulting document fails field validation, the update is rejected and no changes are persisted.
- Multiple elements become sequential stages, so a later element can reference a field computed by an earlier one.
$aggregateand regular update operators ($inc,$push, etc.) cannot be combined in a single request.
Error Handling
| Error | Cause |
|---|---|
$aggregate must be an array of field-expression objects |
The $aggregate value is not an array |
$aggregate must contain at least one field-expression object |
An empty array was provided |
$aggregate element 0 must be an object of field → expression pairs |
An array element is not a plain object |
$aggregate cannot target protected field "uuid" |
A protected field was used as a key |
$aggregate cannot target password field "…" |
A field of type password was used as a key — password fields cannot be set via pipeline expressions |
cannot combine $aggregate pipeline with update operators in the same request |
Both $aggregate and operator keys (e.g. $inc) were present |