When to Use
- Bulk order status transition: A fulfilment workflow transitions all orders with
status: "packed" and a past scheduled ship date to status: "delayed". Set UpdateMany: true to update the entire matching set in a single operation, capturing the count of affected records for reporting.
- Feature flag rollout: A SaaS platform workflow activates a new feature for all users in a specific plan tier by setting
featureFlags.newDashboard: true on every matching user document. UpdateMany applies the change to thousands of records atomically without requiring a loop.
- Inventory stock decrement: On each item in a confirmed order, decrement the corresponding product's
stockQty by the ordered quantity using $inc. Set UpdateMany: false to ensure only the first matching product document (by SKU) is decremented.
- Employee department reassignment: An HR workflow reassigns all employees in a closing department to a successor department by updating
department, reportingManagerId, and updatedAt across all affected employee records in one call.
- Subscription plan migration: When a product plan is discontinued, a workflow sets all subscribers from the old plan to the equivalent new plan, appending a migration note to their
planHistory array using $push. The returned modifiedCount is logged for migration verification.
Configuration
Connection
| Field | Required | Description |
ConnectionUri | Required | MongoDB connection string. Reference via {{ $credentials.mongodbAtlas }}. |
Database | Required | Target database name. |
Collection | Required | Target collection name. |
Operation
| Field | Required | Description |
Query | Optional | Filter to select documents to update. Default {} matches all documents in the collection — use with extreme caution. Use precise filters targeting indexed fields. Example: {"status": "packed", "shipDate": {"$lt": "{{ $now }}"}}. |
Update | Optional | Update operators to apply. Must use MongoDB update operator syntax. Common operators: $set (assign fields), $unset (remove fields), $inc (increment), $push (append to array), $pull (remove from array), $addToSet (append if unique), $currentDate (set to current date). Example: {"$set": {"status": "delayed"}, "$currentDate": {"updatedAt": true}}. |
Upsert | Optional | Default false. When true, if no documents match the Query, a new document is created by merging the filter fields with the update expression. Useful for counter or preference documents that should exist after first update. |
UpdateMany | Optional | Default true. When true, all documents matching the filter are updated. When false, only the first matching document (in natural collection order) is updated. Set to false for single-record updates where you want to guarantee only one document is affected. |
Default UpdateMany is true. With an empty Query: {} and UpdateMany: true, this operation updates every document in the collection. Always specify a precise query filter before enabling UpdateMany on production collections. Test query selectivity in a staging environment first.
Sample Configuration
Bulk-transition all overdue "packed" orders to "delayed" status, updating the delayReason and updatedAt fields:
{
"resource": "document",
"operation": "update",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "ecommerce",
"Collection": "orders",
"Query": {
"status": "packed",
"scheduledShipDate": { "$lt": "{{ $now }}" },
"tenantId": "{{ $json.tenantId }}"
},
"Update": {
"$set": {
"status": "delayed",
"delayReason": "carrier_capacity",
"updatedAt": "{{ $now }}"
},
"$push": {
"statusHistory": {
"from": "packed",
"to": "delayed",
"changedAt": "{{ $now }}",
"reason": "carrier_capacity"
}
}
},
"Upsert": false,
"UpdateMany": true
}
Validation Errors
| Error | Cause |
connectionUri is required | ConnectionUri is empty or not provided. |
database is required | Database field is empty. |
collection is required | Collection field is empty. |
Output — Success Port
| Field | Type | Description |
matchedCount | integer | Number of documents that matched the query filter. May be greater than modifiedCount when some matched documents already had the target field value. |
modifiedCount | integer | Number of documents actually modified. A document matches but is not modified if the update would produce no change (e.g. $set to the same value). |
upsertedCount | integer | Number of documents inserted via upsert. 0 when no upsert occurred. 1 when an upsert created a new document. |
upsertedId | string | The MongoDB ObjectID of the upserted document. Empty string when no upsert occurred. |
status | string | Always "success". |
resource | string | Always "document". |
operation | string | Always "update". |
Sample Output
{
"matchedCount": 47,
"modifiedCount": 47,
"upsertedCount": 0,
"upsertedId": "",
"status": "success",
"resource": "document",
"operation": "update"
}
Expression Reference
| Expression | Returns | Description |
{{ $output.BulkUpdateOrders.matchedCount }} | integer | Total documents matched by the query filter. Compare to modifiedCount to detect skipped documents. |
{{ $output.BulkUpdateOrders.modifiedCount }} | integer | Actual number of documents modified. Use in downstream IfCondition: branch on modifiedCount == 0 to handle no-op updates. |
{{ $output.BulkUpdateOrders.upsertedCount }} | integer | Count of upserted documents. 0 means no new documents were created. |
{{ $output.BulkUpdateOrders.upsertedId }} | string | ID of the upserted document — use to reference the newly created record in subsequent steps. |
{{ $output.BulkUpdateOrders.status }} | string | Confirm "success" for conditional logic. |
Node Policies & GuardRails
| Policy | Recommendation |
| Credential storage | Always reference ConnectionUri from BizFirst Credentials Manager. Never embed raw URIs with credentials in workflow node fields. |
| Query filter discipline | Never use an empty Query: {} with UpdateMany: true in production without a deliberate scope. Always include tenant, status, or date range constraints to scope the update to the intended record set. |
| Test with UpdateMany: false first | When building a new bulk update workflow, test the query filter with UpdateMany: false in a staging environment. Confirm the matched document is the expected one before switching to UpdateMany: true. |
| Index query fields | All fields in the Query should have supporting indexes. UpdateMany on large unindexed collections causes full collection scans and may exceed operation time limits. |
| Multi-tenancy | Always include tenantId in the Query to prevent cross-tenant updates in shared collections. |
| modifiedCount vs matchedCount | If matchedCount > modifiedCount, some documents were already in the target state. This is normal. If matchedCount == 0, no documents matched — branch to handle this in an IfCondition node rather than silently continuing. |
| Upsert uniqueness | When Upsert: true, ensure the query filter uniquely identifies a document to prevent unintended new document creation when used with UpdateMany. |
| Timestamp all updates | Always include "$currentDate": {"updatedAt": true} or "$set": {"updatedAt": "{{ $now }}"} in every update to maintain an audit-compatible modification timestamp on each document. |
Workflow Examples
Example 1 — Bulk Feature Flag Rollout
{
"resource": "document",
"operation": "update",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "platform",
"Collection": "users",
"Query": { "planTier": "enterprise", "tenantId": "{{ $json.tenantId }}" },
"Update": {
"$set": {
"featureFlags.advancedReporting": true,
"featureFlags.bulkExport": true,
"updatedAt": "{{ $now }}"
}
},
"Upsert": false,
"UpdateMany": true
}
Example 2 — Single Product Stock Decrement
{
"resource": "document",
"operation": "update",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "ecommerce",
"Collection": "products",
"Query": { "sku": "{{ $json.sku }}", "stockQty": { "$gte": "{{ $json.orderedQty }}" } },
"Update": {
"$inc": { "stockQty": "{{ $json.orderedQty | multiply(-1) }}" },
"$set": { "lastSoldAt": "{{ $now }}" }
},
"Upsert": false,
"UpdateMany": false
}
Example 3 — Subscription Plan Migration
{
"resource": "document",
"operation": "update",
"ConnectionUri": "{{ $credentials.mongodbAtlas }}",
"Database": "billing",
"Collection": "subscriptions",
"Query": {
"planId": "LEGACY_STARTER_V1",
"status": "active",
"tenantId": "{{ $json.tenantId }}"
},
"Update": {
"$set": { "planId": "STARTER_V2", "updatedAt": "{{ $now }}" },
"$push": {
"planHistory": {
"fromPlan": "LEGACY_STARTER_V1",
"toPlan": "STARTER_V2",
"migratedAt": "{{ $now }}",
"reason": "plan_discontinuation"
}
}
},
"Upsert": false,
"UpdateMany": true
}