Skip to main content

Bulk updates and deletions

Bulk mutations change or delete user data across many users in one request. They do not change data immediately. Instead, each bulk mutation submits a bulk job that runs in the background. A job starts as a dry run by default, so you can review what it would do before it touches any data.

For single-user writes that take effect immediately, see Write user data. To follow a job after you submit it, see Monitor and cancel bulk jobs.

Choose a mutation​

Four mutations cover the two kinds of user data and the two kinds of change.

MutationDataEffect
bulkPatchArrayItemsArray itemsMerges changes into the value of every selected item.
bulkDeleteArrayItemsArray itemsDeletes the selected items.
bulkPatchKeyValuesJSON key-value entriesMerges changes into the value of every selected entry.
bulkDeleteKeyValuesJSON key-value entriesDeletes the selected entries.

All four mutations return a job that you poll with the bulkJob query. They require a User Account Token or a Service Account Token with the BULK_DATA_WRITE or ADMIN permission. See Authentication and permissions for details.

Key-value bulk jobs only work on JSON entries; array bulk jobs operate on array items. Playback progress entries are read-only in the Management API and are never selected, changed, or deleted, even when they are stored under the same key.

Tier and scope​

Every bulk mutation takes a tier that you choose explicitly. The tier sets how far the job reaches within your environment. It is never inferred from the filter.

TierReachRequired filter fields
ENVIRONMENTEvery user in the environment, under one key.key
APPLICATIONEvery user's application-scoped data of one application, under one key.key, applicationId
USEROne user's data under one key.key, userId

If the filter lacks a field that the tier requires, the request is rejected with a FILTER_CONSTRAINT error and no job is created.

The tier is not the same as the data scope. The data scope (USER, PROFILE, or APPLICATION) describes how a record was stored. The tier describes how many users a job can reach. You can narrow a job further with the optional filter fields profileId, applicationId, and scope. A scope and the IDs you give with it must agree. For example, an applicationId only matches application-scoped data, and a request whose IDs contradict its scope is rejected with FILTER_CONSTRAINT.

Dry run by default​

Every bulk mutation accepts a dryRun argument that defaults to true. When you omit it, the job only reports what it would match and change, and it writes nothing. To change or delete data, you must pass dryRun: false explicitly.

A dry run is a real job. It is queued, runs, appears in the job list, and counts toward the one-active-job limit described in Monitor and cancel bulk jobs.

Follow these steps for every bulk change.

  1. Select. Use listArrayItems and listKeyValues to look at a sample of the records you want to change, and decide on the tier, filter, and match.
  2. Dry run. Submit the mutation without dryRun, or with dryRun: true.
  3. Review. Poll the job until it finishes, then read results. Check that matched and changed look reasonable for your intent, and check rejected.
  4. Run. Submit the same mutation again with dryRun: false.
  5. Monitor. Poll the job until it reaches a final status, then read results again. Review skipped and rejected.

A dry run is a preview, not a snapshot guarantee. User data can change between the dry run and the real run, and the real run evaluates the data as it is when the job runs. Treat dry run counts as an estimate of scale and a check on your filter and match, not as a promise of what the real run will do.

Patch array items​

bulkPatchArrayItems merges a list of match and patch pairs into the values of array items.

mutation PatchArrayItems($input: BulkPatchArrayItemsInput!) {
bulkPatchArrayItems(input: $input) {
job {
id
operation
tier
dryRun
status
submittedAt
}
}
}
{
"input": {
"tier": "ENVIRONMENT",
"filter": {
"key": "content:favorites"
},
"patches": [
{
"match": { "value": { "contentType": "movie" } },
"set": { "category": "film" },
"remove": ["legacyFlag"]
}
]
}
}

Because the example omits dryRun, it submits a dry run. Add "dryRun": false to the input object to apply the patch.

How patches work​

Each pair has an optional match, an optional set, and an optional remove.

  • match selects the items the pair applies to. Omit it to apply the pair to every item the filter selects.
  • set is an object whose top-level properties are added to the item value or overwrite existing properties.
  • remove is a list of top-level property names to remove from the item value.

Patches only work on object values. Items whose value is not an object are never changed. Properties that a pair does not set or remove stay as they are.

When several pairs match the same item, their patches are merged in list order, so a later pair wins on every property that it also sets or removes. A patch does not change the item order, the item count of the array, or the item's modification date. See Filtering, matching, and patching for the match rules and the limits on set and remove.

Delete array items​

bulkDeleteArrayItems deletes array items. The optional match limits the deletion to items whose content matches. When you omit match, the job deletes every item that the filter selects.

mutation DeleteArrayItems($input: BulkDeleteArrayItemsInput!) {
bulkDeleteArrayItems(input: $input) {
job {
id
operation
tier
dryRun
status
}
}
}
{
"input": {
"tier": "APPLICATION",
"filter": {
"key": "content:recently-viewed",
"applicationId": "1f0d2c1e-3a4b-4c5d-8e6f-7a8b9c0d1e2f"
},
"match": { "itemId": "movie:100" },
"dryRun": true
}
}

This example previews deletion of the item movie:100 from the content:recently-viewed arrays for one application. After reviewing the dry run results, submit it with dryRun: false to apply the deletion. Deleting items also updates the item counts of the arrays they belonged to.

Patch JSON key-value entries​

bulkPatchKeyValues works like bulkPatchArrayItems, but it patches the values of JSON key-value entries. The match of a pair has a required value object that must be a subset of the entry value.

mutation PatchKeyValues($input: BulkPatchKeyValuesInput!) {
bulkPatchKeyValues(input: $input) {
job {
id
operation
tier
dryRun
status
}
}
}
{
"input": {
"tier": "USER",
"filter": {
"key": "settings:playback",
"userId": "8c6d4b2a-1e3f-4a5b-9c7d-0e1f2a3b4c5d"
},
"patches": [
{
"match": { "value": { "quality": "auto" } },
"set": { "quality": "high" }
}
],
"dryRun": true
}
}

Only JSON entries with an object value are patched. Scalar values and progress entries are never changed.

Delete JSON key-value entries​

bulkDeleteKeyValues deletes JSON key-value entries. As with array items, the optional match limits the deletion to entries whose value matches, and omitting it deletes every JSON entry that the filter selects.

mutation DeleteKeyValues($input: BulkDeleteKeyValuesInput!) {
bulkDeleteKeyValues(input: $input) {
job {
id
operation
tier
dryRun
status
}
}
}
{
"input": {
"tier": "ENVIRONMENT",
"filter": {
"key": "settings:legacy"
},
"match": { "value": { "version": 1 } }
}
}

This example is a dry run, because it omits dryRun. Progress entries stored under the same key are not deleted.

What a submitted job returns​

Submitting a mutation returns the queued job. The job id identifies it for the rest of its life. Use it with the bulkJob query and the cancelBulkJob mutation. See Monitor and cancel bulk jobs for how to read progress and results.

Invalid input is rejected before a job is created. The error extensions identify the problem, for example FILTER_CONSTRAINT for a missing tier field, PATCH_CONSTRAINT for the patch list, a pair's set or remove, and MATCH_CONSTRAINT for an empty or malformed match. If another job is already queued or running in the environment, the request fails with BAD_USER_INPUT and the reason JOB_LIMIT. See Errors and limits for the full list.

What a bulk job does not guarantee​

  • A bulk job has no rollback. If a job stops early, the changes it already applied stay in place. See Partial completion and cancellation.
  • A job scans the selected records once. To pick up records that were added or changed afterwards, submit the job again.
  • If a record changes between the scan and the write, the job skips that record. See Skipped and rejected records.

Was this page helpful?