package micronaut.example.service;
import java.util.Iterator;
import jakarta.inject.Singleton;
import micronaut.example.configuration.AppConfiguration;
import micronaut.example.exception.MovieServiceException;
import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.client.opensearch.core.IndexRequest;
import org.opensearch.client.opensearch.core.IndexResponse;
import org.opensearch.client.opensearch.core.SearchResponse;
import org.opensearch.client.opensearch.core.search.Hit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
class MovieServiceImpl implements MovieService {
private static final Logger LOG = LoggerFactory.getLogger(MovieServiceImpl.class);
private final AppConfiguration appConfiguration;
private final OpenSearchClient client;
public MovieServiceImpl(AppConfiguration appConfiguration, OpenSearchClient client) {
this.appConfiguration = appConfiguration;
this.client = client;
}
@Override
public String saveMovie(Movie movie) {
try {
IndexRequest<Movie> indexRequest = createIndexRequest(movie);
IndexResponse indexResponse = client.index(indexRequest);
String id = indexResponse.id();
LOG.info("Document for '{}' {} successfully in ES. The id is: {}", movie, indexResponse.result(), id);
return id;
} catch (Exception e) {
String errorMessage = String.format("An exception occurred while indexing '%s'", movie);
LOG.error(errorMessage);
throw new MovieServiceException(errorMessage, e);
}
}
private IndexRequest<Movie> createIndexRequest(Movie movie) {
return new IndexRequest.Builder<Movie>()
.index(appConfiguration.getMoviesIndexName())
.document(movie)
.build();
}
@Override
public Movie searchMovies(String title) {
try {
SearchResponse<Movie> searchResponse = client.search(s ->
s.index(appConfiguration.getMoviesIndexName()).query(q ->
q.match(m ->
m.field("title").query(fq -> fq.stringValue(title)))), Movie.class);
LOG.info("Searching for '{}' took {} and found {}",
title,
searchResponse.took(),
searchResponse.hits().total().value());
Iterator<Hit<Movie>> hits = searchResponse.hits().hits().iterator();
if (hits.hasNext()) {
return hits.next().source();
}
return null;
} catch (Exception e) {
String errorMessage = String.format("An exception occurred while searching for title '%s'", title);
LOG.error(errorMessage);
throw new MovieServiceException(errorMessage, e);
}
}
}
Micronaut OpenSearch
Micronaut integration with OpenSearch
Version: 1.2.1-SNAPSHOT
1 Introduction
Micronaut OpenSearch simplifies integration with OpenSearch.
OpenSearch is the flexible, scalable, open-source way to build solutions for data-intensive applications.
OpenSearchClient
Once you have integrated Micronaut OpenSearch, you are able to inject a bean of
type org.opensearch.client.opensearch.OpenSearchClient
or org.opensearch.client.opensearch.OpenSearchAsyncClient
.
package micronaut.example.service
import micronaut.example.configuration.AppConfiguration
import micronaut.example.exception.MovieServiceException
import jakarta.inject.Singleton
import org.opensearch.client.opensearch.OpenSearchClient
import org.opensearch.client.opensearch.core.IndexRequest
import org.opensearch.client.opensearch.core.IndexResponse
import org.opensearch.client.opensearch.core.SearchResponse
import org.opensearch.client.opensearch.core.search.Hit
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@Singleton
class MovieServiceImpl implements MovieService {
private static final Logger LOG = LoggerFactory.getLogger(MovieServiceImpl.class)
private final AppConfiguration appConfiguration
private final OpenSearchClient client
MovieServiceImpl(AppConfiguration appConfiguration, OpenSearchClient client) {
this.appConfiguration = appConfiguration
this.client = client
}
@Override
String saveMovie(Movie movie) {
try {
IndexRequest<Movie> indexRequest = createIndexRequest(movie)
IndexResponse indexResponse = client.index(indexRequest)
String id = indexResponse.id()
LOG.info("Document for '{}' {} successfully in ES. The id is: {}", movie, indexResponse.result(), id)
return id
} catch (Exception e) {
String errorMessage = String.format("An exception occurred while indexing '%s'", movie)
LOG.error(errorMessage)
throw new MovieServiceException(errorMessage, e)
}
}
private IndexRequest<Movie> createIndexRequest(Movie movie) {
return new IndexRequest.Builder<Movie>()
.index(appConfiguration.getMoviesIndexName())
.document(movie)
.build()
}
@Override
Movie searchMovies(String title) {
try {
SearchResponse<Movie> searchResponse = client.search((s) ->
s.index(appConfiguration.getMoviesIndexName())
.query(q -> q.match(m ->
m.field("title")
.query(fq -> fq.stringValue(title))
)), Movie.class
)
LOG.info("Searching for '{}' took {} and found {}", title, searchResponse.took(), searchResponse.hits().total().value())
Iterator<Hit<Movie>> hits = searchResponse.hits().hits().iterator()
if (hits.hasNext()) {
return hits.next().source()
}
return null
} catch (Exception e) {
String errorMessage = String.format("An exception occurred while searching for title '%s'", title)
LOG.error(errorMessage)
throw new MovieServiceException(errorMessage, e)
}
}
}
package micronaut.example.service
import jakarta.inject.Singleton
import micronaut.example.configuration.AppConfiguration
import micronaut.example.exception.MovieServiceException
import org.opensearch.client.opensearch.OpenSearchClient
import org.opensearch.client.opensearch.core.IndexRequest
import org.opensearch.client.opensearch.core.IndexResponse
import org.opensearch.client.opensearch.core.SearchResponse
import org.opensearch.client.opensearch.core.search.Hit
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@Singleton
class MovieServiceImpl(
private val appConfiguration: AppConfiguration,
private val client: OpenSearchClient
) : MovieService {
override fun saveMovie(movie: Movie): String {
try {
val indexRequest: IndexRequest<Movie> = createIndexRequest(movie)
val indexResponse: IndexResponse = client.index(indexRequest)
val id: String = indexResponse.id()
LOG.info("Document for '{}' {} successfully in ES. The id is: {}", movie, indexResponse.result(), id)
return id
} catch (e: Exception) {
val errorMessage = String.format("An exception occurred while indexing '%s'", movie)
LOG.error(errorMessage)
throw MovieServiceException(errorMessage, e)
}
}
private fun createIndexRequest(movie: Movie): IndexRequest<Movie> {
return IndexRequest.Builder<Movie>()
.index(appConfiguration.getMoviesIndexName())
.document(movie)
.build()
}
override fun searchMovies(title: String): Movie? {
try {
val searchResponse: SearchResponse<Movie> = client.search({ s ->
s.index(appConfiguration.getMoviesIndexName()).query { q ->
q.match { m ->
m.field("title").query { fq -> fq.stringValue(title) }
}}}, Movie::class.java)
LOG.info(
"Searching for '{}' took {} and found {}",
title,
searchResponse.took(),
searchResponse.hits().total().value()
)
val hits: Iterator<Hit<Movie>> = searchResponse.hits().hits().iterator()
if (hits.hasNext()) {
return hits.next().source()
}
return null
} catch (e: Exception) {
val errorMessage = String.format("An exception occurred while searching for title '%s'", title)
LOG.error(errorMessage)
throw MovieServiceException(errorMessage, e)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger(MovieServiceImpl::class.java)
}
}
2 OpenSearch Amazon
To use Micronaut OpenSearch and connect to Amazon OpenSearch Service add the following dependency:
implementation("io.micronaut.opensearch:micronaut-opensearch-amazon")
<dependency>
<groupId>io.micronaut.opensearch</groupId>
<artifactId>micronaut-opensearch-amazon</artifactId>
</dependency>
You can configure the connection with:
Property | Type | Description |
---|---|---|
|
boolean |
Whether Amazon OpenSearch Service integration is enabled. Default value true |
|
java.lang.String |
Sets the OpenSearch endpoint, without https:// For example: |
|
software.amazon.awssdk.regions.Region |
The region to use for signing requests. For example: |
Moreover, you can create a BeanCreatedEventListener
for a bean of type org.opensearch.client.transport.aws.AwsSdk2TransportOptions.Builder
, to configure the connection according to your use case.
import io.micronaut.context.event.BeanCreatedEvent;
import io.micronaut.context.event.BeanCreatedEventListener;
import jakarta.inject.Singleton;
import org.opensearch.client.transport.aws.AwsSdk2TransportOptions;
@Singleton
class AwsSdk2TransportOptionsBeanCreatedEventListener implements BeanCreatedEventListener<AwsSdk2TransportOptions.Builder> {
@Override
public AwsSdk2TransportOptions.Builder onCreated(BeanCreatedEvent<AwsSdk2TransportOptions.Builder> event) {
AwsSdk2TransportOptions.Builder builder = event.getBean();
// Modify the builder here
return builder;
}
}
3 OpenSearch using Apache HttpClient 5 Transport
To use Micronaut OpenSearch and connect using Apache HttpClient 5 transport, add the following dependencies:
implementation("io.micronaut.opensearch:micronaut-opensearch-httpclient5")
<dependency>
<groupId>io.micronaut.opensearch</groupId>
<artifactId>micronaut-opensearch-httpclient5</artifactId>
</dependency>
You can configure the connection with:
Property | Type | Description |
---|---|---|
|
org.apache.hc.core5.http.HttpHost |
One or more hosts that client will connect to. |
|
boolean |
If OpenSearch integration is enabled. Default value true |
You can create a BeanCreatedEventListener for a bean of type org.opensearch.client.transport.httpclient5.ApacheHttpClient5TransportBuilder
to configure the connection according to your use case.
4 OpenSearch RestClient
To use Micronaut OpenSearch and connect with RestClient-based transport add the following dependencies:
implementation("io.micronaut.opensearch:micronaut-opensearch-restclient")
<dependency>
<groupId>io.micronaut.opensearch</groupId>
<artifactId>micronaut-opensearch-restclient</artifactId>
</dependency>
implementation("io.micronaut:micronaut-jackson-databind")
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-jackson-databind</artifactId>
</dependency>
You can configure the connection with:
Property | Type | Description |
---|---|---|
|
org.apache.http.HttpHost |
One or more hosts that client will connect to. |
|
org.opensearch.client.NodeSelector |
The {@link NodeSelector} to be used, in case of multiple nodes. |
|
org.apache.http.Header |
The defaults {@link Header} to sent with each request. |
|
boolean |
If OpenSearch integration is enabled. Default value true |
You can create BeanCreatedEventListeners for a beans of
type org.apache.http.client.config.RequestConfig.Builder
, org.apache.http.impl.nio.client.HttpAsyncClientBuilder
, and org.opensearch.client.RestClientBuilder
to configure the connection according to your use case.
5 Health
After you add the management dependency, the Health Endpoint exposes a health indicator for OpenSearch.
You can disable it with:
Property | Type | Description |
---|---|---|
|
boolean |
Whether OpenSearch Health Indicator is enabled. Default value true |
6 Release History
For this project, you can find a list of releases (with release notes) here:
7 Repository
You can find the source code of this project in this repository: