Elasticsearch Java Client

repository·main·Indexed 19 days ago

https://github.com/elastic/elasticsearch-java

The official Java client for Elasticsearch, providing a strongly typed API for real-time search, vector search, and data management. It features a fluent builder pattern with lambda support, a namespace-based client structure, and a port of the Low Level Rest Client to Apache HTTP Client 5. The client is designed for forward compatibility with Elasticsearch server versions.

Tokens
49.2K
Snippets
117
Records
174
Agent score
68%

What's inside elasticsearch-java

  1. Overview of the Elasticsearch Java API Client

    main

    The official Elasticsearch Java API Client provides strongly typed requests and responses for all Elasticsearch APIs. It is designed to handle complex nested structures using fluent builders and functional patterns.

    Key capabilities include:

    • Strong Typing: All APIs have corresponding typed request and response objects.
    • Execution Models: Supports both blocking and asynchronous versions of all APIs.
    • Object Mapping: Integrates seamlessly with application classes using object mappers like Jackson or any JSON-B implementation.
    • Transport Delegation: Delegates protocol-level concerns (HTTP connection pooling, retries, node discovery) to an underlying HTTP client, such as the Java Low Level REST Client.
  2. Overview of the RealWorld Example App

    main

    The RealWorld Example App is a full-stack demonstration codebase designed to show how to use the Elasticsearch Java API Client within a production-grade environment. It implements the RealWorld specification, providing a complete implementation of CRUD operations, authentication, routing, and pagination.

    Tech Stack

    • Language: Java (OpenJDK 21.0.2 recommended)
    • Framework: Spring Boot
    • Build Tool: Gradle
    • Database: Elasticsearch
    • Serialization: Jackson
    • Authentication: Java JWT & Jaxb
    • Testing: JUnit & Testcontainers (for spinning up Elasticsearch instances)
  3. Overview of the Elasticsearch Java Client

    main

    The Elasticsearch Java Client is the official client for interacting with Elasticsearch. It provides strongly typed requests and responses for all Elasticsearch APIs.

    Key design principles include:

    • Builder Pattern: Object construction is primarily based on the builder pattern.
    • Builder Lambdas: Nested objects can be constructed using builder lambdas, enabling a clean, DSL-like syntax for expressive code.
    • Nullability: Instead of using java.util.Optional, optional values are represented as null and marked with @Nullable annotations to align with common Java ecosystem patterns.
    • Protocol Delegation: The client delegates low-level protocol handling (HTTP connection establishment, pooling, retries, etc.) to an underlying HTTP client, such as the Elasticsearch Low Level REST client.
  4. Use the Legacy REST Client [java-rest-low]

    main

    The java-rest-low client is a low-level REST client for Elasticsearch. It is designed for minimal dependencies and provides core transport-level features.

    Key features include:

    • Load balancing: Distributes requests across all available nodes.
    • Failover: Handles node failures and specific response codes automatically.
    • Connection Penalization: Implements a backoff mechanism where the wait time before retrying a failed node increases based on the number of consecutive failures.
    • Persistent Connections: Maintains connections for efficiency.
    • Trace Logging: Allows for logging of raw requests and responses.
    • Node Discovery: Supports optional automatic discovery of cluster nodes via a sniffer.
  5. Understand experimental TransportHttpClient implementations

    main

    The example-transports directory contains experimental implementations of the TransportHttpClient interface. These implementations are provided solely for educational purposes, serving as examples and inspiration for developers looking to implement custom transport layers.

    Warning: These implementations are not considered production-ready and should not be used in production environments.

  6. Features of the REST 5 Client

    main

    The low-level REST 5 Client provides a high-performance, minimal-dependency way to interact with Elasticsearch. Key features include:

    • Minimal dependencies: Lightweight footprint.
    • Load balancing: Distributes requests across all available nodes.
    • Failover: Automatically handles node failures and specific response codes.
    • Failed connection penalization: Implements a backoff mechanism where the wait time before retrying a failed node increases based on the number of consecutive failures.
    • Persistent connections: Maintains connections for efficiency.
    • Trace logging: Supports logging of raw requests and responses.
    • Automatic node discovery: Optional support for discovering cluster nodes via a sniffer.
  7. Distinguish between API methods and framework methods

    main

    The Elasticsearch Java API Client distinguishes between methods that map directly to the Elasticsearch JSON API and methods that belong to the underlying client framework. Understanding this distinction helps you identify which methods are part of your data operations and which are part of the client's internal mechanics.

    • API Methods/Properties: These represent actual Elasticsearch API calls or response fields (e.g., ElasticsearchClient.search() or SearchResponse.maxScore()). They follow standard Java camelCaseNaming and are derived directly from the Elasticsearch JSON API names.
    • Framework Methods/Properties: These are part of the client framework itself (e.g., Query._kind()). They are prefixed with an underscore (_) to prevent naming collisions with the Elasticsearch API and to provide a clear visual distinction.
  8. Stream ingestion with BulkIngester

    main

    The BulkIngester utility automatically groups individual operations into bulk requests based on configured thresholds. This simplifies ingestion by allowing you to simply call .add() for each operation.

    Automatic flush triggers:

    • Maximum number of operations reached (default: 1000).
    • Maximum bulk request size in bytes reached (default: 5 MiB).
    • Periodic flush interval expired.

    Backpressure: You can define a maximum number of concurrent requests. If this limit is reached, calling .add() will block, preventing the client from overloading the Elasticsearch server.

    BulkIngester<Void> ingester = BulkIngester.of(b -> b
        .client(esClient)    // Set the Elasticsearch client
        .maxOperations(100)  // Max operations before flush
        .flushInterval(1, TimeUnit.SECONDS) // Periodic flush interval
    );
    
    for (File file: logFiles) {
        FileInputStream input = new FileInputStream(file);
        BinaryData data = BinaryData.of(IOUtils.toByteArray(input), ContentType.APPLICATION_JSON);
    
        ingester.add(op -> op // Add a bulk operation
            .index(idx -> idx
                .index("logs")
                .document(data)
            )
        );
    }
    
    ingester.close(); // Flush pending operations and release resources
  9. Understand the Elasticsearch server compatibility policy

    main

    The Elasticsearch Java client follows a forward compatibility policy. This means a client version can communicate with Elasticsearch server versions that are greater than or equal to its own minor version without breaking.

    Important distinctions:

    • Feature Support: Forward compatibility does not mean the client automatically supports new features introduced in newer server versions. To use new features of a newer Elasticsearch version, you must upgrade to the corresponding client version (e.g., an 8.12 client cannot use 8.13 features; you need the 8.13 client).
    • Version Synchronization: Client releases are synchronized with the Elasticsearch server for major and minor versions. Patches are released independently for faster bugfixes.
    • Patch Compatibility: A client version 8.13.x is compatible with server versions 8.13.y where y >= x.
  10. How optional values are represented in model classes

    main

    The Elasticsearch Java Client represents optional values (both objects and primitives) using nullable references rather than java.util.Optional wrappers.

    To ensure type safety and provide clear contracts for developers, the client uses @Nullable and @NotNull annotations on all getters and setters. This allows IDEs and static analysis tools to help prevent NullPointerException while maintaining high performance and compatibility with other languages like Kotlin and Scala.

    Key Characteristics

    • Objects: Optional object fields (e.g., String) are returned as nullable references.
    • Primitives: Optional primitive fields (e.g., int) are returned as nullable boxed types (e.g., Integer).
    • Safety: Developers can easily lift these nullable values into the functional world using Optional.ofNullable(value) if desired.
    • Interoperability: This approach works seamlessly with Kotlin's null-safety and Scala's Option type.
  11. Use lazy deserializers for performance and circular dependencies

    main

    The client uses ObjectBuilderDeserializer.lazy() to wrap deserializers. This approach provides two main benefits:

    1. Resolving Circular Dependencies: It prevents NullPointerException or StackOverflowError during class loading by deferring the initialization of deserializers that have recursive dependencies (common in queries and aggregations).
    2. Optimizing Request-Only Classes: For classes used only in requests (and not responses), the deserializer is only created if actually needed (e.g., when a user calls withJson() to create a request from a JSON string). This avoids unnecessary memory and CPU overhead during application startup.
    public static final JsonpDeserializer<TermQuery> _DESERIALIZER =
        ObjectBuilderDeserializer.lazy(
            TermQuery.Builder::new,
            TermQuery::setupTermQueryDeserializer
        );
  12. Build API objects using the Builder pattern

    main

    All data types in the Elasticsearch Java API Client are immutable. To create objects, you must use the builder pattern. You instantiate a Builder class, call setter methods to configure the object, and finally call .build() to create the immutable instance.

    Important: A builder should not be reused after its .build() method has been called.

    ElasticsearchClient esClient = createClient();
    CreateIndexResponse createResponse = esClient.indices().create(
        new CreateIndexRequest.Builder()
            .index("my-index")
            .aliases("foo",
                new Alias.Builder().isWriteIndex(true).build()
            )
            .build()
    );