Spring Boot 邂逅Elasticsearch:打造搜索"光速引擎"
一、为什么要整合 Spring Boot 与 Elasticsearch
在当今这个数据爆炸的时代,海量数据处理和高效搜索已经成为众多应用系统绕不开的关键挑战。就拿电商平台来说,商品数据量可能轻松达到百万甚至千万级别,用户在搜索商品时,传统的数据库搜索方式往往难以快速、精准地返回结果,这不仅影响用户体验,还可能导致用户流失。再比如,在日志分析场景中,随着业务规模的扩大,每天产生的日志数据量巨大,传统的分析方法根本无法满足快速定位问题和分析趋势的需求。
Elasticsearch 作为一款基于 Lucene 的分布式搜索引擎,在处理这些复杂场景时展现出了巨大的优势。它采用分布式架构,能够将数据分布在多个节点上,轻松实现水平扩展,从而处理海量数据。其强大的全文搜索功能,借助倒排索引等技术,能在海量文本数据中快速定位到用户所需信息,无论是关键词搜索、模糊搜索还是短语搜索,都能轻松应对。并且,Elasticsearch 还具备实时数据分析能力,能对数据进行统计、分组、排序等操作,为业务决策提供有力支持。
而 Spring Boot 作为简化 Spring 应用开发的利器,它的出现大大降低了 Spring 应用的开发门槛和复杂度。通过自动配置和约定优于配置的原则,Spring Boot 让开发者可以快速搭建起一个稳定的应用框架,专注于业务逻辑的实现,而无需花费大量时间在繁琐的配置上。它内置的服务器、自动化依赖管理以及丰富的 Starter 依赖等特性,使得开发、测试和部署都变得更加高效和便捷。
当我们将 Spring Boot 与 Elasticsearch 整合起来时,就像是为搜索功能插上了 "光速翅膀"。Spring Boot 提供的便捷开发框架与 Elasticsearch 强大的搜索能力相结合,既能提升搜索的效率和准确性,又能加快开发速度,减少开发过程中的各种麻烦。开发者可以利用 Spring Boot 的特性轻松集成 Elasticsearch,实现高效的数据存储、检索和分析功能,为用户带来更加流畅、快速的搜索体验,同时也能满足业务不断发展变化的需求。
二、整合前的准备工作
(一)环境搭建
在正式开启 Spring Boot 与 Elasticsearch 的整合之旅前,我们得先把本地的 Elasticsearch 和 Kibana 开发环境搭建好。别担心,借助 Docker 和 Docker Compose 这两个神器,这个过程会变得轻松又快捷。
首先,确保你的机器上已经安装了 Docker。如果还没安装,那就赶紧前往Docker 官方网站,按照对应操作系统的安装指南进行安装。安装完成后,再安装 Docker Compose。在 Linux 系统上,你可以使用以下命令进行安装(以 Ubuntu 为例):
bash
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
对于 Windows 和 Mac 系统,直接在Docker Desktop安装包中就已经包含了 Docker Compose,安装 Docker Desktop 时记得勾选相关选项就行。
接下来,我们要利用 Docker Compose 来配置和启动 Elasticsearch 与 Kibana 服务。新建一个docker-compose.yml文件,内容如下:
yaml
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata:/usr/share/elasticsearch/data
ports:
- 9200:9200
networks:
- esnet
kibana:
image: docker.elastic.co/kibana/kibana:7.17.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- 5601:5601
depends_on:
- elasticsearch
networks:
- esnet
volumes:
esdata:
driver: local
networks:
esnet:
driver: bridge
在这个配置文件里,我们定义了两个服务:elasticsearch和kibana。elasticsearch服务使用的是官方镜像docker.elastic.co/elasticsearch/elasticsearch:7.17.0,并设置了一些环境变量,比如discovery.type=single-node表示以单节点模式启动,ES_JAVA_OPTS=-Xms512m -Xmx512m用于设置 Java 堆内存大小。同时,通过volumes挂载数据卷,确保 Elasticsearch 的数据能够持久化保存。ports将容器的 9200 端口映射到宿主机的 9200 端口,这样我们就能通过http://localhost:9200访问 Elasticsearch 服务了。
kibana服务则依赖于elasticsearch服务,使用官方镜像docker.elastic.co/kibana/kibana:7.17.0,并通过ELASTICSEARCH_HOSTS=http://elasticsearch:9200环境变量指定 Elasticsearch 的地址。容器的 5601 端口映射到宿主机的 5601 端口,我们可以通过http://localhost:5601访问 Kibana 的控制台。
配置好docker-compose.yml文件后,在该文件所在目录下执行命令docker-compose up -d,Docker Compose 就会自动拉取镜像并启动这两个服务。稍等片刻,当你看到控制台没有报错信息时,就说明 Elasticsearch 和 Kibana 已经成功启动啦!打开浏览器,访问http://localhost:9200,如果能看到 Elasticsearch 的欢迎信息,访问http://localhost:5601能进入 Kibana 的登录界面,那就大功告成,本地开发环境搭建完毕!
(二)Spring Boot 项目初始化
环境搭建好后,接着就要初始化 Spring Boot 项目了。这里我们借助 Spring Initializr 来快速搭建项目,它就像是一个神奇的代码生成器,能帮我们省去很多繁琐的初始化步骤。
打开浏览器,访问Spring Initializr 官网,也可以使用国内的镜像地址start.springboot.io/,后者访问速度可能会更快一些。在页面中,我们可以看到几个关键的配置项:
-
Project:选择项目构建工具,有 Maven 和 Gradle 两种可选。Maven 使用 XML 文件来管理项目依赖和构建过程,配置相对详细但略显繁琐;Gradle 则采用基于 Groovy 或 Kotlin 的 DSL 进行配置,构建速度更快且更灵活。这里我们以 Maven 为例进行演示。
-
Language:选择开发语言,当然是选 Java 啦。
-
Spring Boot:选择 Spring Boot 版本,建议选择最新的稳定版本,以享受最新的功能和性能优化。
-
Project Metadata :填写项目的基本信息,包括
Group(项目组织名,通常是公司域名的倒写,比如com.example)、Artifact(项目唯一标识符,一般是项目名称)、Name(项目名称)、Description(项目描述)、Package name(包名,默认会根据Group和Artifact生成)、Packaging(打包方式,可选 JAR 或 WAR,一般 Web 项目选 JAR 即可)、Java(JDK 版本,确保与本地安装的 JDK 版本一致)。
配置好这些基本信息后,点击 "Add Dependencies" 按钮,添加 Spring Boot 整合 Elasticsearch 所需的依赖。在搜索框中输入 "Elasticsearch",找到 "Spring Data Elasticsearch" 依赖并添加。此外,为了方便开发,我们还可以添加一些常用的依赖,比如 "Spring Web"(用于创建 Web 应用)、"Lombok"(简化 Java 代码,自动生成 Getter、Setter、构造函数等)、"Spring Boot DevTools"(热部署,修改代码后无需重启项目即可生效,大大提高开发效率)。添加完成后,点击 "GENERATE" 按钮,Spring Initializr 会自动生成一个 Spring Boot 项目的压缩包并下载到本地。
解压下载的压缩包,用 IDE(如 IntelliJ IDEA、Eclipse 等)打开项目。此时,你会看到项目已经按照 Spring Boot 的标准结构生成好了,包括src/main/java(存放 Java 源代码)、src/main/resources(存放配置文件、静态资源等)、src/test/java(存放测试代码)等目录,以及pom.xml(Maven 项目的核心配置文件,管理项目依赖和构建信息)。打开pom.xml文件,可以看到我们在 Spring Initializr 中添加的依赖已经自动配置好了,如下所示:
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
至此,Spring Boot 项目初始化完成,我们已经为 Spring Boot 与 Elasticsearch 的整合做好了充分准备,可以正式进入整合环节,开启高效搜索功能的开发之旅啦!
三、Spring Boot 整合 Elasticsearch 实战
前面的准备工作完成后,接下来就是见证奇迹的时刻,我们要正式开启 Spring Boot 与 Elasticsearch 的整合实战,一步一步实现高效的搜索功能。这个过程就像是搭建一座高楼,每一步都至关重要,让我们一起有条不紊地进行吧!
(一)添加依赖
在pom.xml文件中添加 Spring Boot 和 Elasticsearch 整合的关键依赖,就像给机器添加必要的零件一样,少了它可不行。打开pom.xml文件,在<dependencies>标签内添加以下依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
这个spring-boot-starter-data-elasticsearch依赖可不得了,它是 Spring Boot 与 Elasticsearch 整合的桥梁,引入了 Spring Data Elasticsearch,为我们提供了操作 Elasticsearch 的各种工具和抽象层,让我们可以像操作普通 Java 对象一样轻松地与 Elasticsearch 交互,大大简化了开发过程。添加完依赖后,Maven 会自动下载相关的库文件,耐心等待一会儿,等下载完成,我们就完成了重要的一步。 同时,记得检查一下 Maven 的配置,确保它能正常下载依赖,比如确认settings.xml文件中的镜像配置是否正确,避免因为网络问题导致依赖下载失败,影响后续的开发进度。
(二)配置连接信息
添加完依赖,接着要在application.yml或application.properties文件中配置 Elasticsearch 的连接地址、用户名、密码等信息,这就好比告诉 Spring Boot 要连接到哪个 "宝藏库" 去获取数据。如果使用application.yml文件,添加如下配置:
yaml
spring:
elasticsearch:
rest:
uris: http://localhost:9200 # Elasticsearch服务地址
username: elastic # 用户名,默认是elastic
password: changeme # 密码,默认是changeme
connection-timeout: 5s # 连接超时时间
socket-timeout: 3s # socket超时时间
这里的uris指定了 Elasticsearch 服务的地址,username和password用于认证(如果 Elasticsearch 开启了安全认证),connection-timeout设置连接超时时间为 5 秒,socket-timeout设置 socket 超时时间为 3 秒。这些配置就像是一把把钥匙,确保 Spring Boot 能够顺利地与 Elasticsearch 建立连接,进行数据的交互。如果你的 Elasticsearch 服务部署在远程服务器上,记得把localhost替换为实际的服务器地址,并且确保服务器的 9200 端口是开放的,否则连接会失败哦。 另外,为了安全起见,生产环境中不要直接在配置文件中明文写密码,可以考虑使用 Spring Boot 的配置加密功能,或者将密码存储在外部的配置中心,通过环境变量等方式注入到应用中。
(三)创建 Elasticsearch 实体类
配置好连接信息后,我们要创建 Elasticsearch 实体类,它就像是 Elasticsearch 索引的 "代言人",通过它我们可以方便地操作索引中的数据。使用@Document注解定义实体类,映射到 Elasticsearch 的索引。例如,我们创建一个Product实体类,用于表示商品信息:
java
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@Document(indexName = "products")
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String description;
@Field(type = FieldType.Double)
private Double price;
}
在这个实体类中,@Document(indexName = "products")注解指定了该实体类映射到 Elasticsearch 中的products索引。@Id注解标识id字段为主键,Elasticsearch 会根据这个id来唯一标识文档。@Field注解用于定义字段的类型和相关属性,比如name和description字段类型为Text,并使用ik_max_word分词器进行中文分词,这样在搜索时能够更精准地匹配中文内容;price字段类型为Double,用于存储商品价格。通过合理定义实体类的字段和注解,我们可以让 Elasticsearch 更好地理解和处理数据,提高搜索的准确性和效率。注意,在定义字段类型时,一定要根据实际的数据类型和搜索需求来选择合适的类型,否则可能会导致数据存储或搜索出现问题。比如,如果把一个应该是数字类型的字段定义成了文本类型,那么在进行数值比较或范围查询时就会出错。
(四)创建 Repository 接口
创建完实体类,接着要创建 Repository 接口,它就像是一个 "超级助手",帮我们实现对 Elasticsearch 的各种操作。创建一个继承ElasticsearchRepository的接口,利用 Spring Data Elasticsearch 提供的方法操作 Elasticsearch,还能自定义查询方法。例如:
java
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
List<Product> findByNameContaining(String name);
List<Product> findByDescriptionContaining(String description);
}
这个ProductRepository接口继承了ElasticsearchRepository,并指定了操作的实体类为Product,主键类型为String。通过继承ElasticsearchRepository,我们可以免费获得一系列基本的 CRUD 操作方法,比如save(保存或更新文档)、findById(根据 ID 查询文档)、findAll(查询所有文档)、delete(删除文档)等。同时,我们还自定义了两个查询方法findByNameContaining和findByDescriptionContaining,用于根据商品名称和描述进行模糊查询。Spring Data Elasticsearch 会根据方法名自动生成对应的查询语句,是不是很神奇?这种约定优于配置的方式,大大减少了我们编写 SQL 语句的工作量,提高了开发效率。不过,在自定义查询方法时,要遵循 Spring Data Elasticsearch 的命名规则,否则可能无法正确生成查询语句。比如,如果方法名拼写错误,或者不符合命名规范,就会导致查询失败。
(五)控制器层实现
最后,来到控制器层实现,它就像是一个 "大门",负责接收前端的请求,并将处理结果返回给前端。创建 REST 控制器,处理搜索请求,通过调用 Repository 接口方法实现搜索功能,并返回结果给前端。例如:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping("/products/search")
public List<Product> searchProducts(@RequestParam String keyword) {
List<Product> products = productRepository.findByNameContaining(keyword);
if (products.isEmpty()) {
products = productRepository.findByDescriptionContaining(keyword);
}
return products;
}
}
在这个控制器中,@RestController注解表示这是一个 RESTful 风格的控制器,会自动将返回结果转换为 JSON 格式。@Autowired注解注入了ProductRepository,方便我们调用其中的方法。@GetMapping("/products/search")注解映射了一个 GET 请求路径/products/search,当用户访问这个路径时,会执行searchProducts方法。searchProducts方法接收一个keyword参数,用于接收前端传递的搜索关键词。在方法内部,首先尝试根据商品名称进行模糊查询,如果没有找到结果,再根据商品描述进行模糊查询,最后将查询结果返回给前端。通过这样的控制器实现,我们就完成了从前端请求到 Elasticsearch 搜索再到结果返回的整个流程,实现了高效的搜索功能。注意,在实际应用中,可能还需要对参数进行校验,对异常进行处理,以提高接口的稳定性和可靠性。比如,当用户输入的关键词为空时,应该返回合适的提示信息,而不是直接进行查询;当查询过程中出现异常时,要捕获异常并返回友好的错误信息给前端,而不是让前端看到一堆看不懂的错误堆栈信息。
四、实战案例:电商商品搜索
(一)业务场景
在电商平台中,商品搜索是用户查找心仪商品的关键入口,其重要性不言而喻。当用户在搜索框中输入关键词时,他们期待能够迅速获取到与关键词相关的商品列表,并且这些商品的展示要精准、有序,以帮助他们快速做出购买决策。因此,电商商品搜索面临着诸多复杂的业务需求。
多字段搜索是其中的核心需求之一。用户的搜索关键词可能出现在商品的多个属性字段中,比如商品名称、商品描述、品牌等。以搜索 "苹果手机" 为例,用户希望不仅能搜索到名称中包含 "苹果手机" 的商品,商品描述中提及 "苹果手机" 特性、优势的商品也应被检索出来,这样才能提供更全面的搜索结果,满足用户多样化的搜索意图。
排序功能同样不可或缺。用户往往希望搜索结果能够按照一定的规则进行排序,以便快速找到最符合自己需求的商品。常见的排序方式有按价格升序或降序排列,方便用户筛选出价格实惠或高端的商品;按销量排序,让热门畅销的商品优先展示,符合大多数用户跟风购买的心理;按评分排序,则能帮助用户快速找到质量和口碑较好的商品。
高亮显示则是提升用户体验的重要手段。当用户搜索 "智能手表" 时,在搜索结果列表中,将商品名称、描述中出现 "智能手表" 的部分以醒目的方式标记出来,比如使用不同的颜色、字体或添加下划线等,能让用户一眼定位到与搜索关键词相关的内容,快速判断商品是否符合自己的需求,大大提高了信息获取的效率。
(二)实现步骤
- 创建商品索引:使用 Elasticsearch 的 REST API 创建商品索引,这就像是为商品数据打造一个专属的 "仓库",让数据能够有条理地存储和管理。通过发送 HTTP 请求到 Elasticsearch 服务,我们可以设置索引的各种参数。例如,使用以下请求创建一个名为 "products" 的商品索引,并设置索引的映射和分片等参数:
json
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word"
},
"description": {
"type": "text",
"analyzer": "ik_max_word"
},
"price": {
"type": "double"
}
}
}
}
在这个请求中,settings部分设置了索引的分片数为 3,副本数为 1。分片可以将数据分布到多个节点上,提高查询性能和数据的可用性;副本则用于数据备份和读操作的负载均衡,确保在部分节点出现故障时,数据依然可访问,服务能够正常运行。mappings部分定义了索引中字段的类型和相关属性,name和description字段类型为text,并使用ik_max_word分词器进行中文分词,以提高中文搜索的准确性;price字段类型为double,用于存储商品价格。 注意,在设置分片和副本数时,要根据实际的数据量和服务器资源进行合理配置。如果分片数过多,会增加管理和维护的成本,降低查询性能;如果副本数过多,会占用过多的磁盘空间和网络带宽。同时,选择合适的分词器对于中文搜索至关重要,不同的分词器可能会导致搜索结果的差异。 2. 添加商品数据 :通过 Spring Boot 应用向 Elasticsearch 中添加商品数据,就像是往 "仓库" 里存放商品。在 Spring Boot 应用中,我们可以利用之前创建的ProductRepository接口来实现数据的添加。例如:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ElasticsearchDemoApplication implements CommandLineRunner {
@Autowired
private ProductRepository productRepository;
public static void main(String[] args) {
SpringApplication.run(ElasticsearchDemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Product product1 = new Product();
product1.setId("1");
product1.setName("华为 Mate 60 Pro");
product1.setDescription("搭载麒麟处理器,拥有超强影像能力,支持卫星通信。");
product1.setPrice(6999.0);
Product product2 = new Product();
product2.setId("2");
product2.setName("苹果 iPhone 15 Pro");
product2.setDescription("采用A17 Pro芯片,灵动岛交互,拍照性能卓越。");
product2.setPrice(7999.0);
productRepository.save(product1);
productRepository.save(product2);
}
}
在这段代码中,我们创建了两个Product对象,并设置了它们的属性。然后,通过调用productRepository.save方法,将这两个商品数据保存到 Elasticsearch 的 "products" 索引中。CommandLineRunner接口的run方法会在 Spring Boot 应用启动完成后自动执行,确保数据在应用启动时就被添加到 Elasticsearch 中。 注意,在添加数据时,要确保数据的准确性和完整性。如果数据存在错误或缺失,可能会影响后续的搜索结果和业务逻辑。同时,对于大量数据的添加,可以考虑使用批量操作的方式,以提高数据添加的效率,减少与 Elasticsearch 的交互次数。 3. 实现搜索与高亮 :在 Repository 中定义复杂查询方法,使用 Elasticsearch 的高亮查询功能,在搜索结果中高亮显示关键字,就像是给搜索结果中的关键信息贴上 "标签",让用户一目了然。首先,在ProductRepository接口中定义一个复杂查询方法,用于根据关键词在商品名称和描述中进行搜索:
java
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
@Query("{\"multi_match\": {\"query\": \"?0\", \"fields\": [\"name\", \"description\"]}}")
List<Product> findByQuery(String query);
}
这里使用了@Query注解,通过编写 Elasticsearch 的查询 DSL 语句,实现了多字段匹配查询。multi_match查询会在指定的name和description字段中搜索关键词。接下来,在服务层实现高亮查询功能:
java
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ProductService {
@Autowired
private ElasticsearchRestTemplate elasticsearchTemplate;
public List<SearchHit<Product>> searchProductsWithHighlight(String keyword, int page, int size) {
// 创建查询条件
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(keyword, "name", "description"))
.withHighlightFields(
new HighlightBuilder.Field("name").preTags("<em>").postTags("</em>"),
new HighlightBuilder.Field("description").preTags("<em>").postTags("</em>")
)
.withPageable(PageRequest.of(page, size))
.build();
// 执行搜索
SearchHits<Product> searchHits = elasticsearchTemplate.search(searchQuery, Product.class);
// 处理高亮结果
List<SearchHit<Product>> hits = new ArrayList<>();
for (SearchHit<Product> hit : searchHits) {
hit.getHighlightFields().forEach((field, highlight) -> {
if ("name".equals(field)) {
hit.getContent().setName(highlight.getFragments()[0].toString());
} else if ("description".equals(field)) {
hit.getContent().setDescription(highlight.getFragments()[0].toString());
}
});
hits.add(hit);
}
return hits;
}
}
在这个方法中,首先创建了一个NativeSearchQuery对象,使用QueryBuilders.multiMatchQuery进行多字段匹配查询,同时通过withHighlightFields方法设置了要高亮显示的字段,并指定了高亮标签的前缀和后缀,这里使用<em>标签来突出显示关键词。withPageable方法用于设置分页参数,实现分页查询,避免一次性返回过多数据,提高查询效率和用户体验。然后,通过elasticsearchTemplate.search方法执行搜索,并获取搜索结果。最后,对搜索结果进行处理,将高亮后的内容替换原始内容,返回带有高亮信息的搜索结果列表。 注意,在设置高亮标签时,要确保标签在前端页面中能够正确显示,并且不会与页面的其他样式产生冲突。同时,对于分页查询,要合理设置每页的大小,根据实际业务需求和数据量进行调整,以平衡查询性能和用户体验。
五、性能优化与注意事项
(一)性能优化
-
分词器选择:根据业务需求选择合适的分词器,这就像是为搜索功能挑选一把精准的 "手术刀",能极大提高搜索精度。对于中文搜索场景,中文的词语不像英文那样有空格自然分隔,使用默认的分词器往往无法准确切分中文词汇,导致搜索结果不理想。而 IK 分词器则是中文搜索的得力助手,它基于词典的正向 / 逆向最大匹配算法,结合动态加载机制,能够在准确性和性能之间取得良好平衡 。例如,在电商商品搜索中,当用户搜索 "华为手机",使用 IK 分词器的 "ik_max_word" 模式,会将 "华为手机" 切分为 "华为""手机""华为手机" 等多个词条,这样在搜索时,不仅能匹配到包含 "华为手机" 完整词汇的商品,还能匹配到仅包含 "华为" 或 "手机" 的相关商品,大大提高了搜索的召回率和准确性,让用户能够更精准地找到自己想要的商品。
-
索引优化:合理设计索引结构至关重要,避免索引字段过多或过少,就像建造房屋时要合理规划房间布局一样,这样才能提高索引的查询效率。如果索引字段过多,会增加索引的大小和维护成本,导致查询时需要处理更多的数据,降低查询速度;而索引字段过少,则可能无法满足复杂的查询需求,影响搜索结果的准确性。在设计索引结构时,要深入分析业务需求,确定哪些字段是真正需要索引的。比如在电商商品索引中,对于商品名称、描述、品牌等经常用于搜索的字段,应设置为索引字段,并且根据字段的数据类型和搜索方式,选择合适的字段类型和映射设置。对于数值类型的字段,如价格,设置为 "double" 类型,并根据业务需求设置是否开启聚合等功能;对于文本类型的字段,如商品描述,根据是否需要分词和精确匹配,选择合适的分词器和子字段设置,确保索引结构既能满足搜索需求,又能保持高效的查询性能。
-
缓存策略:结合 Redis 等缓存工具,缓存热点数据和搜索结果,这就像是在搜索功能旁边搭建了一个 "快速通道",能有效减少 Elasticsearch 的压力。Redis 作为一款高性能的内存数据库,具有快速读写、丰富的数据结构支持等特点,非常适合用于缓存。在电商场景中,对于热门商品的信息、经常搜索的关键词对应的搜索结果等,可以缓存到 Redis 中。当用户发起搜索请求时,首先从 Redis 缓存中查找,如果缓存命中,直接返回结果,无需查询 Elasticsearch,大大提高了响应速度;如果缓存未命中,再查询 Elasticsearch,并将查询结果缓存到 Redis 中,以便下次使用。通过这种方式,不仅减轻了 Elasticsearch 的负载,还能提高系统的整体性能和用户体验。同时,为了保证缓存数据的一致性,需要合理设计缓存更新机制,当 Elasticsearch 中的数据发生变化时,及时更新或删除 Redis 中的缓存数据。
(二)注意事项
-
版本兼容性:确保 Spring Boot、Elasticsearch 及其相关依赖的版本兼容,这是整合过程中不容忽视的关键环节,就像组装机器时要确保各个零件相互适配一样,否则可能会导致兼容性错误。Elasticsearch 的版本更新较为频繁,每个版本都可能带来一些新特性、功能改进或 API 变化,同时也可能引入一些不兼容的改动。Spring Boot 与 Elasticsearch 的整合依赖也需要与两者的版本相匹配,才能保证正常的功能和性能。例如,Spring Boot 2.7.x 版本与 Elasticsearch 7.17.x 系列搭配使用较为合适,如果在 Spring Boot 2.7.x 项目中使用 Elasticsearch 8.x 版本,可能会出现连接问题、API 调用失败等兼容性错误。在选择版本时,要仔细查阅官方文档和版本兼容性矩阵,确保各个组件的版本相互兼容。同时,在项目升级或引入新的依赖时,也要注意版本的一致性,避免因版本冲突导致的各种问题。
-
集群配置:在生产环境中,合理配置 Elasticsearch 集群至关重要,包括节点数量、分片和副本的设置,这关系到集群的高可用性和性能,就像搭建一座桥梁要合理设计桥墩数量和结构一样。节点数量的设置要根据数据量、并发请求数和服务器资源等因素综合考虑。如果节点数量过少,可能无法承载大量的数据和高并发的请求,导致性能瓶颈;如果节点数量过多,又会增加集群的管理成本和资源消耗。分片的设置决定了数据在节点间的分布方式,合理的分片数量可以提高查询性能和数据的可用性。一般来说,单个分片的大小建议控制在 10GB - 50GB,根据预估的数据总量和目标分片大小来计算分片数量 。副本的设置则用于数据备份和读操作的负载均衡,生产环境中至少配置 1 个副本,对于读密集型应用,可以适当增加副本数量,以提高读性能,但同时也会占用更多的磁盘空间和网络带宽。在配置集群时,还要考虑节点的硬件配置、网络拓扑等因素,确保集群能够稳定、高效地运行。