Elasticsearch-浅尝-java整合和一些相关概念理解

文章目录

概要

内容有点多,关于DSL语句我放在一个新的文章中了,不在这里赘述了,有问题大家可以随时交流:
提示:这里可以添加技术概要

Elasticsearch 是一个基于 Apache Lucene 构建的开源、分布式、RESTful 搜索和分析引擎。它使得存储、搜索和分析大量数据变得更加快速和简单。Elasticsearch 在垂直搜索领域被广泛使用,并且能够扩展至处理 PB 级别的数据。

核心特性

1、全文搜索:

  • Elasticsearch 强大的全文搜索能力是其最受欢迎的特性之一。它支持复杂的搜索查询,并且可以通过分析器进行高级文本处理。

2、分布式实时文档存储:

  • 每个索引可以分散到多个节点,并且分为多个分片。每个分片可以有零个或多个副本。

3、高可用和可扩展性:

  • 分片机制使得 Elasticsearch 可以水平扩展,增加节点可以提升容量和吞吐能力。副本机制提供了数据冗余和故障转移。

4、多租户:

  • Elasticsearch 支持多个索引(数据库),以及在同一索引中多种类型的文档(现已弃用),使其成为真正的多租户解决方案。

5、RESTful API:

  • 通过使用 JSON over HTTP 的方式,用户可以与 Elasticsearch 进行通信,执行各种操作,如数据的增删改查、管理集群等。

6、丰富的生态系统:

  • Elasticsearch 是 ELK(Elasticsearch, Logstash, Kibana)堆栈的核心组件,广泛用于日志分析、实时数据监控等场景。

使用场景:

1、全文搜索: 对大量文本数据进行即席查询,如网站搜索或文档检索。提供网站的搜索能力,包括高亮、自动纠错、搜索建议等高级搜索特性

2、日志分析: 存储、搜索和分析日志或事件数据,帮助监控应用和基础设施。

3、实时分析: 分析和可视化实时数据流,例如社交媒体信息或系统监控数据。

4、数据聚合: 对大数据进行聚合查询和分析,深度挖掘数据模式和趋势。

5、地理空间搜索: 处理和查询地理位置数据,比如寻找特定区域内的对象。

6、指标分析和可视化: 使用 Kibana 或其他可视化工具,可以对收集的指标数据进行分析和展示。

使用Elasticsearch的场合往往是因为它提供了快速的搜索能力、处理大规模数据的能力,以及相较于传统关系型数据库更为灵活的数据模型。如果你的应用需要以上的能力,考虑Elasticsearch往往是一个合适的选择。

概念描述

提示:这里可以添加技术整体架构

1、文档 (Document)

  • 描述:文档是 Elasticsearch 中可以被索引的基本数据单位。通常用 JSON 格式表示,它包含了一些字段或键值对。
  • 联系:每个文档都存储在一个索引中,并且包括一个或多个映射类型中的类型定义。

2、映射 (Mapping)

  • 描述:映射类似于数据库中的表结构定义,它定义了文档可能包含的字段及其数据类型。
  • 联系:映射用于告诉 Elasticsearch 如何理解文档中的字段数据类型及其如何被索引和搜索。

3、索引 (Index)

  • 描述:索引是一组拥有相似特征的文档的集合。它是文档存储和检索的地方。
  • 联系:索引是分片的容器,其中的文档按映射的规则进行组织,并可以被搜索和查询。

4、类型 (Type, 已在较新版本中弃用)

  • 描述:类型在早期版本的 Elasticsearch 中用于对索引内的文档进行逻辑分组。
  • 联系:由于弃用,类型的概念不再是 Elasticsearch 的重要组成部分,现代应用被建议使用单独的索引替代类型。

5、分片 (Shard)

  • 描述:分片是索引的一个子集,是数据存储的基本单位。Elasticsearch 自动将索引数据分布到多个分片中,且每个分片是一个功能完整的搜索引擎。
  • 联系:分片使得 Elasticsearch 能够水平扩展以存储和处理更多数据。每个分片可以有零个或多个副本分片。

6、副本 (Replica)

  • 描述:副本是分片的一个复制副本,用于提供数据的冗余,以提高系统的可靠性和搜索操作的并行性。

  • 联系:副本分片可以在原有的分片丢失或出现问题时提供数据的备份。同时,它们也可以处理搜索查询,进而提高查询性能和提供高可用性。

7、节点 (Node)

  • 描述:节点是运行着 Elasticsearch 实例的单个服务器。节点可以存储数据并参与集群的索引和搜索能力。
  • 联系:节点可以持有多个分片,包括主分片和副本分片。节点相互协同工作构成 Elasticsearch 集群。

8、集群 (Cluster)

  • 描述:集群是一个或多个节点的集合,它们共同工作,共享数据,提供相关的索引和搜索功能。
  • 联系:集群将所有的节点资源汇聚起来,提供跨所有节点的全局索引和搜索能力。

9、查询 (Query)

  • 描述:查询用于检索出匹配某些条件的文档集合。Elasticsearch 提供了丰富的查询DSL(域特定语言)供用户定义和执行复杂的搜索。
  • 联系:查询操作在索引的分片上执行,可以利用分片的并行性来提高性能。

10、过滤 (Filter)

  • 描述:过滤和查询类似,但主要用于快速确定哪些文档与指定的条件相匹配,通常不涉及相关性打分。
  • 联系:过滤可以缓存,因此对于重复执行的过滤条件,性能较高。

11、聚合 (Aggregation)

  • 描述:聚合功能用于基于搜索查询结果进一步分析和总结数据,比如求和、计数、平均值和分组等。
  • 联系:聚合可以在索引的文档上执行复杂的数据分析,依赖于文档存储和搜索的基础设施。

12、分词器 (Tokenizer) 和 分析器 (Analyzer)

  • 描述:在索引过程中,文档中的文本字段会被分析器分解成一系列的独立词条或"tokens"。这些tokens用于构建反向索引,以便于进行全文搜索。
  • 联系:分析器由分词器和多个标记过滤器组成,用于处理字段的文本,并决定如何将这些文本索引到集群中。

在 Elasticsearch 中,以上各个概念紧密联系,并共同工作以支持高效、弹性和可拓展的搜索和数据分析能力。

文档是数据的核心,映射定义了文档如何被索引,索引将数据组织在一起并分布在不同的分片上。

分片和其副本被分配到集群中的节点上,这些节点协同工作以处理查询、过滤、聚合等操作。

分析器在文档被索引前对文本数据进行处理,确保数据在搜索时能被正确理解和匹配。

java整合:

整合Elasticsearch和Java主要通过使用Elasticsearch提供的Java客户端。有两种主要的客户端可以使用:

1、Elasticsearch Java High Level REST Client: 基于Elasticsearch的REST API,提供了一套高级别的API来操作Elasticsearch中的数据。

2、Elasticsearch Java Low Level REST Client: 提供了与Elasticsearch RESTful API通讯的基础客户端,可以执行所有的Elasticsearch操作。

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
        <version>7.10.0</version>
    </dependency>
</dependencies>

注意:请以官方文档中提供的最新稳定版本作为准则。

创建Elasticsearch客户端

java 复制代码
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;

public class ElasticsearchClient {
    private static final String HOST = "localhost";
    private static final int PORT = 9200;
    private static final String SCHEME = "http";

    public static RestHighLevelClient createClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost(HOST, PORT, SCHEME)
                )
        );
    }
}

索引一个文档

java 复制代码
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;

import java.util.HashMap;
import java.util.Map;

public class IndexDocumentExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            Map<String, Object> jsonMap = new HashMap<>();
            jsonMap.put("field1", "value1");
            jsonMap.put("field2", "value2");

            IndexRequest indexRequest = new IndexRequest("index_name")
                    .id("document_id").source(jsonMap);

            IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);

            System.out.println("Document indexed with version: " + indexResponse.getVersion());
        } finally {
            client.close();
        }
    }
}

搜索文档

java 复制代码
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;

public class SearchDocumentExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            SearchRequest searchRequest = new SearchRequest("index_name");

            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());

            searchRequest.source(searchSourceBuilder);

            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

            Arrays.stream(searchResponse.getHits().getHits())
                .forEach(hit -> System.out.println(hit.getSourceAsString()));
        } finally {
            client.close();
        }
    }
}

这些代码示例展示了如何使用Elasticsearch的Java客户端进行基本的文档操作,包括创建客户端、索引文档以及执行搜索。更复杂的操作,如聚合、更新、删除等,同样通过构造对应的请求实现。

请注意,代码示例需要处理异常,通常是通过try-catch块捕获IOException来实现。此外,客户端资源应当妥善管理,在使用完成后关闭。

此外:在生产环境中,您需要格外小心地处理连接的创建与销毁,并确保代码的健壮性和鲁棒性。以下是针对更新、删除文档操作和错误处理的代码示例。

更新文档

使用UpdateRequest可以更新现有文档。

java 复制代码
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentType;

public class UpdateDocumentExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            // Create an UpdateRequest for the specified index, document ID
            UpdateRequest updateRequest = new UpdateRequest("index_name", "document_id");

            // Prepare the content for update
            String jsonString = "{" +
                "\"field1\":\"updated_value1\"," +
                "\"field2\":\"updated_value2\"" +
                "}";
            
            // Update the document
            updateRequest.doc(jsonString, XContentType.JSON);

            // Perform the update
            UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);

            System.out.println("Document updated with version: " + updateResponse.getVersion());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Close the client
            client.close();
        }
    }
}

删除文档

删除文档通过创建一个DeleteRequest来实现。

java 复制代码
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.client.RequestOptions;

public class DeleteDocumentExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            // Create a DeleteRequest for the specified index and document ID
            DeleteRequest deleteRequest = new DeleteRequest("index_name", "document_id");

            // Perform the deletion
            DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);

            System.out.println("Document deleted with version: " + deleteResponse.getVersion());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Close the client
            client.close();
        }
    }
}

错误处理

当您与Elasticsearch互动时,可能会遇到各种异常。以下是您如何处理这些异常的示例。

java 复制代码
import org.elasticsearch.ElasticsearchException;

// ... (in a try-catch block within your Elasticsearch operation)
} catch (ElasticsearchException e) {
    if (e.status() == RestStatus.NOT_FOUND) {
        System.err.println("The document was not found.");
    } else if (e.status() == RestStatus.CONFLICT) {
        System.err.println("A version conflict occurred.");
    } else {
        System.err.println("An Elasticsearch exception occurred: " + e.getDetailedMessage());
    }
} catch (IOException e) {
    System.err.println("Communication with the server failed: " + e.getMessage());
} finally {
    // Close the client
    client.close();
}

在以上示例中,我们捕获了ElasticsearchException来处理特定的HTTP状态码。例如,404表示文档未找到,而409表示版本冲突。任何其他异常都归类为通信或Elasticsearch引擎错误。

确保正确处理这些异常对避免资源泄露及应用的健壮性至关重要。您应该总是在finally块中关闭客户端,以确保清理资源,即使操作发生异常也是如此。

结束后,记得在每个操作中都适当地处理异常,以维持程序的稳定性和健壮性。这个通用的错误处理模式可以帮助您进行调试,并在生产环境中更好地处理问题。

了解了如何与Elasticsearch进行基本的文档操作,接下来我会阐述与集群监控、性能优化和高级查询等相关的Java客户端使用实践。这提供了潜在的使用方式,了解如何更深入地与Elasticsearch集成。

集群状态监控

您可以使用Java客户端获取Elasticsearch集群的健康状况和其他统计数据,此信息对于监控和调优至关重要。

java 复制代码
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.cluster.health.ClusterHealthStatus;

public class ClusterHealthExample {
    public static void main(String[] args) {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            // Prepare the request
            ClusterHealthRequest request = new ClusterHealthRequest();
            
            // Perform the health check
            ClusterHealthResponse response = client.cluster().health(request, RequestOptions.DEFAULT);

            String clusterName = response.getClusterName();
            ClusterHealthStatus status = response.getStatus();

            System.out.println("Cluster Name: " + clusterName);
            System.out.println("Cluster Health Status: " + status.name());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Close the client
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

此代码会输出集群的名称和健康状态,其中健康状态是通过一个枚举表示的,可以是GREEN、YELLOW或RED。

性能优化

为了优化性能,您可能需要调整分片的数量,修改副本数,或者是调整索引管理策略。这些通常通过对Elasticsearch的配置文件进行更改来完成,但也可以通过API动态调整某些参数。

使用Scroll API进行深分页

在Elasticsearch中,Scroll API用于检索大量数据(通常称为"深分页"),而不是一次性检索所有数据。这对于处理大量数据很有用,因为一次性加载所有数据可能会对Elasticsearch集群的性能产生负面影响。Scroll API可以在一段时间内保持搜索上下文,允许应用程序批量获取结果集。

1、初始化Scroll:

  • 首先,执行一个带有scroll参数的搜索请求。这个参数指定了scroll上下文要保持活跃的时间。响应中将包含一个 _scroll_id,它用于后续的滚动。
java 复制代码
POST /<index>/_search?scroll=5m
{
  "size": 1000,
  "query": {
    "match_all": {}
  }
}

2、使用Scroll ID继续检索:

  • 用上一步骤获得的_scroll_id执行一个搜索滚动请求,以获取下一批结果。这样反复进行,直到获取了所有的数据。
java 复制代码
POST /_search/scroll 
{
  "scroll": "5m", 
  "scroll_id": "<scroll_id_from_previous_response>" 
}

3、清除Scroll:

  • 当不再需要scroll上下文时,应该清除scroll ID以释放在Elasticsearch集群中占用的资源。
java 复制代码
DELETE /_search/scroll
{
  "scroll_id" : ["<scroll_id_from_previous_response>"]
}

首先,确保你的Java项目已经加入了Elasticsearch High Level REST Client作为依赖。

接下来,可以参考以下代码来实现用Scroll API的深分页功能。

java 复制代码
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.slice.SliceBuilder;
import org.elasticsearch.client.core.ClearScrollRequest;
import org.elasticsearch.client.core.ClearScrollResponse;

public class DeepPaginationWithScroll {

  private static final long SCROLL_TIMEOUT = TimeValue.timeValueMinutes(5).millis();
  private static final int BATCH_SIZE = 1000;

  public static void main(String[] args) throws IOException {
    // 初始化客户端
    RestHighLevelClient client = new RestHighLevelClient(...);

    try {
      // 初始化Scroll
      final SearchRequest searchRequest = new SearchRequest("index_name");
      searchRequest.scroll(TimeValue.timeValueMillis(SCROLL_TIMEOUT));
      SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
      searchSourceBuilder.query(QueryBuilders.matchAllQuery());
      searchSourceBuilder.size(BATCH_SIZE);
      searchRequest.source(searchSourceBuilder);

      SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
      String scrollId = searchResponse.getScrollId();
      SearchHit[] searchHits = searchResponse.getHits().getHits();

      // 循环处理结果,直到没有文档返回
      while (searchHits != null && searchHits.length > 0) {
        // 处理获取到的结果批次
        for (SearchHit searchHit : searchHits) {
          // 处理每个文档...
        }

        // 使用Scroll ID继续检索
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
        scrollRequest.scroll(TimeValue.timeValueMillis(SCROLL_TIMEOUT));
        searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
        scrollId = searchResponse.getScrollId();
        searchHits = searchResponse.getHits().getHits();
      }

      // 最终,清除SCROLL ID
      ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
      clearScrollRequest.addScrollId(scrollId);
      ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
      boolean succeeded = clearScrollResponse.isSucceeded();
      // 根据succeeded做相关处理...

    } finally {
      // 清理资源
      client.close();
    }
  }
}

在本示例中,我们首先使用Scroll API初始化一个查询,保存返回的scroll ID,然后再循环地使用这个scroll ID来获取数据。我们应该设置响应中的大小size(每个批次的文档数量),并确定scroll的时间窗口。

当检索完成后,或者在程序中不再需要继续滚动检索时,使用提供的scroll ID来清除scroll上下文,这对于资源优化很重要。在使用Scroll API时,推荐的实践是始终在finally块中调用client.close()来清理客户端资源。

高级查询构建

Elasticsearch支持多种复杂查询方式,这些可以通过Java客户端的查询构建器进行配置和使用。

java 复制代码
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;

public class AdvancedSearchExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        // Example of a boolean query that combines several conditions
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
            .must(QueryBuilders.matchQuery("fieldName1", "value1"))
            .mustNot(QueryBuilders.rangeQuery("fieldName2").gte("value2"))
            .should(QueryBuilders.termQuery("fieldName3", "value3"))
            .filter(QueryBuilders.termQuery("fieldName4", "value4"));

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(boolQueryBuilder);

        SearchRequest searchRequest = new SearchRequest("index_name");
        searchRequest.source(searchSourceBuilder);

        try {
            // Perform the search request
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

            // Process the search hits
            Arrays.stream(searchResponse.getHits().getHits())
                .forEach(hit -> System.out.println(hit.getSourceAsString()));

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // Close the client
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这段代码中,我们构建了一个布尔查询,它结合了多个条件:必须匹配一个值,必须不包含另一个范围的值,应该匹配第三个值,以及按第四个值进行过滤。这是Elasticsearch高级查询的一个简单示例,展现了如何通过Java构建复杂的查询逻辑。

由于前面的例子已经涵盖了Elasticsearch Java客户端的基本使用,接下来我们将探讨一些更为高级的使用场景和最佳实践。

异步操作

Elasticsearch高级REST客户端提供了异步执行请求的能力。这对于不想阻塞调用线程的情况非常有用。

java 复制代码
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;

// ...省略其他引入...

public class AsyncSearchExample {
    public static void main(String[] args) {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        // Create a search request
        SearchRequest searchRequest = new SearchRequest("index_name");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchRequest.source(searchSourceBuilder);

        // Execute the search request asynchronously
        client.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener<SearchResponse>() {
            @Override
            public void onResponse(SearchResponse searchResponse) {
                // Handle the response
                Arrays.stream(searchResponse.getHits().getHits())
                    .forEach(hit -> System.out.println(hit.getSourceAsString()));
            }

            @Override
            public void onFailure(Exception e) {
                // Handle the failure
                e.printStackTrace();
            }
        });
        
        // Note: The client should be closed after it is not needed anymore, 
        // potentially in some kind of shutdown hook to make sure it is executed
    }
}

请注意,在这种情况下,由于我们执行的是异步操作,我们通常不会立刻关闭客户端。客户端的关闭逻辑应该在确保所有异步操作已经完成后进行(例如,在应用程序关闭时)。

使用Aggregations聚合数据

Elasticsearch强大的特性之一是聚合框架,它允许你构建复杂的数据分析。以下示例演示如何对文档中的字段进行聚合并进行统计分析:

java 复制代码
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;

// ...省略其他引入...

public class AggregationsExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            SearchRequest searchRequest = new SearchRequest("index_name");
            
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.aggregation(
                AggregationBuilders.terms("agg_by_field")
                    .field("field_name")
                    .order(BucketOrder.count(true))
                    .size(10) // Top 10 terms
            );

            searchRequest.source(searchSourceBuilder);
            
            // Execute the search request
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            
            // Retrieve the aggregation results
            Terms terms = searchResponse.getAggregations().get("agg_by_field");
            for (Terms.Bucket bucket : terms.getBuckets()) {
                System.out.println(bucket.getKeyAsString() + ": " + bucket.getDocCount());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // Close the client
            client.close();
        }
    }
}

在上面的代码中,我们使用了一个名为agg_by_field的术语聚合来统计特定字段中出现的术语及其出现次数,并将结果进行了排序。这样的聚合可以在大量数据中找到重要的数据模式和统计信息,它们常用于数据分析和可视化场景。

索引管理与映射

在创建索引或者修改索引映射时,可以使用Java客户端的相关API直接从Java应用程序中管理这些操作。

以下是使用Java High Level REST Client创建定义了映射的索引的示例:

java 复制代码
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;

// ...省略其他引入...

public class IndexManagementExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            // Prepare a new index request with settings and mappings
            CreateIndexRequest createIndexRequest = new CreateIndexRequest("index_name");
            createIndexRequest.settings(Settings.builder()
                    .put("index.number_of_shards", 3)
                    .put("index.number_of_replicas", 1)
            );
            createIndexRequest.mapping(
                "{\n" +
                "  \"properties\": {\n" +
                "    \"field1\": { \"type\": \"text\" },\n" +
                "    \"field2\": { \"type\": \"keyword\" }\n" +
                "  }\n" +
                "}", 
                XContentType.JSON
            );
            
            // Create the index with the provided settings and mappings
            CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);

            if (createIndexResponse.isAcknowledged()) {
                System.out.println("Index created");
            } else {
                System.out.println("Index creation failed");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // Close the client
            client.close();
        }
    }
}

此示例演示了如何设置索引如分片数和副本数等相关配置。同时也展示了如何在索引创建时加入映射信息,这样可以定义每个字段的类型,例如text或keyword等。

优化连接和线程管理

在实际应用中,与Elasticsearch集群建立的客户端连接管理是非常重要的环节。客户端的连接数、线程数应根据应用程序负载、集群容量及负载均衡来合理配置。

Elasticsearch Java客户端内部使用Apache HttpAsyncClient进行HTTP通信,允许自定义多线程配置以适应不同的负载需求。

以下是配置连接数和线

java 复制代码
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.elasticsearch.client.RestClientBuilder;

// ...省略其他引入...

public class ElasticsearchClientConfig {
    public static RestHighLevelClient createClient() {
        RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9200, "http"));
        
        // Customize the client with a callback
        builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
            @Override
            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                return httpClientBuilder.setDefaultIOReactorConfig(
                    IOReactorConfig.custom()
                        .setIoThreadCount(Runtime.getRuntime().availableProcessors())
                        .setConnectTimeout(1000)
                        .setSoTimeout(1000)
                        .build()
                );
            }
        });

        return new RestHighLevelClient(builder);
    }
    
    public static void main(String[] args) {
        // Use the createClient method to get a client configured with the custom settings.
        RestHighLevelClient client = createClient();
        
        // Remember to close the client after use
    }
}

在上述代码中,我们通过自定义一个HttpClientConfigCallback来配置Elasticsearch客户端。在这个回调中,我们设置了IO反应器配置,例如IO线程数(根据可用的处理器核心数)和连接超时设置。

这些设置可以为Elasticsearch客户端提供一个更加优化的线程模型,特别是在高性能应用中。不同的配置选项会对客户端的行为和性能有显著的影响,需要根据实际场景进行调优。

使用别名进行索引管理

Elasticsearch中的别名允许对索引进行更灵活的管理。别名可以作为索引的引用来使用,这对于在不中断服务的情况下重建或更改索引非常有用。例如,当你需要更改索引结构,或者重新导入数据以优化查询性能时,可以先构建一个新的索引,然后通过更改别名的指向来无缝切换到新索引。

以下是使用Elasticsearch Java客户端如何管理别名的例子:

java 复制代码
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;

// ...省略其他引入...

public class AliasManagementExample {
    public static void main(String[] args) throws IOException {
        RestHighLevelClient client = ElasticsearchClient.createClient();

        try {
            // 添加别名
            IndicesAliasesRequest aliasRequest = new IndicesAliasesRequest();
            IndicesAliasesRequest.AliasActions addAliasAction = new IndicesAliasesRequest.AliasActions(
                IndicesAliasesRequest.AliasActions.Type.ADD
            ).index("index_name_new").alias("alias_name");
            aliasRequest.addAliasAction(addAliasAction);

            // 执行添加别名
            client.indices().updateAliases(aliasRequest, RequestOptions.DEFAULT);
        
            // 删除别名
            IndicesAliasesRequest removeAliasRequest = new IndicesAliasesRequest();
            IndicesAliasesRequest.AliasActions removeAliasAction = new IndicesAliasesRequest.AliasActions(
                IndicesAliasesRequest.AliasActions.Type.REMOVE
            ).index("index_name_old").alias("alias_name");
            removeAliasRequest.addAliasAction(removeAliasAction);

            // 执行删除别名
            client.indices().updateAliases(removeAliasRequest, RequestOptions.DEFAULT);

            // 更换别名指向新的索引
            IndicesAliasesRequest switchAliasRequest = new IndicesAliasesRequest();
            switchAliasRequest.addAliasAction(
                IndicesAliasesRequest.AliasActions.remove().index("index_name_old").alias("alias_name")
            ).addAliasAction(
                IndicesAliasesRequest.AliasActions.add().index("index_name_new").alias("alias_name")
            );

            // 执行别名更换
            client.indices().updateAliases(switchAliasRequest, RequestOptions.DEFAULT);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // Close the client
            client.close();
        }
    }
}

