javaspringspring-bootelasticsearchspring-data-elasticsearch

Spring Data Elasticsearch doesn't implicitly create mapping from annotations


I'm trying to work with Spring Data Elasticsearch 4.0.1 and trying to figure out how and when the Elasticsearch mappings are being created by Spring or if they are being created by Spring.

If I have an entity like so:

@Document(indexName = "companies")
public class CompanyEntity {

  @Id
  private final String id;

  @MultiField(
      mainField = @Field(type = Text),
      otherFields = {
          @InnerField(suffix = "raw", type = Keyword)
      }
  )
  private final String companyName;

  @PersistenceConstructor
  public CompanyEntity(String id, String companyName) {
    this.id = id;
    this.companyName = companyName;
  }

  public String getId() {
    return id;
  }

  public String getCompanyName() {
    return companyName;
  }
}

I was under the impression that Spring would implicitly create the mapping for this index but I seem to be mistaken. Elasticsearch still creates the mapping for this index.

{
    "companies": {
        "mappings": {
            "properties": {
                "_class": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "companyName": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "id": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                }
            }
        }
    }
}

Clearly from the above, the suffix in the InnerField annotation is not used and the ignore_above value is not used as well since it defaults to -1, and Elasticsearch would remove this field altogether if trying to set ignore_above to -1.

The only way I was able to get the mapping for the above annotations was to explicitly set the mapping myself.

@Autowired private ElasticsearchOperations operations;

Document mapping = operations.indexOps(CompanyEntity.class).createMapping();
operations.indexOps(CompanyEntity.class).putMapping(mapping); 

Which yields the expected mapping:

{
    "companies": {
        "mappings": {
            "properties": {
                "_class": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "companyName": {
                    "type": "text",
                    "fields": {
                        "raw": {
                            "type": "keyword"
                        }
                    }
                },
                "id": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                }
            }
        }
    }
}

Which is fine, but feels a bit odd to me as I could not find any details of this approach in the official Spring Data Elasticsearch docs. And the JavaDocs are kinda void of any details.

Is this the right approach to installing mappings into Elasticsearch from Spring Data?


Solution

  • If you are using the ElasticsearchOperations to work with, then this is the right way to create the mapping. In version 4.1 there will be an additional method

    boolean putMapping(Class<?> clazz)
    

    which combines the two steps of creating and writing the mapping.

    ElasticsearchOperations and IndexOperations - or better the implementations - are the more low level access to Elasticsearch in Spring Data Elasticsearch - create an index, put the mapping, write an entity etc. These are elemantary operations. You have the full control which operations are executed, but it's your respsonsibility to execute them.

    Building on top of that is the repository support. If you define a repository interface

    interface CompanyRepository extends ElasticsearchRepository<CompanyEntity, String>{}
    

    which you inject in one of your classes

    @Autowired CompanyRepository repository;
    

    then on application startup Spring Data Elasticsearch will create an implementation of this interface and will check if the index defined by the entity's @Document annotation exists. If not, it will create the index and write the mapping - The @Document annotation has an argument createIndex which by default is true.

    So for autocreation you must use the repository, the repository support also makes use of the ElasticsearchOperations to offer things like query derivation from method names, paging support and more.