How to Change a Field Type in an Elasticsearch Index

You cannot change the type of an existing field in an Elasticsearch index. Mappings are immutable once data has been indexed under them - the underlying Lucene segments are typed and cannot be rewritten in place. The supported workflow is to create a new index with the corrected mapping, reindex from the old index into the new one, and swap an alias so the application stays pointed at "the current index" with no code change. Additive mapping changes (new fields) work without a reindex; type changes do not.

When You Actually Need to Change a Field Type

Situation What to do
Field was inferred as text, should be keyword (e.g. an ID) Reindex with explicit mapping
Stored numeric strings, now want sorting/aggregations to work Reindex with long or keyword
Switched from keyword to text for full-text search Reindex; consider text with .keyword subfield
Need to add a new field Put mapping API - no reindex needed
Need a different analyzer on text Reindex; the analyzer is baked into the indexed terms
Need both old and new types for the same field Use a multi-field - no reindex if added before write

If the change is purely additive (a new field, or a new sub-field via fields), the put mapping API handles it on a live index. Only existing field types are immutable.

Prerequisites

  • The user or API key has read on the source index, manage and write on the target index, and manage on aliases if you plan to swap.
  • Disk space for both indices during the migration - roughly 2x the source index size, plus translog and replicas.
  • A read-pause or write-pause plan if the source receives concurrent writes.
  • A recent snapshot, so you have an undo path if something goes wrong.

Step-by-Step: Change a Field Type via Reindex

  1. Snapshot the source index. Mapping mistakes during reindex are unrecoverable without one.

    PUT /_snapshot/my-repo/pre-remap-2026-05-17?wait_for_completion=true
    {
      "indices": "products-v1",
      "include_global_state": false
    }
    
  2. Create the new index with the corrected mapping. Include every field, not just the one being changed, so dynamic mapping does not silently re-introduce the bug.

    PUT /products-v2
    {
      "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 1,
        "refresh_interval": "30s"
      },
      "mappings": {
        "properties": {
          "sku":         { "type": "keyword" },
          "name":        { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } },
          "price":       { "type": "scaled_float", "scaling_factor": 100 },
          "category":    { "type": "keyword" },
          "created_at":  { "type": "date" },
          "in_stock":    { "type": "boolean" }
        }
      }
    }
    
  3. Reindex from the old index to the new one. Use slices=auto for parallelism and wait_for_completion=false so you get a task ID for tracking.

    POST /_reindex?wait_for_completion=false&slices=auto
    {
      "source": { "index": "products-v1" },
      "dest":   { "index": "products-v2" }
    }
    

    Response: { "task": "<task-id>" }. Track progress with GET /_tasks/<task-id>.

  4. If a field needs transformation (not just retyping), use an inline script.

    POST /_reindex?wait_for_completion=false&slices=auto
    {
      "source": { "index": "products-v1" },
      "dest":   { "index": "products-v2" },
      "script": {
        "source": "if (ctx._source.price instanceof String) { ctx._source.price = Float.parseFloat(ctx._source.price) }",
        "lang":   "painless"
      }
    }
    
  5. Verify document counts match.

    GET /products-v1/_count
    GET /products-v2/_count
    

    They should be equal (or off by however many were rejected during reindex; check failures in the task response).

  6. Spot-check the new field type works.

    GET /products-v2/_search
    { "aggs": { "by_sku": { "terms": { "field": "sku" } } } }
    

    Aggregations and sorts on the corrected field should now succeed where they previously failed.

  7. Swap the alias. If your application reads products (an alias), point it at the new index in one atomic operation:

    POST /_aliases
    {
      "actions": [
        { "remove": { "index": "products-v1", "alias": "products" } },
        { "add":    { "index": "products-v2", "alias": "products" } }
      ]
    }
    
  8. Delete the old index once you are confident the new one is healthy and the application is fully cut over:

    DELETE /products-v1
    

    Keep the snapshot from step 1 for at least one retention window before purging.

What About Mapping Coercion?

If you change string-looking data to a numeric type during reindex, Elasticsearch will attempt to coerce values. Coercion failures fail the document, not the reindex. The failures array in the task response shows what was rejected and why. For partial-coercion problems, use an ingest pipeline on the reindex destination, or a Painless script in the reindex body, to clean up values before they hit the new mapping.

Reindex in Production: What to Watch For

