Spring Data Elasticsearch custom converters
It happens quite often to have Elasticsearch fields with a small number of possible values so it is natural to model these as enums.
Spring Data maps enum by default using the enum name and it gets a bit complicated when we want to map these values to a human-readable form.
Let's take as example simplified Gender enumeration:
enum class Gender(val esValue: String) {
MALE("m"),
FEMALE("f"),
OTHER("o");
}
We need to write 2 converters, one for serialization and one for deserialization:
@ReadingConverter
class StringToGenderConverter : Converter<String, Gender> {
private val mapping: Map<String, Gender> = Gender.values().associateBy { it.esValue }
override fun convert(source: String): Gender {
return mapping.getOrDefault(source, OTHER)
}
}
@WritingConverter
class GenderToStringConverter : Converter<Gender, String> {
override fun convert(source: Gender): String {
return source.esValue
}
}
And then we need to add them to configuration:
@Configuration
class ElasticsearchConfig(private val conversionService: GenericConversionService) : AbstractReactiveElasticsearchConfiguration() {
@Bean
override fun entityMapper(): EntityMapper {
val entityMapper = ElasticsearchEntityMapper(elasticsearchMappingContext(), conversionService)
val converters = listOf(
GenderToStringConverter(),
StringToGenderConverter()
)
entityMapper.setConversions(ElasticsearchCustomConversions(converters))
return entityMapper
}
}
GenderToStringConverter
can be made generic by having Gender implement an interface:
enum class Gender(private val esValue: String) : ElasticsearchValue {
MALE("m"),
FEMALE("f"),
OTHER("o");
override fun esValue(): String = esValue
}
interface ElasticsearchValue {
fun esValue() : String
}
and converter:
@WritingConverter
class ElasticsearchValueToStringConverter : Converter<ElasticsearchValue, String> {
override fun convert(source: ElasticsearchValue): String {
return source.esValue()
}
}