In Spring Data Elasticsearch is there a way of detecting that the mapping on the index does not match the mapping created from the Entity object?
i.e.
I allowed Spring Data Elasticsearch to create the mapping originally from the entity model annotated with @Document
and @Field
At a later point I add a new field to the model, but do not update the index mapping, this new field then won't be correctly configured until I re-index to the new mapping
So is there a way to detect such discrepancies so I will know which indexes need to have their mappings re-created, and documents re-indexed?
This is an interesting question, but the answer is not too complicated.
Let's assume we have a Foo
entity class to be stored in an foo
index and a FooRepository
that uses this class. On
application startup, when the index does not exist, it will be created with the mapping derived from the entity.
In order to detect changes in the maping derived from the class and the one stored in Elasticsearch you can use an approach like this:
@Component
public class FooMappingValidator {
private static final Logger LOGGER = LoggerFactory.getLogger(FooMappingValidator.class);
private final ObjectMapper objectMapper = new ObjectMapper();
private final ElasticsearchOperations operations;
public FooMappingValidator(ElasticsearchOperations operations) {
this.operations = operations;
}
@Autowired
public void checkFooMapping() {
var indexOperations = operations.indexOps(Foo.class);
if (indexOperations.exists()) {
LOGGER.info("checking if mapping for Foo changed");
var mappingFromEntity = indexOperations.createMapping();
var mappingFromEntityNode = objectMapper.valueToTree(mappingFromEntity);
var mappingFromIndexNode = objectMapper.valueToTree(indexOperations.getMapping());
if (!mappingFromEntityNode.equals(mappingFromIndexNode)) {
LOGGER.info("mapping for class Foo changed!");
indexOperations.putMapping(mappingFromEntity);
}
}
}
}
This is a Spring component that has ElasticsearchOperations
injected and that has a method that is annotated with
@Autowired
. An autowired method is executed after all the dependencies have been injected in the beans. This means
it runs before the normal application logic is started.
In this method we first get an IndexOperations
instance for our entity class. Next we check if the index exists,
if it doesn't, we do not need to check.
In the next step we get the current mapping from the entity and convert it to a JsonNode
and do the same with the
mapping we retrieve from Elasticsearch. We use JsonNode
s here because the have an equals()
method that
does the comparison we need.
If we detect that the both mappings are different, we write the new one to the index with the putMapping()
method.
NOTE:
This only works when new properties are added to the entity, as existing mappings cannot be changed in Elasticsearch, there you'd need reindexing.