在上述代码中,首先创建了IndicesAliasesRequest来添加新索引的别名,然后执行了添加操作,这样新的索引就可以通过别名被检索到。然后为了完成切换,我们首先删除旧索引的别名,再添加别名到新索引,这两个操作可以打包在同一个请求中原子性地完成。

使用别名的另一个好处是它可以作为查询的过滤器,这样你可以为不同的用户或应用程序创建视图级别的别名。

监听节点变化

在一些场景下,你可能需要跟踪集群的变化,如节点加入或离开集群。Elasticsearch客户端可以配置节点选择器来控制哪些节点可以接受请求。如果配置监听器,客户端可以在发生这些事件时作出反应。

以下是如何配置监听器的示例:

java 复制代码
import org.elasticsearch.client.Node;
import org.elasticsearch.client.NodeListener;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;

// ...省略其他引入...

public class NodeChangeListenerExample {
    public static void main(String[] args) {
        RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9200, "http"));

        // 自定义监听器
        builder.setNodeListener(new NodeListener() {
            @Override
            public void onNodeFailure(Node node, Exception ex) {
                System.out.println("Node failed: " + node + " with exception: " + ex.getMessage());
            }

            @Override
            public void onNodeAdded(Node node) {
                System.out.println("Node added: " + node);
            }

            @Override
            public void onNodeRemoved(Node node) {
                System.out.println("Node removed: " + node);
            }
        });

        RestHighLevelClient client = new RestHighLevelClient(builder);

        // ... 使用client进行相关操作 ...

        // 最终关闭客户端
        try {
            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在自定义的NodeListener中,我们可以实现onNodeAdded、onNodeRemoved和onNodeFailure方法,以响应相应的集群节点事件。

总之,Elasticsearch Java客户端为开发人员提供了广泛的功能来管理和操作Elasticsearch集群。通过合适的客户端,结合Elasticsearch强大的查询语言和索引功能,您可以构建高效且强大的搜索和数据分析解决方案。在你的应用程序中正确地管理客户端连接、理解并优化查询以及高效使用批量和滚动API,将有助于提升整个系统的性能和可靠性。

小结

内容有点多看到这里确实是不容易,有啥问题及时沟通:

相关推荐
萤丰信息2 分钟前
数字经济与 “双碳” 战略双轮驱动下 智慧园区的智能化管理实践与未来演进
大数据·人工智能·科技·智慧城市·智慧园区
pingao1413783 分钟前
实时远程监控,4G温湿度传感器守护环境安全
大数据·人工智能·安全
程农8 分钟前
java计算机毕业设计婚纱摄影网站(附源码、数据库)
java·数据库·课程设计
BlockChain8889 分钟前
Spring框架终极入门指南(12000字深度解析)
java·后端·python·spring
你才是臭弟弟11 分钟前
TDengine TSDB(数据备份与恢复)
大数据·时序数据库·tdengine
川西胖墩墩12 分钟前
网站开发完整流程梳理
大数据·数据库·架构·流程图·敏捷流程
AI智能探索者19 分钟前
大数据特征工程:如何处理文本与数值混合特征
大数据·ai
发哥来了19 分钟前
主流AI视频生成商用方案选型:关键维度与成本效益分析
大数据·人工智能
青云交23 分钟前
Java 大视界 -- Java 大数据实战:分布式架构重构气象预警平台(2 小时→2 分钟)
java·java 大数据 气象预警平台·flink 实时数据清洗·spark 区域定制模型·气象灾害预警系统
柏林以东_23 分钟前
异常的分类与用法
java·开发语言