U
UTMStack
Elasticsearch Service

The ElasticsearchService is a core backend component in UTMStack responsible for managing interactions with the underlying search and analytics engine. Despite its legacy naming, this service acts as a primary facade for the OpenSearch client, executing complex queries, aggregating data, and orchestrating search-related events across the platform.

OpenSearch Connector

This service heavily relies on the internal opensearch_connector library to handle low-level cluster communication, query DSL building, and response parsing.

Architecture

The service sits at the intersection of data retrieval and system orchestration. It not only queries the database but also interacts with notification, compliance, and auditing subsystems based on the data retrieved.

OpenSearch Transition
While the class is named ElasticsearchService for backward compatibility within the codebase, the underlying implementation utilizes the OpensearchClientBuilder and org.opensearch.client packages. All queries are executed against an OpenSearch cluster.

Service Dependencies

The service is instantiated via Spring's dependency injection (@Service) and requires several supporting services to function correctly.

Initialization

The service is initialized automatically by the Spring IoC container.

Spring Boot identifies the @Service annotation on ElasticsearchService during application startup.

The IoC container resolves the required beans (OpensearchClientBuilder, MailService, etc.) from the application context.

The dependencies are injected via the constructor, ensuring the service is immutable and thread-safe for concurrent query execution.

Implementation Details

Retrieving Field Values

One of the core utilities provided by the service is the ability to extract unique values for a specific keyword field across an index pattern. This is heavily used for building dynamic UI filters and dropdowns.

The getFieldValues method executes an aggregation query to retrieve these values, automatically sorting them by document count in descending order.

/**
 * Gets all values from an index keyword field
 *
 * @param keyword:      Keyword field name
 * @param indexPattern: Index pattern (e.g., "utmstack-events-*")
 * @return List of field values
 */
public List<String> getFieldValues(String keyword, String indexPattern) {
    final String ctx = CLASSNAME + ".getFieldValues";
    try {
        // Fetches up to 10,000 unique terms, sorted by frequency (Count/Desc)
        return new ArrayList<>(client.getClient().getFieldValues(
                keyword, 
                indexPattern,
                null, 
                10000, 
                TermOrder.Count, 
                SortOrder.Desc
        ).keySet());
    } catch (Exception e) {
        // Wraps underlying OpenSearch exceptions for generic handling
        throw new RuntimeException(ctx + ": " + e.getLocalizedMessage());
    }
}

Performance Consideration
The getFieldValues method has a hardcoded limit of 10000 terms. Running this on high-cardinality fields (like unique IDs or IP addresses) across massive index patterns may cause high memory consumption on both the OpenSearch cluster and the JVM.

Error Handling

The service interfaces with several custom exceptions to provide granular error reporting when interacting with the search cluster.

  • OpenSearchIndexNotFoundException: Thrown when a query attempts to target an index pattern that does not exist in the cluster.

  • UtmElasticsearchException: A generic wrapper for internal UTMStack search logic failures.

  • OpenSearchException: Propagated from the underlying opensearch_connector when the cluster returns an error response (e.g., parsing errors, timeout).

  • RuntimeException: Used as a fallback to wrap checked exceptions during data aggregation, ensuring the Spring transaction is handled appropriately.

UTMStack
UTMStack © 2026 All rights reserved