Skip to content

rebuild() of bulk operations (IndexOperation, CreateOperation, UpdateOperation) drops most fields #1353

Open

Description

@rursprung

Java API client version

9.5.4 (also current main)

Java version

25

Elasticsearch Version

9.5.4

Problem description

(full disclosure: analysis by copilot)

rebuild() should return a builder "initialized with field values of this instance". For the bulk operation classes it doesn't: most fields are lost.

Expected: op.rebuild().build() is equal to op.

Actual:

  • IndexOperation and CreateOperation: the private Builder(IndexOperation<TDocument> instance) / Builder(CreateOperation<TDocument> instance) constructors are empty. Nothing is copied:

    • fields inherited from BulkOperationBase: id, index, routing, ifSeqNo, ifPrimaryTerm, version, versionType;
    • fields inherited from WriteOperation: pipeline, requireAlias, dynamicTemplates;
    • their own fields: document and tDocumentSerializer.

    Because document is required, rebuild().build() throws a MissingRequiredPropertyException. If you set a new document first (for example .document(binaryData)), it builds without errors, but without _id, _index, routing and so on.

  • UpdateOperation: only requireAlias and retryOnConflict are copied. The inherited BulkOperationBase fields, action, binaryAction and the serializers are lost.

This looks like a code generator problem. Fields inherited from a generated base class are never copied, and neither are fields of the generic type parameter. Request classes without a base class, such as IndexRequest, copy their fields correctly.

Reproducer:

IndexOperation<Map<String, Object>> op = IndexOperation.of(b -> b
    .index("my-index")
    .id("1")
    .routing("r1")
    .ifSeqNo(5L)
    .ifPrimaryTerm(1L)
    .pipeline("my-pipeline")
    .document(Map.of("field", "value")));

// throws MissingRequiredPropertyException: Missing required property 'IndexOperation.document'
op.rebuild().build();

// builds without errors, but all values are lost
IndexOperation<?> copy = op.rebuild().document(Map.of("field", "value")).build();
System.out.println(copy.index());   // null, expected "my-index"
System.out.println(copy.id());      // null, expected "1"
System.out.println(copy.routing()); // null, expected "r1"
System.out.println(copy.pipeline()); // null, expected "my-pipeline"

CreateOperation behaves the same. For UpdateOperation, rebuild().build() does not throw, but only requireAlias and retryOnConflict are kept:

UpdateOperation<Object, Object> op = UpdateOperation.of(b -> b
    .index("my-index")
    .id("1")
    .routing("r1")
    .retryOnConflict(3)
    .action(a -> a.docAsUpsert(true)));

UpdateOperation<?, ?> copy = op.rebuild().build();
System.out.println(copy.index());           // null, expected "my-index"
System.out.println(copy.id());              // null, expected "1"
System.out.println(copy.routing());         // null, expected "r1"
System.out.println(copy.retryOnConflict()); // 3
System.out.println(copy.action());          // null

Context: I hit this in Spring Data Elasticsearch. Like the client's own BulkIngester, it needs to replace the document of an operation with a pre-serialized BinaryData so that it can measure its size. The obvious way is op.rebuild().document(binaryData).build(), but that silently drops _id and _index. So we copy all properties by hand, as IngesterOperation.copyIndexProperties() / copyBaseProperties() do.
see spring-projects/spring-data-elasticsearch#3357

Note: rebuild() also returns the raw type Builder instead of Builder<TDocument>, so callers get unchecked warnings.

Activity

  1. added theissue type on Oct 14, 2026
  2. l-trotta commented on Oct 14, 2026

    @l-trotta
    Contributor

    Hello, thank you for the report!

    Fields inherited from a generated base class are never copied

    Then most likely this affects more classes then just bulk operations. I'll look into this and hopefully fix it before the next release!

  3. self-assigned this
    on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions