Bootstrap

Elasticsearch 在 Java 中的使用教程

目录
  1. Elasticsearch 简介
  2. 环境准备
  3. 使用 Java 连接 Elasticsearch
  4. 基本 CRUD 操作
  5. 复杂查询操作
  6. 索引管理与优化
  7. 聚合操作
  8. 总结

1. Elasticsearch 简介

Elasticsearch 是一个分布式搜索和分析引擎,基于 Apache Lucene 构建,能够实现实时数据的存储、搜索、和分析。它广泛应用于全文搜索、日志分析、性能监控等领域。Elasticsearch 的核心概念包括文档(document)、索引(index)、和分片(shard)。

2. 环境准备

2.1 安装 Elasticsearch

Elasticsearch 官方网站 下载并安装适合你操作系统的版本。

安装完成后,通过以下命令启动 Elasticsearch 服务:

./bin/elasticsearch

默认情况下,Elasticsearch 运行在 http://localhost:9200

2.2 Java 开发环境配置
  1. 安装 Java SDK(JDK 11 或更高版本)。
  2. 安装 Maven 或 Gradle。
  3. 创建一个 Maven 项目,添加 Elasticsearch Java 客户端的依赖。
2.3 添加 Elasticsearch 客户端依赖

在 Maven 项目的 pom.xml 文件中添加以下依赖:

<dependencies>
    <!-- Elasticsearch Java Client -->
    <dependency>
        <groupId>co.elastic.clients</groupId>
        <artifactId>elasticsearch-java</artifactId>
        <version>8.10.0</version>
    </dependency>
</dependencies>

如果你使用 Gradle,可以在 build.gradle 文件中添加以下依赖:

dependencies {
    implementation 'co.elastic.clients:elasticsearch-java:8.10.0'
}

添加依赖后,确保项目能够正常编译。

3. 使用 Java 连接 Elasticsearch

接下来,我们将编写一个简单的 Java 程序来连接 Elasticsearch。

3.1 编写连接代码

创建一个 Java 类,例如 ElasticsearchConnection.java,并编写以下代码:

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.InfoResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;

public class ElasticsearchConnection {
    public static void main(String[] args) throws Exception {
        // 创建 REST 客户端
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();

        // 创建传输层
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());

        // 创建 Elasticsearch 客户端
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 获取集群信息
        InfoResponse info = client.info();
        System.out.println("Connected to Elasticsearch cluster: " + info.clusterName());

        // 关闭客户端
        transport.close();
    }
}
3.2 运行代码

编译并运行这个程序,如果成功,你将看到类似如下的输出:

Connected to Elasticsearch cluster: elasticsearch

这表明你已经成功连接到了 Elasticsearch。

4. 基本 CRUD 操作

Elasticsearch 的 CRUD 操作主要涉及对索引中的文档进行增(Create)、查(Read)、改(Update)、删(Delete)操作。

4.1 创建索引和插入文档
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.core.IndexRequest;

public class ElasticsearchCRUD {
    public static void main(String[] args) throws Exception {
        // 创建 REST 客户端
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();

        // 创建传输层
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());

        // 创建 Elasticsearch 客户端
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 创建文档
        String json = "{ \"name\": \"John Doe\", \"age\": 30, \"city\": \"New York\" }";

        // 插入文档到索引
        IndexRequest<Object> request = new IndexRequest.Builder<>()
            .index("users")
            .id("1")
            .document(json)
            .build();

        IndexResponse response = client.index(request);
        System.out.println("Document inserted with ID: " + response.id());

        // 关闭客户端
        transport.close();
    }
}
4.2 查询文档
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.GetResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.core.GetRequest;

public class ElasticsearchRead {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 查询文档
        GetRequest getRequest = new GetRequest.Builder()
                .index("users")
                .id("1")
                .build();

        GetResponse<Object> response = client.get(getRequest, Object.class);
        if (response.found()) {
            System.out.println("Document found: " + response.source());
        } else {
            System.out.println("Document not found");
        }

        transport.close();
    }
}
4.3 更新文档
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.UpdateResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.core.UpdateRequest;

import java.util.Map;

public class ElasticsearchUpdate {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 更新文档
        Map<String, Object> updateJson = Map.of(
                "age", 31,
                "city", "San Francisco"
        );

