Elasticsearch 指北

Table of Contents

本文旨在通过一个简单的例子——如何在使用 Maven 和 MySQL 的 Spring Boot 项目中使用 Elasticsearch 分析用户数量变化趋势——学习ES。

为了全面学习 ES,也可以参考 Java 全栈知识体系 的这篇文档:https://pdai.tech/md/db/nosql-es/elasticsearch.html

全文整理自和 Bing AI 的对话学习记录。

第零步:什么是ES?

Elasticsearch 是一个分布式的、免费的、开放的搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。它基于 Apache Lucene 构建,由 Elasticsearch N.V.(现在称为 Elastic)于 2010 年首次发布。它以其简单的 REST API、分布式性质、速度和可扩展性而闻名。

Elasticsearch 用于许多不同的场景,一些最常见的包括日志记录和日志分析、抓取和组合公共数据、全文搜索、事件数据和指标分析以及数据可视化。公司们使用 Elasticsearch 作为其主要的数据访问、检索和报告搜索平台。例如,沃尔玛使用 Elasticsearch 通过实时识别欺诈实例来防止经常针对老年人的礼品卡诈骗1

第一步:添加依赖

首先,需要通过在 pom.xml 文件中添加 spring-data-elasticsearch 依赖来将其添加到项目中。

第二步:创建 Elasticsearch 客户端 Bean

接下来需要创建一个 Elasticsearch 客户端 Bean。可以通过在 application.properties 文件中添加属性或使用 Java High-Level Rest Client (JHLC) 以编程方式构建 Bean 来完成此操作。下面是一个如何以编程方式创建 Elasticsearch 客户端 Bean 的示例:

@Configuration
@EnableElasticsearchRepositories
public class ElasticsearchClientConfiguration extends AbstractElasticsearchConfiguration {
    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();
        return RestClients.create(clientConfiguration).rest();
    }
}

JHLC 代表 Java High-level Rest Client。它是一个 Java 客户端,允许用户使用简单明了的 API 与 Elasticsearch 集群进行交互。JHLC 构建在 low-level Java REST client 之上,并为索引、搜索和更新文档等常见任务提供更高级别的 API。这是从 Java 应用程序与 Elasticsearch 交互的推荐方式。它是一个通用客户端,可用于与任何 RESTful Web 服务交互。但是,它通常与 Elasticsearch 一起使用,因为它为索引、搜索和更新文档等常见任务提供了简单明了的 API。

上述代码中,connectedTo("localhost:9200")指的是 ES cluster 的地址,而非 Spring boot 项目的地址。ES cluster 的搭建方法可以参见官网:https://www.elastic.co/downloads/elasticsearch

第三步:创建文档实体

一旦设置了 Elasticsearch 客户端,就需要创建一个 @Document 注释的实体来表示要索引和分析的用户数据。此实体应包括用户 ID、创建日期和其他相关信息的字段。例如:

@Document(indexName = "my-index")
public class MyEntity {
    @Id
    private String id;
    @Field(type = FieldType.Text)
    private String content;
    // getters and setters
}

Elasticsearch 可以处理的一项简单任务是全文搜索(Full-text search)。Full-text search 是一种搜索文档或文本集合以查找包含用户指定的一个或多个单词的技术。它不同于传统的数据库搜索技术,因为它不仅搜索与搜索词完全匹配的内容,而且还会考虑文档中搜索词之间的相关性和接近程度等因素。这使得查找与用户查询相关的文档成为可能,即使它们不包含确切的搜索词。

第四步:创建存储库接口

创建一个扩展 ElasticsearchRepository 的存储库接口来处理用户实体的 CRUD 操作。下面是一个示例:

public interface UserRepository extends ElasticsearchRepository<User, String> {
    @Query("{\"bool\": {\"must\": [{\"range\": {\"creationDate\": {\"gte\": \"?0\",\"lte\": \"?1\"}}}]}}")
    List<User> findByCreationDateBetween(Date startDate, Date endDate);
}

代码仅供参考。直接写 DSL 语句是很不规范的写法。规范的写法可参见 SpringBoot 整合 SpringDataElasticsearch – 唐浩荣 – 博客园

第五步:索引用户数据

接下来需要编写代码将用户数据索引到 Elasticsearch 中。可以通过从 MySQL 数据库检索数据并使用存储库提供的 save 方法将数据索引到 Elasticsearch 中来完成此操作。下面是一个如何执行此操作的示例:

@Service
public class UserService {
    private final UserRepository repository;
    private final JdbcTemplate jdbcTemplate;

    public UserService(UserRepository repository, JdbcTemplate jdbcTemplate) {
        this.repository = repository;
        this.jdbcTemplate = jdbcTemplate;
    }

