How to provide a dynamic index name in Spring Data Elasticsearch using SpEL
In Spring Data Elasticsearch – at the time of writing, version 4.0 is the current version – the name of an index is normally defined by the @Document
annotation on the entity class. For the following examples let’s assume we want to write some log entries to Elasticsearch with our application. We use the following entity:
@Document(indexName = "log")
public class LogEntity {
@Id
private String id = UUID.randomUUID().toString();
@Field(type = FieldType.Text)
private String text;
@Field(name = "log-time", type = FieldType.Date, format = DateFormat.basic_date_time)
private ZonedDateTime logTime = ZonedDateTime.now();
public String getId() {
return id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public ZonedDateTime getLogTime() {
return logTime;
}
public void setLogTime(ZonedDateTime logTime) {
this.logTime = logTime;
}
}
Here the index name is the fixed name log.
it is possible to use a dynamically defined name for an index by using Spring Expression Language SpEL. Important: We need to use a SpEL template expression, that is an expression enclosed in #{}
. This allows for the following setups:
Use a value from the application configuration
Let’s assume we have the following entry in the application.properties file:
index.prefix=test
We then use this code
@Document(indexName = "#{@environment.getProperty('index.prefix')}-log")
and the index name to use changes to test-log.
Use a value provided by a static method of some class
The second example shows how to call a static function to get a dynamic value. We use the following definition to add the current date to the index name:
@Document(indexName = "log-#{T(java.time.LocalDate).now().toString()}")
Currently this would provide an index name of log-2020-07-28.
Use a value provided by a Spring bean
For the third case we provide a bean that will give us a dynamically created string to be used as part of the index name.
>@Component
public class LogIndexNameProvider {
public String timeSuffix() {
return LocalTime.now().truncatedTo(ChronoUnit.MINUTES).toString().replace(':', '-');
}
}
This bean, named logIndexNameProvider, can return a String that contains the current time as hh-mm (I would not use this for naming indices, but this is just an example).
Changing the definition to
@Document(indexName = "log-#{@logIndexNameProvider.timeSuffix()}")
will now create index names like log-08-25 or log-22-07.
Of course we can mix all of these together: add a prefix from the configuration, append the current date.
Important notice:
The evaluation of SpEL for index names is only done for the index names defined in the @Document
annotation. It is not done for index names that are passed as a IndexCoordinates
parameter in the different methods of the ElasticsearchOperations
or IndexOperations
interfaces. If it were allowed on these, it would be easy to set up a scenario, where an expression is read from some outside source. And then someone might send something like "log-#{T(java.lang.Runtime).getRuntime().exec(new String[]{'/bin/rm', '/tmp/somefile'})}"
which will not provide an index name, but delete files on your computer.