rafaelbattesti
rafaelbattesti

Reputation: 552

Elasticsearch 6.4: XContentBuilder fails to close when passed to request.mapping()

I've been hitting the same problem with mappings on 6.4 over and over and I wonder what I've been doing wrong.

I'm simply creating a mapping to add to an index when creating it, but it always breaks my test with a java.lang.IllegalStateException: Failed to close the XContentBuilder

This is how I create the XContentBuilder object for my mapping

[UPDATE]: culprit spotted - mapping missing .endObject()

public XContentBuilder buildIndexMapping() throws IOException{
    XContentBuilder mapping = XContentFactory.jsonBuilder()
        .startObject()
            .startObject("_doc")
                .startObject("_routing")
                    .field( "required", true )
                .endObject()
                .startObject( "properties" )
                    .startObject( "launchCode" )
                        .field( "type", "text" )
                    .endObject()
                    .startObject( "tenant_id" )
                        .field("type", "keyword")
                    .endObject()
                    .startObject( "environment_id" )
                        .field( "type", "keyword" )
                    .endObject()
                    .startObject("app_id")
                        .field( "type", "keyword" )
                    .endObject() //Culprit
                    .startObject( "launch_id" )
                        .field( "type", "keyword" )
                    .endObject()
                    .startObject( "timestamp" )
                        .field( "type", "date" )
                    .endObject()
                    .startObject( "data" )
                        .startObject( "properties" )
                            .startObject( "changed" )
                                .field( "type", "boolean" )
                            .endObject()
                            .startObject( "census_groups" )
                                .field( "type", "object" )
                            .endObject()
                            .startObject( "local_member_id" )
                                .field( "type", "keyword" )
                            .endObject()
                            .startObject( "last_service_counter" )
                                .field( "type", "integer" )
                            .endObject()
                            .startObject( "last_election_counter" )
                                .field( "type", "integer" )
                            .endObject()
                            .startObject( "last_election_update_counter" )
                                .field( "type", "integer" )
                            .endObject()
                            .startObject( "last_membership_counter" )
                                .field( "type", "integer" )
                            .endObject()
                            .startObject( "last_service_config_counter" )
                                .field( "type", "integer" )
                            .endObject()
                            .startObject( "last_service_file_counter" )
                                .field( "type", "integer" )
                            .endObject()
                        .endObject() //properties
                    .endObject() //data
                .endObject() //properties
            .endObject() //doc
        .endObject(); //root
    return mapping;
}

And this is how I create the index request.

public CreateIndexRequest buildIndexRequest( String indexType, List<String> tenantList ) throws IOException{
    SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd" );
    String date = format.format( new Date() );
    String indexName = date + "-" + indexType;
    CreateIndexRequest request = new CreateIndexRequest( indexName );
    request.aliases( buildIndexAliases( tenantList, indexType ) );
    request.settings( buildIndexSettings() );
    request.mapping( "_doc", buildIndexMapping() ); //Test passes when commented out
    return request;
}

And this is how I create the index.

public CreateIndexResponse createIndex( CreateIndexRequest request ){
    CreateIndexResponse response = null;
    try{
        L.info( "Requesting creation of the index" );
        response = client.indices().create( request, RequestOptions.DEFAULT );
        if( response.isAcknowledged() ){
            L.info( "Index created" );
        }
        else{
            L.warn( "Index not created: request not acknowledged" );
        }
    }
    catch( IOException e ){
        L.error( "Could not create Index" );
    }
    return response;
}

And this is my unit test.

@Test
public void createIndexTest() {
    try{
        CreateIndexRequest request = client.buildIndexRequest( "census", this.tenantList );
        CreateIndexResponse response = client.createIndex( request );
        if (!response.isAcknowledged()) {
            fail();
        }
    }
    catch( IOException e ){
        fail();
    }

}

And this is the body of an index request that successfully creates the index with the mapping when using PUT /index through Postman.

{
    "aliases": {
        "census-tenant-a": {
            "filter": {
                "term": {
                    "_routing": "tenant-a"
                }
            },
            "index_routing": "tenant-a",
            "search_routing": "tenant-a"
        },
        "census-tenant-b": {
            "filter": {
                "term": {
                    "_routing": "tenant-b"
                }
            },
            "index_routing": "tenant-b",
            "search_routing": "tenant-b"
        },
        "census-tenant-c": {
            "filter": {
                "term": {
                    "_routing": "tenant-c"
                }
            },
            "index_routing": "tenant-c",
            "search_routing": "tenant-c"
        }
    },
    "mappings": {
        "_doc": {
            "_routing": {
                "required": true
            },
            "properties": {
                "launch_code": {
                    "type": "text"
                },
                "tenant_id": {
                    "type": "keyword"
                },
                "environment_id": {
                    "type": "keyword"
                },
                "app_id": {
                    "type": "keyword"
                },
                "launch_id": {
                    "type": "keyword"
                },
                "timestamp": {
                    "type": "date"
                },
                "data": {
                    "properties": {
                        "changed": {
                            "type": "boolean"
                        },
                        "census_groups": {
                            "type": "object"
                        },
                        "local_member_id": {
                            "type": "keyword"
                        },
                        "last_service_counter": {
                            "type": "integer"
                        },
                        "last_election_counter": {
                            "type": "integer"
                        },
                        "last_election_update_counter": {
                            "type": "integer"
                        },
                        "last_membership_counter": {
                            "type": "integer"
                        },
                        "last_service_config_counter": {
                            "type": "integer"
                        },
                        "last_service_file_counter": {
                            "type": "integer"
                        }
                    }
                }
            }
        }
    },
    "settings": {
        "index": {
            "number_of_shards": "3",
            "number_of_replicas": "1"
        }
    }
}

I really can't understand what is happening. I know the XContentBuilder should be closed after used to avoid memory leaks.

However, I would like to get the index created first and later deal with the resource closure.

I am using the same logic to create aliases and settings, it only doesn't work with the mapping.

Moreover as mentioned, the JSON representation of my XContentBuilder succeeds when I use the Index API with PUT /index.

Thanks!

Upvotes: 0

Views: 1264

Answers (1)

Aashish Manandhar
Aashish Manandhar

Reputation: 11

You probably want to end object started for "app_id" : startObject("app_id")

Upvotes: 1

Related Questions