spring-data-elasticsearch

Spring Data Elasticsearch : detect mapping differences


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?


Solution

  • 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 JsonNodes 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.