A library to migrate Elasticsearch and OpenSearch mappings. Inspired by Flyway.
Elasticsearch-Evolution executes versioned migration scripts reliably and persists the execution state in an internal Elasticsearch/OpenSearch index. Successfully executed migration scripts will not be executed again!
- tested on Java 17, 21 and 25
- runs on Spring-Boot 3.x and 4.x (and of course without Spring-Boot)
- runs on Elasticsearch version 8.x - 9.x
- but focussing on maintained versions, see elastic.co/support/eol or endoflife.date/elasticsearch
- runs on OpenSearch version 2.x - 3.x
- but focussing on maintained versions, see opensearch.org/releases or endoflife.date/opensearch
- highly configurable (e.g. location(s) of your migration files, migration file format patterns)
- placeholder substitution in migration scripts
- easily extendable to your needs
- supports microservices / multiple parallel running instances via logical database locks
- ready to use default configuration
- line comments in migration files
| Compatibility | Spring Boot | Elasticsearch | OpenSearch |
|---|---|---|---|
| elasticsearch-evolution >= 0.9.0 | 3.3 - 4.0 | 8.x - 9.x | 2.x - 3.x |
| elasticsearch-evolution >= 0.8.0 | 3.2 - 4.0 | 7.5.x - 9.x | 2.x - 3.x |
| elasticsearch-evolution >= 0.7.0 | 3.2 - 3.5 | 7.5.x - 9.x | 2.x - 3.x |
| elasticsearch-evolution >= 0.6.1 | 3.0 - 3.2 | 7.5.x - 8.19.x | 1.x - 2.x |
| elasticsearch-evolution >= 0.4.2 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2 | 7.5.x - 8.13.x | 1.x - 2.x |
| elasticsearch-evolution >= 0.4.0 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7 | 7.5.x - 8.6.x | 1.x - 2.x |
| elasticsearch-evolution 0.3.x | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7 | 7.5.x - 7.17.x | |
| elasticsearch-evolution 0.2.x | 1.5, 2.0, 2.1, 2.2, 2.3, 2.4 | 7.0.x - 7.4.x, 6.8.x |
First, add the latest version of the Elasticsearch-Evolution Spring Boot starter as a dependency to your Maven pom.xml
and choose an elasticsearch-evolution-rest-abstraction implementation (here: Elasticsearch RestClient implementation):
<dependency>
<groupId>com.senacor.elasticsearch.evolution</groupId>
<artifactId>spring-boot-starter-elasticsearch-evolution</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.senacor.elasticsearch.evolution</groupId>
<artifactId>elasticsearch-evolution-rest-abstraction-es-client</artifactId>
<version>0.9.0</version>
</dependency>Place your migration scripts in your application classpath under es/migration.
That's it. Elasticsearch-Evolution runs at application startup and expects your Elasticsearch/OpenSearch instance at http://localhost:9200.
First, add the latest version of Elasticsearch-Evolution core as a dependency
and choose an elasticsearch-evolution-rest-abstraction implementation (here: Elasticsearch RestClient implementation):
<dependency>
<groupId>com.senacor.elasticsearch.evolution</groupId>
<artifactId>elasticsearch-evolution-core</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>com.senacor.elasticsearch.evolution</groupId>
<artifactId>elasticsearch-evolution-rest-abstraction-es-client</artifactId>
<version>0.9.0</version>
</dependency>Place your migration scripts in your application classpath under es/migration.
Create an ElasticsearchEvolution instance and execute the migration:
// First, create an Elasticsearch RestClient
RestClient restClient = RestClient.builder(HttpHost.create("http://localhost:9200")).build();
// Then, create an EvolutionRestClient abstraction for Elasticsearch
EvolutionRestClient evolutionRestClient = new EvolutionESRestClient(restClient);
// Then, create an ElasticsearchEvolution configuration and instance
ElasticsearchEvolution elasticsearchEvolution = ElasticsearchEvolution.configure()
.load(evolutionRestClient);
// Execute the migration
elasticsearchEvolution.migrate();If you just want to validate your migration scripts without executing them, you can use the validate() method:
ElasticsearchEvolution elasticsearchEvolution = ...;
// Just validate the migrations
elasticsearchEvolution.validate();This will validate applied migrations against resolved ones (on the filesystem or classpath) to detect accidental changes that may prevent the schema(s) from being recreated exactly.
Validation fails if:
- a previously applied migration has been modified after it was applied (if enabled in configuration, see
validateOnMigrate) - versions have been resolved that haven't been applied yet
When validation fails, a ValidateException is thrown.
Elasticsearch-Evolution uses a REST client abstraction (EvolutionRestClient). Currently, these implementations exist:
- Elasticsearch RestClient implementation:
EvolutionESRestClient<dependency> <groupId>com.senacor.elasticsearch.evolution</groupId> <artifactId>elasticsearch-evolution-rest-abstraction-es-client</artifactId> <version>0.9.0</version> </dependency>
- Elasticsearch Rest5Client implementation:
EvolutionESRest5Clientwhich is designed for Elasticsearch9.xand Spring Boot4.x.<dependency> <groupId>com.senacor.elasticsearch.evolution</groupId> <artifactId>elasticsearch-evolution-rest-abstraction-es-rest5client</artifactId> <version>0.9.0</version> </dependency>
- OpenSearch RestClient implementation:
EvolutionOpenSearchRestClientwhich is designed for OpenSearch2.xbecause theRestClientTransportis deprecated for removal since3.x.<dependency> <groupId>com.senacor.elasticsearch.evolution</groupId> <artifactId>elasticsearch-evolution-rest-abstraction-os-restclient</artifactId> <version>0.9.0</version> </dependency>
- OpenSearch OpenSearchGenericClient implementation:
EvolutionOpenSearchGenericClientwhich is designed for OpenSearch3.xand later.<dependency> <groupId>com.senacor.elasticsearch.evolution</groupId> <artifactId>elasticsearch-evolution-rest-abstraction-os-genericclient</artifactId> <version>0.9.0</version> </dependency>
You can provide your own implementation of EvolutionRestClient if you want to use another HTTP client like Apache HTTPClient or OkHttpClient. This interface is located in:
<dependency>
<groupId>com.senacor.elasticsearch.evolution</groupId>
<artifactId>elasticsearch-evolution-rest-abstraction</artifactId>
<version>0.9.0</version>
</dependency>- With the Spring Boot starter, you have to create a bean with your custom
EvolutionRestClientimplementation like this:@Bean public EvolutionRestClient customEvolutionRestClient() { return new MyOkHttpClientEvolutionRestClient(...); }
An Elasticsearch-Evolution migration script represents a REST call. Here is an example:
PUT /_template/my_template
Content-Type: application/json
{
"index_patterns": [
"my_index_*"
],
"order": 1,
"version": 1,
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
"version": {
"type": "keyword",
"ignore_above": 20,
"similarity": "boolean"
},
"locked": {
"type": "boolean"
}
}
}
}The first line defines the HTTP method PUT and the relative path to the Elasticsearch/OpenSearch endpoint /_template/my_template to create a new mapping template.
This is followed by an HTTP header Content-Type: application/json.
After a blank line, the HTTP body is defined.
The pattern is strongly oriented towards ordinary HTTP requests and consists of 4 parts:
- The HTTP method (required). Supported HTTP methods are
GET,HEAD,POST,PUT,DELETE,OPTIONSandPATCH. The first non-comment line must always start with an HTTP method. - The path to the Elasticsearch/OpenSearch endpoint to call (required). The path is separated from the HTTP method by a blank space.
You can provide any query parameters like in an ordinary browser, such as
/my_index_1/_doc/1?refresh=true&op_type=create. - HTTP header(s) (optional). All non-comment lines after the HTTP method line will be interpreted as HTTP headers. Header name and content are separated by
:. - HTTP body (optional). The HTTP body is separated by a blank line and can contain any content you want to send to Elasticsearch/OpenSearch.
Elasticsearch-Evolution supports line comments in its migration scripts. Every line starting with # or // will be interpreted as a comment line.
Comment lines are not sent to Elasticsearch/OpenSearch; they will be filtered by Elasticsearch-Evolution.
Elasticsearch-Evolution supports named placeholder substitution. Placeholders are marked in your migration script like this: ${my-placeholder}
- Starts with
placeholderPrefix, which is by default${and is configurable. - Followed by the
placeholder name, which can be any string but must not containplaceholderPrefixorplaceholderSuffix. - Ends with
placeholderSuffix, which is by default}and is configurable.
Here is an example filename: V1.0__my-description.http
The filename must follow a pattern:
- Starts with
esMigrationPrefix, which is by defaultVand is configurable. - Followed by a version, which must be numeric and can be structured by separating the version parts with
.. - Followed by the
versionDescriptionSeparator:__. - Followed by a description, which can be any text your filesystem supports.
- Ends with
esMigrationSuffixes, which is by default.httpand is configurable and case-insensitive.
Elasticsearch-Evolution uses the version for ordering your scripts and enforces strictly ordered execution of your scripts by default. Out-of-order execution is supported but disabled by default. Elasticsearch-Evolution interprets the version parts as integers, so each version part must be between 1 (inclusive) and 2,147,483,647 (inclusive).
Here is an example that indicates the ordering: 1.0.1 < 1.1 < 1.2.1 < (2.0.0 == 2).
In this example, version 1.0.1 is the smallest version and is executed first, followed by versions 1.1, 1.2.1, and finally 2.
2 is the same as 2.0 or 2.0.0 - trailing zeros will be trimmed.
NOTE: Versions with major version 0 are reserved for internal usage, so the smallest version you can define is 1.
Elasticsearch-Evolution can be configured to your needs:
- enabled (default=
true): Whether to enable or disable Elasticsearch-Evolution. - locations (default=
[classpath:es/migration]): List of locations of migration scripts. Supported areclasspath:some/pathandfile:/some/path. The location is scanned recursively, but only to a depth of 10. NOTE: All scripts in all locations/subdirectories will be flattened, and only the version number will be used to order them. - encoding (default=
UTF-8): Encoding of migration files. - defaultContentType (default=
application/json; charset=UTF-8): This content type will be used as the default if noContent-Typeheader is specified in the header section of a migration script. If no charset is defined, theencodingcharset is used. - esMigrationPrefix (default=
V): File name prefix for migration files. - esMigrationSuffixes (default=
[.http]): List of file name suffixes for migration files. The suffix is checked case-insensitively. - placeholderReplacement (default=
true): Whether to enable or disable placeholder replacement in migration scripts. - placeholders (default=
[]): Map of placeholders and their replacements to apply to migration scripts. - placeholderPrefix (default=
${): Prefix of placeholders in migration scripts. - placeholderSuffix (default=
}): Suffix of placeholders in migration scripts. - historyIndex (default=
es_evolution): Name of the history index that will be used by Elasticsearch-Evolution. In this index, Elasticsearch-Evolution will persist its internal state and track which migration scripts have already been executed. - historyMaxQuerySize (default=
1000): The maximum query size while validating already executed scripts. This query size must be higher than the total count of your migration scripts. - validateOnMigrate (default=
true): Whether to fail when a previously applied migration script has been modified after it was applied. - baselineVersion (default=
1.0): Version to use as a baseline. Versions lower than this will not be applied. - lineSeparator (default=
\n): Line separator, used only temporarily between reading raw migration file line-by-line and parsing it later. Only needed for backward compatibility/checksum stability! Should be one of\n,\ror\r\n. - outOfOrder (default=
false): Allows migrations to be run "out of order". If you already have versions 1.0 and 3.0 applied, and now version 2.0 is found, it will be applied too instead of being rejected. - trimTrailingNewlineInMigrations (default=
false): Whether to remove a trailing newline in migration scripts. Only needed for backward compatibility/checksum stability!
You can set the above configurations via Spring Boot's default configuration mechanism. Just use the prefix spring.elasticsearch.evolution. Here is an example application.properties:
spring.elasticsearch.evolution.locations[0]=classpath:es/migration
spring.elasticsearch.evolution.locations[1]=classpath:es/more_migration_scripts
spring.elasticsearch.evolution.placeholderReplacement=true
spring.elasticsearch.evolution.placeholders.indexname=myIndexReplacement
spring.elasticsearch.evolution.placeholders.docType=_doc
spring.elasticsearch.evolution.placeholders.foo=bar
spring.elasticsearch.evolution.historyIndex=es_evolutionSince Spring Boot 2.1, AutoConfiguration for Elasticsearch's REST client is provided (see org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration or org.springframework.boot.elasticsearch.autoconfigure.ElasticsearchRestClientAutoConfiguration for Spring Boot 4).
You can configure the client, required for Elasticsearch-Evolution, just like this in your application.properties:
spring.elasticsearch.uris[0]=https://example.com:9200
spring.elasticsearch.username=my-user-name
spring.elasticsearch.password=my-secret-pwElasticsearch-Evolution tries to be compatible with spring-data-opensearch-starter and its AutoConfiguration.
AutoConfiguration for OpenSearch's REST client is provided (see org.opensearch.spring.boot.autoconfigure.OpenSearchRestClientAutoConfiguration).
AutoConfiguration for OpenSearch's java client is provided (see org.opensearch.spring.boot.autoconfigure.OpenSearchClientAutoConfiguration).
You can configure the client, required for Elasticsearch-Evolution, just like this in your application.properties:
opensearch.uris[0]=https://example.com:9200
opensearch.username=my-user-name
opensearch.password=my-secret-pwElasticsearch-Evolution just needs an EvolutionRestClient as a Spring bean. The Elasticsearch EvolutionESRestClient implementation needs a RestClient Spring bean.
If you don't have Spring Boot 2.1 or later, or you need a special RestClient configuration (e.g., to accept self-signed certificates or disable hostname validation), you can provide a custom RestClient like this:
@Bean
public RestClient myRestClient() {
RestClientBuilder builder = RestClient.builder(HttpHost.create("https://localhost:9200"))
.setHttpClientConfigCallback(httpClientBuilder -> {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("my-user-name", "my-secret-pw"));
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
try {
httpClientBuilder
.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
} catch (GeneralSecurityException e) {
throw new IllegalStateException("could not configure http client to accept all certificates", e);
}
return httpClientBuilder;
}
);
return builder.build();
}Elasticsearch-Evolution just needs an EvolutionRestClient as a Spring bean. The OpenSearch EvolutionOpenSearchRestClient implementation needs a RestClient Spring bean.
If you don't use already the spring-data-opensearch-starter or you need a special RestClient configuration (e.g., to accept self-signed certificates or disable hostname validation), you can provide a custom RestClient similar to the Elasticsearch example above.
Elasticsearch-Evolution just needs an EvolutionRestClient as a Spring bean. The OpenSearch EvolutionOpenSearchGenericClient implementation needs a OpenSearchGenericClient or OpenSearchClient Spring bean.
If you don't use already the spring-data-opensearch-starter or you need a special OpenSearch*Client configuration (e.g., to accept self-signed certificates or disable hostname validation), you can provide a custom OpenSearchGenericClient or OpenSearchClient as Spring bean like this:
@Bean
public OpenSearchClient myOpenSearchClient() throws URISyntaxException {
final HttpHost httpHost = HttpHost.create("https://localhost:9200");
OpenSearchTransport openSearchTransport = ApacheHttpClient5TransportBuilder.builder(httpHost)
// I want to enable compression for better performance
.setCompressionEnabled(true)
.build();
return new OpenSearchClient(openSearchTransport);
}Providing a OpenSearchTransport Spring bean is also possible, the ElasticsearchEvolutionAutoConfiguration will create the EvolutionOpenSearchGenericClient from it:
@Bean
public OpenSearchTransport myOpenSearchTransport() throws URISyntaxException {
final HttpHost httpHost = HttpHost.create("https://localhost:9200");
return ApacheHttpClient5TransportBuilder.builder(httpHost)
// I want to enable compression for better performance
.setCompressionEnabled(true)
.build();
}If you want to provide a customized initializer for Elasticsearch-Evolution (e.g., with a different order):
@Bean
public ElasticsearchEvolutionInitializer customElasticsearchEvolutionInitializer(ElasticsearchEvolution elasticsearchEvolution) {
return new ElasticsearchEvolutionInitializer(elasticsearchEvolution) {
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
};
}You can set the above configurations via the ElasticsearchEvolutionConfig fluent builder like this:
ElasticsearchEvolution.configure()
.setLocations(Collections.singletonList("classpath:es/migration"))
.setPlaceholderReplacement(true)
.setPlaceholders(Collections.singletonMap("indexname", "myIndexReplacement"))
.setHistoryIndex("es_evolution");- Drop Elasticsearch 7.x and OpenSearch 1.x support (#570).
- Focusing on maintained versions.
- Drop deprecated RestHighLevelClient in tests (#549)
- Drop Spring Boot 3.2 compatibility tests. Further versions may run on Spring Boot 3.2, but they are not tested anymore.
- Improve release process (#562).
- Trigger a release from within the GitHub Actions Website (via workflow_dispatch Event).
- Automatically set the release version and the next development version after the release finished.
- fixed
EvolutionOpenSearchRestClient(elasticsearch-evolution-rest-abstraction-os-restclient) compatibility with OpenSearch 2.x client libs (#565), - added Spring Boot 4 compatibility (#564).
- Added Elasticsearch
EvolutionRestClientimplementation:EvolutionESRest5Client. It uses the Apache HttpClient 5 basedRest5Clientfromco.elastic.clients:elasticsearch-rest5-client.
- Added Elasticsearch
- Added 2 OpenSearch
EvolutionRestClientimplementations (#198, #220, #287, #348).EvolutionOpenSearchRestClientfor OpenSearchRestClient.EvolutionOpenSearchGenericClientforOpenSearchGenericClientandOpenSearchClient.
- Release process: Replace archived
actions/create-releasewithsoftprops/action-gh-release(#554). - Release process: Switch to new maven central publishing API / central-publishing-maven-plugin (#538).
- Added unique version number validation (#551).
- When two migration scripts have the same version number, the migration fails with a
MigrationException.
- When two migration scripts have the same version number, the migration fails with a
- Breaking change: Added abstraction for the underlying HTTP Client (
EvolutionRestClientin new artifactcom.senacor.elasticsearch.evolution:elasticsearch-evolution-rest-abstraction) and an implementation for the ElasticsearchRestClient(EvolutionESRestClientin artifactcom.senacor.elasticsearch.evolution:elasticsearch-evolution-rest-abstraction-es-client) (#553).- You have to add the dependency
com.senacor.elasticsearch.evolution:elasticsearch-evolution-rest-abstraction-es-client.
- You have to add the dependency
- Drop Spring Boot 3.0 and 3.1 compatibility tests. Further versions may run on Spring Boot 3.0 and 3.1, but they are not tested anymore.
- Added regression tests for Spring Boot 3.4 and 3.5.
- Added regression tests against Elasticsearch 9.0 - 9.2.
- Added regression tests against OpenSearch 3.3 and 3.4.
- Added option to just validate without executing migrations (#435).
- Added regression tests against OpenSearch 2.19.
- Bump Spring Boot version to 3.5.
- Added regression tests for Spring Boot 3.3.
- Bugfix (#536): Don't do HTTP GET request with a body.
- Added regression tests on JDK 25.
- Added regression tests against Elasticsearch 8.14 - 8.19.
- Added option to trim a trailing newline in migration scripts (fixes #298). NOTE: This option is only needed for backward compatibility/checksum stability!
- The minimum supported Java version is now 17.
- Drop Spring Boot 2 compatibility. Further versions may run on Spring Boot 2, but they are not tested anymore.
- Bugfix (#293): Trailing newlines will no longer be removed from migration scripts.
- Added regression tests against OpenSearch 2.13.
- Added regression tests against Elasticsearch 8.13.
- Version updates (Spring Boot 2.7.18).
- Added regression tests for Spring Boot 3.2.
- Remove deprecated query parameter
ignore_throttledfrom ES requests.
- Added Spring Boot configuration metadata #240.
- Replaces unmaintained org.reflections library with classgraph to scan the classpath for migration files. Fixes #239.
- Support out-of-order migration execution.
- Version updates (Spring Boot 2.7.17).
- Added regression tests against OpenSearch 2.11, 2.10 and 2.9.
- Added regression tests against Elasticsearch 8.11, 8.10 and 8.9.
- Drop older Elasticsearch and OpenSearch versions in regression tests. Only test against the last 3 minor versions of the latest major release.
- Added regression tests on JDK 21.
- Added regression tests for Spring Boot 3.1.
- Update org.reflections:reflections from 0.9.12 to 0.10.2 #233 thanks @RiVogel.
- KNOWN ISSUES:
- #239: Migration files not found in Spring Boot jar.
- Workaround 1: Downgrade
org.reflections:reflectionsto0.10.1. - Workaround 2: Downgrade
elasticsearch-evolutionto0.4.2.
- Workaround 1: Downgrade
- #239: Migration files not found in Spring Boot jar.
- Bugfix (#182): Checksum calculation was based on system-dependent line separators which led to different checksums on different operating systems (e.g., Windows vs. Linux). The default is now
\n. For backward compatibility, you can set a different line separator via thelineSeparatorconfig property. - Version updates (Spring Boot 2.7.8).
- Spring Boot 3 compatibility + tests.
- Added OpenSearch 2.5 compatibility tests.
- Optimization: Don't acquire lock if no scripts need to be executed (#172).
- Previously applied migration scripts are now checked for modifications and rejected if they've been modified after they were applied. The old behaviour can be restored by setting the new configuration parameter
validateOnMigrateto false (default: true) (#155). - Version updates (Spring Boot 2.7.7).
- Added Java 19 compatibility tests.
- Added Spring Boot 2.7 compatibility tests.
- Added Elasticsearch 8.6, 8.5, 8.4, 8.3, and 8.2 compatibility tests.
- Added OpenSearch 2.4, 2.3, 2.2, 2.1 and 2.0 compatibility tests.
- It is now possible to set a
baselineVersionto skip migrations with versions lower than the definedbaselineVersion(#164).
- Breaking change: Drop
org.elasticsearch.client.RestHighLevelClientand replace withorg.elasticsearch.client.RestClient(LowLevelClient). This will drop the big transitive dependencyorg.elasticsearch:elasticsearchand opens compatibility to Elasticsearch 8 and OpenSearch. - Version updates (Spring Boot 2.6.6).
- Added Spring Boot 2.5 and 2.6 compatibility tests.
- Added Java 17 and 18 compatibility tests.
- Added Elasticsearch 8.1, 8.0, 7.17, 7.16, 7.15, 7.14 and 7.13 compatibility tests.
- Added OpenSearch 1.0, 1.1, 1.2 and 1.3 compatibility tests.
- Fixed issue #114.
- Fixed issue #36.
- Version updates (Spring Boot 2.4.5).
- Added Java 16 compatibility tests.
- Added Elasticsearch 7.12 compatibility tests.
- Version upgrade elasticsearch-rest-high-level-client to 7.5.2 (ES versions < 7.5.0 are no longer supported).
- Remove Spring Boot 1.5 and 2.0 support.
- Version updates (Spring Boot 2.4.0).
- Added Spring Boot 2.4 compatibility tests.
- Version updates (Spring Boot 2.3.5.RELEASE).
- Version updates (Spring Boot 2.3.0.RELEASE, Elasticsearch 6.8.6, Jackson 2.10.3, SLF4J 1.7.30, Reflections 0.9.12).
- Added Spring Boot 2.2 compatibility tests.
- Added Spring Boot 2.3 compatibility tests.
- New configuration parameter
historyMaxQuerySize. - Version updates (Spring Boot 2.1.9.RELEASE; Elasticsearch 6.8.3).
- ElasticsearchEvolutionInitializer now logs the migration duration.
- Version updates (Spring Boot 2.1.3.RELEASE; Elasticsearch 6.7.0).
- Bugfix: Support more than 10 migration scripts: now 1000.
- Improved logging.
- Fixed classpath scanning in fat-jars like Spring Boot.
- Initial version.
We welcome contributions to Elasticsearch-Evolution! Here's how you can help: CONTRIBUTING.md
