Grammin
Grammin

Reputation: 12205

How can I set a parent / child relationship using the Elastic Search Java API?

I'm trying to create two documents, one build a child of the other, using the Elastic Search Java API.

My code looks something like this:

public void createRecord(Road road, Car car) throws Exception{
  ObjectMapper objectMapper = new ObjectMapper();

  String roadJson = objectMapper.writeValueAsString(road);
  String carJson = objectMapper.writeValueAsString(car);

  Client client = TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300));

  IndexResponse response = client.prepareIndex("myIndex", "roadType").setSource(roadJson).get();

  client.prepareIndex("myIndex", "carType").setParent(response.getId()).setSource(carJson).get();
}

This code successfully creates a road document but when it goes to create a car document I get the error:

java.lang.IllegalArgumentException: Can't specify parent if no parent field has been configured

How can I properly create two Elastic documents that form a parent / child relationship with the ES Java API?

Upvotes: 3

Views: 3992

Answers (2)

Grammin
Grammin

Reputation: 12205

I had to create a custom mapper for the parent / child relationship. This still preserves the dynamic mapping that can happen on document creation.

public void createRecord(Road road, Car car) throws Exception{
  ObjectMapper objectMapper = new ObjectMapper();

  String roadJson = objectMapper.writeValueAsString(road);
  String carJson = objectMapper.writeValueAsString(car);

  Client client = TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300));

  if(!client.admin().indices().prepareExists("myIndex").get().isExists()){ 
     XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().startObject().startObject("roadType").endObject().endObject();
     XContentBuilder xContentBuilder2 = XContentFactory.jsonBuilder().startObject().startObject("carType").startObject("_parent").field("type", "roadType").endObject().endObject().endObject();

     client.admin().indices().create(new CreateIndexRequest("myIndex")).get();
     client.admin().indices().preparePutMapping("myIndex").setType("carType").setSource(xContentBuilder2).get();
     client.admin().indices().preparePutMapping("myIndex").setType("roadType").setSource(xContentBuilder).get();
  }

  IndexResponse response = client.prepareIndex("myIndex", "roadType").setSource(roadJson).get();

  client.prepareIndex("myIndex", "carType").setParent(response.getId()).setSource(carJson).get();
}

This creates a custom mapper that sets up the parent child relationship between roadType and carType while still allowing you to dynamically map additional properties. Make sure you add the child mapping first or it won't work.

Upvotes: 0

ryanlutgen
ryanlutgen

Reputation: 3041

It looks like you are not configuring your "carType" mapping to specify "roadType" as a parent.

"mappings": {
    "roadType": {
        "properties": {
        }
    },
    "carType": {
        "_parent": {
            "type": "roadType"
        },
        "properties": {
            "make": {
                "type": "string"
            },
            "model": {
                "type": "string"
            }
        }
    }
}

For more info, see here: https://www.elastic.co/guide/en/elasticsearch/guide/current/parent-child-mapping.html

Upvotes: 2

Related Questions