A full reindex copies every document, runs analyzers again, and rebuilds every segment. On terabyte-scale indices it can take hours to days. The cluster effects are predictable but not always small: heap pressure on the source for the scrolled scan, sustained write load on the destination, and an inflated translog if refresh_interval is at the default of 1s. Always set the destination's refresh_interval to 30s or -1 during the load and restore it afterward.

The biggest production failure mode is silent rejection: documents that fail to fit the new mapping are recorded in failures and then ignored. If you do not check the task output, you can ship a "successful" reindex that quietly dropped a percentage of the data.

Run Field-Type Changes Safely with Pulse

Pulse is an AI DBA for Elasticsearch and OpenSearch. Before and during a reindex driven by a field-type change, Pulse:

  • Verifies cluster capacity for the operation: disk for the ~2x intermediate footprint, heap for the scrolled scan, write thread pool headroom for sustained bulk indexing
  • Surfaces concurrent operations that could collide - active ILM rollover on the source, snapshots in progress, or another reindex sharing the same nodes
  • Tracks the operation's progress and impact on production traffic in real time: document counts on source and destination, the failures array, indexing latency, search latency p95
  • Recommends throttling or pausing the reindex if production search latency starts climbing, and alerts when the new mapping is silently dropping documents

After cutover, Pulse continues to watch for fields the new mapping rejects from incoming writes.

Start a free trial before your next field-type change.

Common Mistakes

  1. Attempting PUT /<index>/_mapping to change an existing field's type. Elasticsearch responds with illegal_argument_exception: mapper [field] cannot be changed.
  2. Skipping the alias. If the application uses the underlying index name, the cutover is a deploy. If it uses an alias, the cutover is one API call.
  3. Letting dynamic mapping re-introduce the bad type. When the new index is created without explicit mappings, the first document re-types the field exactly as the old one. Always be explicit.
  4. Not snapshotting before reindex. If the new mapping turns out to be wrong, you have no clean rollback.
  5. Ignoring the failures array. Documents that fail to fit the new mapping are not retried automatically; you have to extract them from the task response and decide what to do.
  6. Reindexing into the same index name. You cannot. The target must be a different index. Use the alias swap to make it look the same to the application.

Frequently Asked Questions

Q: Can I change a field type directly in an existing Elasticsearch index?
A: No. Mappings are immutable once data is indexed. The supported workflow is to create a new index with the corrected mapping, reindex from the old index into the new one, and swap an alias. Additive changes (new fields) are allowed via the put mapping API.

Q: How long does it take to change a field type in a large index?
A: It depends on document count and cluster size, but reindex is the bottleneck. Expect hours for hundreds of millions of documents, days for billions. Run with slices=auto, set refresh_interval: -1 on the destination during the load, and monitor the task with GET /_tasks/<task-id>.

Q: What happens to documents whose values cannot be coerced to the new field type?
A: They appear in the failures array of the reindex task response. Reindex does not abort on per-document failures by default. Plan to inspect failures and either fix the source documents or run an ingest pipeline to clean them up during the reindex.

Q: Can I keep the same field name with two different types using multi-fields?
A: Yes, but only if the multi-field is configured before data is indexed under it. A text field can have a .keyword subfield from day one. Once data is indexed, you cannot add a new analyzer-driven subfield without a reindex.

Q: Will changing a field type break my existing queries and dashboards?
A: Sometimes. A text to keyword change typically fixes broken aggregations but breaks any full-text match queries that depended on analyzed terms. Review every place the field is used in queries, aggregations, sorts, and runtime fields before swapping the alias.

Q: Can I change a field type without downtime?
A: Yes, with the alias-swap pattern. The application reads from an alias, you reindex into a new index in the background, and a single _aliases call swaps the pointer atomically. Writes that occur during the reindex either need to be replayed to the new index or paused for the cutover.

Q: Does the put mapping API support any field type changes at all?
A: It supports additive changes only: new fields, new multi-field subfields on fields that have no data indexed yet, and certain parameter changes that do not affect the indexed representation. Changing the type of an existing field is not supported and produces illegal_argument_exception.

Q: What's the best tool to safely change a field type in a large Elasticsearch index?
A: Pulse is purpose-built for this. It is an AI DBA for Elasticsearch and OpenSearch that pre-checks disk, heap, and thread-pool capacity for the reindex, tracks failures against the source and destination counts, and alerts when a mapping change reindex is silently dropping documents - turning a long, opaque migration into a continuously verifiable operation.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.