Skip to content

Transform Documents

AOSC can run a Painless update script for each source document during backfill and replay.

The built-in transform context is update. It is a 1:1 transform: each source document becomes one target document. Omit transform_script for an identity transform.

Script Context

AOSC compiles the script using OpenSearch's UpdateScript context. Use the standard update-script shape:

VariableMeaning
ctx._sourceMutable source map that will be indexed into the target.
ctx._idDocument ID.
ctx._routingDocument routing, when present.
paramsParameters from transform_script.params.

Use ctx._source.field, not ctx.field.

AOSC currently ignores ctx.op. Setting ctx.op = "noop", ctx.op = "delete", or similar does not skip the target write in the built-in transform path.

Inline Script

bash
curl -X POST 'http://localhost:9200/_plugins/_aosc/my-index-v1/_start' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_index": "my-index-v2",
    "alias": "my-index",
    "transform_script": {
      "type": "inline",
      "source": "ctx._source.author = ctx._source.remove(\"author_name\")"
    }
  }'

With parameters:

bash
curl -X POST 'http://localhost:9200/_plugins/_aosc/my-index-v1/_start' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_index": "my-index-v2",
    "alias": "my-index",
    "transform_script": {
      "type": "inline",
      "source": "ctx._source.status = params.default_status",
      "params": { "default_status": "active" }
    }
  }'

Stored Script

Register a stored script:

bash
curl -X POST 'http://localhost:9200/_scripts/my-transform-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "script": {
      "lang": "painless",
      "source": "ctx._source.author = ctx._source.remove(\"author_name\")"
    }
  }'

Reference it from the migration request:

bash
curl -X POST 'http://localhost:9200/_plugins/_aosc/my-index-v1/_start' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_index": "my-index-v2",
    "alias": "my-index",
    "transform_script": {
      "type": "stored",
      "id": "my-transform-v1"
    }
  }'

Common Patterns

Rename a field:

text
ctx._source.new_field = ctx._source.remove("old_field")

Convert a value to a long:

text
ctx._source.count = Long.parseLong(ctx._source.count.toString())

Create a computed field:

text
ctx._source.full_name = ctx._source.first_name + " " + ctx._source.last_name

Remove a field:

text
ctx._source.remove("unused_field")

Set a default value:

text
if (ctx._source.region == null) {
  ctx._source.region = "unknown"
}

Update a nested field:

text
ctx._source.author.name = ctx._source.author.name.toUpperCase()

Validation Behavior

AOSC compiles and dry-runs the transform at migration start. This catches syntax errors, missing stored scripts, unknown script_context values, and some missing parameter errors before the migration is accepted.

Runtime script errors on individual documents fail the shard worker to avoid silently indexing corrupted data. Test scripts against representative documents before starting a migration.

Released under the Apache 2.0 License.