    public void indexUsers() {
        List<User> users = jdbcTemplate.query("SELECT * FROM users", (rs, rowNum) -> new User(rs.getString("id"), rs.getDate("creation_date")));
        repository.saveAll(users);
    }
}

代码仅供参考。直接使用 JDBC 是非常不规范的写法。

第六步:查询和聚合数据

一旦数据被索引到 Elasticsearch 中,就可以使用存储库提供的 search 方法执行查询和聚合。例如,可以使用日期直方图聚合按创建日期对用户数据进行分组并计算每天创建的用户数量。下面是一个如何执行此操作的示例:

@Service
public class UserService {
    private final UserRepository repository;

    public UserService(UserRepository repository) {
        this.repository = repository;
    }

    public Map<Date, Long> getUserTrend(Date startDate, Date endDate) {
        DateHistogramAggregationBuilder aggregation = AggregationBuilders.dateHistogram("user_trend")
                .field("creationDate")
                .calendarInterval(DateHistogramInterval.DAY)
                .minDocCount(0L)
                .extendedBounds(new ExtendedBounds(startDate.getTime(), endDate.getTime()));

        NativeSearchQuery query = new NativeSearchQueryBuilder()
                .addAggregation(aggregation)
                .build();

        Aggregations aggregations = elasticsearchTemplate.query(query, SearchResponse::getAggregations);

        Histogram histogram = aggregations.get("user_trend");
        Map<Date, Long> trend = new LinkedHashMap<>();
        for (Histogram.Bucket bucket : histogram.getBuckets()) {
            trend.put(new Date(((DateTime) bucket.getKey()).getMillis()), bucket.getDocCount());
        }
        return trend;
    }
}
  • AggregationBuilders.dateHistogram("user_trend"):创建一个名为“user_trend”的新 DateHistogramAggregationBuilder 对象。该名称用于标识响应中的聚合。
  • field("creationDate"):这一行设置聚合操作的字段。将其设置为“creationDate”,这意味着聚合将按 creationDate 字段对我们的用户数据进行分组。
  • calendarInterval(DateHistogramInterval.DAY):此行设置日期直方图的间隔。在这种情况下,我们将其设置为 DateHistogramInterval.DAY,这意味着聚合会将我们的数据分组为一天的时间间隔。
  • minDocCount(0L):此行设置直方图中每个 bucket 的最小文档数。将其设置为 0L,这意味着空桶(即没有文档的桶)将包含在 response 中。
  • extendedBounds(new ExtendBounds(startDate.getTime(), endDate.getTime())):此行设置直方图的扩展范围。将其设置为 startDateendDate 之间的范围,这意味着聚合将仅包括落在该范围内的桶。

第七步:可视化数据

最后,可以使用 Kibana 等工具可视化数据并创建仪表板以帮助分析用户数量变化趋势。Kibana 是一种数据可视化和探索工具,旨在与 Elasticsearch 配合使用。可以使用它来创建以多种方式显示 Elasticsearch 数据的可视化效果和仪表板。

参考官方文档以安装 Kibana:https://www.elastic.co/guide/cn/kibana/current/install.html

安装 Kibana 后,配置它以连接到 Elasticsearch 集群。编辑 /etc/kibana/kibana.yml 文件并将 elasticsearch.hosts 属性设置为 Elasticsearch 集群的地址。

启动 Kibana 服务后,可以通过打开 Web 浏览器并导航到 http://your_server_ip:5601 来访问 Kibana Web UI。

以下是如何使用 Kibana 查看用户数量变化趋势的示例:

  1. 首先,配置 Kibana 以连接到 Elasticsearch 集群。按照 Kibana 文档中的说明执行此操作。
  2. 设置好 Kibana 并将其连接到 Elasticsearch 集群后,单击左侧菜单中的“Visualize”按钮来创建新的可视化。
  3. 在“Create a visualization”屏幕中,选择“Line”图表类型。
  4. 在“Data”选项卡中,从“Index pattern”下拉列表中选择您的用户索引。
  5. 在“Buckets”部分,单击“Add”按钮并选择“X-Axis”。
  6. 在“Aggregation”下拉菜单中,选择“Date Histogram”。
  7. 在“Field”下拉列表中,选择“creationDate”字段。
  8. 单击“Apply changes”按钮以更新图表。
  9. 现在应该会看到一个显示用户数量随时间变化趋势的折线图。

  1. <p><a href=”https://www.elastic.co/customers/”>https://www.elastic.co/customers/</a>&#160;<a href=”#fnref1:1″ rev=”footnote” class=”footnote-backref”>&#8617;</a></p>
Share