        UpdateRequest<Object, Map<String, Object>> updateRequest = new UpdateRequest.Builder<>()
                .index("users")
                .id("1")
                .doc(updateJson)
                .build();

        UpdateResponse<Object> updateResponse = client.update(updateRequest, Object.class);
        System.out.println("Document updated: " + updateResponse.result());

        transport.close();
    }
}
4.4 删除文档
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.DeleteResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.core.DeleteRequest;

public class ElasticsearchDelete {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 删除文档
        DeleteRequest deleteRequest = new DeleteRequest.Builder()
                .index("users")
                .id("1")
                .build();

        DeleteResponse deleteResponse = client.delete(deleteRequest);
        System.out.println("Document deleted: " + deleteResponse.result());

        transport.close();
    }
}

5. 复杂查询操作

Elasticsearch 提供了强大的查询 DSL(Domain Specific Language),支持布尔查询、范围查询、聚合查询等。

5.1 布尔查询
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.search.Query;

public class ElasticsearchBooleanQuery {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 布尔查询
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("users")
                .query(Query
                .bool(b -> b
                    .must(m -> m.match(match -> match.field("name").query("John Doe")))
                    .filter(f -> f.range(r -> r.field("age").gte(30)))
                ))
                .build();

        SearchResponse<Object> searchResponse = client.search(searchRequest, Object.class);

        for (Hit<Object> hit : searchResponse.hits().hits()) {
            System.out.println("Document found: " + hit.source());
        }

        transport.close();
    }
}
5.2 范围查询
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.search.Query;

public class ElasticsearchRangeQuery {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 范围查询:查询 age 在 25 到 35 之间的文档
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("users")
                .query(Query.range(r -> r.field("age").gte(25).lte(35)))
                .build();

        SearchResponse<Object> searchResponse = client.search(searchRequest, Object.class);

        for (Hit<Object> hit : searchResponse.hits().hits()) {
            System.out.println("Document found: " + hit.source());
        }

        transport.close();
    }
}

6. 索引管理与优化

在 Elasticsearch 中,索引管理是非常重要的操作,合理的索引设置和优化可以大幅提升查询性能。

6.1 创建索引并设置映射
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;

public class ElasticsearchCreateIndex {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 创建索引并设置映射
        CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder()
                .index("users")
                .mappings(m -> m
                    .properties("name", p -> p.text(t -> t))
                    .properties("age", p -> p.integer(i -> i))
                    .properties("city", p -> p.text(t -> t))
                )
                .build();

        CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest);
        if (createIndexResponse.acknowledged()) {
            System.out.println("Index created successfully!");
        }

        transport.close();
    }
}
6.2 删除索引
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;

public class ElasticsearchDeleteIndex {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 删除索引
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest.Builder()
                .index("users")
                .build();

        DeleteIndexResponse deleteIndexResponse = client.indices().delete(deleteIndexRequest);
        if (deleteIndexResponse.acknowledged()) {
            System.out.println("Index deleted successfully!");
        }

        transport.close();
    }
}

7. 聚合操作

Elasticsearch 的聚合功能非常强大,能够对数据进行分组统计、计算平均值、最大值、最小值等操作。

7.1 聚合查询:计算年龄的平均值
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Aggregation;
import co.elastic.clients.elasticsearch.core.search.Bucket;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import co.elastic.clients.elasticsearch.core.SearchRequest;

public class ElasticsearchAggregation {
    public static void main(String[] args) throws Exception {
        RestClient restClient = RestClient.builder(
                new HttpHost("localhost", 9200, "http")).build();
        RestClientTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 聚合查询:计算年龄的平均值
        SearchRequest searchRequest = new SearchRequest.Builder()
                .index("users")
                .size(0)  // 不返回文档,只返回聚合结果
                .aggregations("average_age", a -> a.avg(avg -> avg.field("age")))
                .build();

        SearchResponse<Object> searchResponse = client.search(searchRequest, Object.class);

        double averageAge = searchResponse.aggregations().get("average_age").avg().value();
        System.out.println("Average age: " + averageAge);

        transport.close();
    }
}

8. 总结

本教程详细介绍了如何在 Java 中使用 Elasticsearch,涵盖了连接、基本 CRUD 操作、复杂查询、索引管理和聚合操作等方面的内容。通过这些示例,开发者可以一步步地掌握如何在 Java 项目中集成 Elasticsearch,并利用其强大的搜索和分析功能来构建高效的应用程序。

;