一、没有使用Elasticsearch的查询速度698ms
**1.**数据库模糊查询不走索引,在数据量较大的时候,查询性能很差。需要注意的是,数据库模糊查询随着表数据量的增多,查询性能的下降会非常明显,而搜索引擎的性能则不会随着数据增多而下降太多。目前仅10万不到的数据量差距就如此明显,如果数据量达到百万、千万、甚至上亿级别,这个性能差距会非常夸张。
**2.**功能单一
数据库的模糊搜索功能单一,匹配条件非常苛刻,必须恰好包含用户搜索的关键字。而在搜索引擎中,用户输入出现个别错字,或者用拼音搜索、同义词搜索都能正确匹配到数据。
综上,在面临海量数据的搜索,或者有一些复杂搜索需求的时候,推荐使用专门的搜索引擎来实现搜索功能。

二、 Elasticsearch使用
1.依赖
版本:因为SpringBoot默认的ES版本是7.17.10
,所以我们需要覆盖默认的ES版本:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
启动es服务---测试是否连接es成功:
java
package com.itfly;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class IndexTest {
private RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://localhost:9200")
));
}
@Test
void testConnect() {
System.out.println(client);
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}
成功连接

2、创建索引库
实现搜索功能需要的字段包括三大部分:
搜索过滤字段
分类
品牌
价格
排序字段
默认:按照更新时间降序排序
销量
价格
展示字段
商品id:用于点击后跳转
图片地址
是否是广告推广商品
名称
价格
评价数量
销量
代码分为三步:
-
1)创建Request对象。
- 因为是创建索引库的操作,因此Request是
CreateIndexRequest
。
- 因为是创建索引库的操作,因此Request是
-
2)添加请求参数
- 其实就是Json格式的Mapping映射参数。因为json字符串很长,这里是定义了静态字符串常量
MAPPING_TEMPLATE
,让代码看起来更加优雅。
- 其实就是Json格式的Mapping映射参数。因为json字符串很长,这里是定义了静态字符串常量
-
3)发送请求
client.``indices``()
方法的返回值是IndicesClient
类型,封装了所有与索引库操作有关的方法。例如创建索引、删除索引、判断索引是否存在等
java
@Test
void testCreateIndex() throws IOException {
// 1.创建Request对象
CreateIndexRequest request = new CreateIndexRequest("goods");
// 2.准备请求参数
request.source(MAPPING_TEMPLATE, XContentType.JSON);
// 3.发送请求
client.indices().create(request, RequestOptions.DEFAULT);
}
static final String MAPPING_TEMPLATE = "{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\": { \"type\": \"long\" },\n" +
" \"name\": { \"type\": \"text\", \"analyzer\": \"ik_smart\" },\n" +
" \"description\": { \"type\": \"text\", \"analyzer\": \"ik_smart\" },\n" +
" \"price\": { \"type\": \"scaled_float\", \"scaling_factor\": 100 },\n" +
" \"stock\": { \"type\": \"integer\" },\n" +
" \"status\": { \"type\": \"integer\" },\n" +
" \"image\": { \"type\": \"keyword\", \"index\": false },\n" +
" \"created_at\": { \"type\": \"date\", \"format\": \"yyyy-MM-dd HH:mm:ss\" },\n" +
" \"updated_at\": { \"type\": \"date\", \"format\": \"yyyy-MM-dd HH:mm:ss\" }\n" +
" }\n" +
" }\n" +
"}";
判断索引库是否存在
java
@Test
void testExistsIndex() throws IOException {
// 1.创建Request对象
GetIndexRequest request = new GetIndexRequest("goods");
// 2.发送请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
// 3.输出
System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}

RestClient操作文档
索引库准备好以后,就可以操作文档了。为了与索引库操作分离,我们再次创建一个测试类,做两件事情:
java
package com.itfly;
import com.itfly.service.IProductsService;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest(properties = "spring.profiles.active=local")
public class DocumentTest {
private RestHighLevelClient client;
@Autowired
private IProductsService productsService;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://localhost:9200")
));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}
新增文档
我们需要将数据库中的商品信息导入elasticsearch中,而不是造假数据了。
语法:
java
POST /{索引库名}/_doc/1
{
"name": "Jack",
"age": 21
}
可以看到与索引库操作的API非常类似,同样是三步走:
-
1)创建Request对象,这里是
IndexRequest
,因为添加文档就是创建倒排索引的过程 -
2)准备请求参数,本例中就是Json文档
-
3)发送请求
变化的地方在于,这里直接使用client.xxx()
的API,不再需要client.indices()
了。
java
@Test
void testAddDocument() throws IOException {
// 1.根据id查询商品数据,批量新增文档
productsService.list().forEach(item -> {
IndexRequest indexRequest = new IndexRequest("goods");
indexRequest.id(item.getId().toString());
indexRequest.source(JSONUtil.parseObj(item), XContentType.JSON);
try {
client.index(indexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("添加成功");
});
//
}
把所有的数据加入索引库,并创建文档
RestClient查询
第一步,创建
SearchRequest
对象,指定索引库名第二步,利用
request.source()
构建DSL,DSL中可以包含查询、分页、排序、高亮等
query()
:代表查询条件,利用QueryBuilders.matchAllQuery()
构建一个match_all
查询的DSL第三步,利用
client.search()
发送请求,得到响应
java
@Test
void testMatchAll() throws IOException {
// 1.创建Request
SearchRequest request = new SearchRequest("goods");
// 2.组织请求参数
request.source().query(QueryBuilders.matchAllQuery());
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
private void handleResponse(SearchResponse response) {
SearchHits searchHits = response.getHits();
// 1.获取总条数
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "条数据");
// 2.遍历结果数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
// 3.得到_source,也就是原始json文档
String source = hit.getSourceAsString();
// 4.反序列化并打印
ItemDoc item = JSONUtil.toBean(source, ItemDoc.class);
System.out.println(item);
}
}
查询
java
package com.itfly.controller;
import com.itfly.DTO.ProductsSearchDTO;
import com.itfly.resp.ResultData;
import com.itfly.service.IProductsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Map;
/**
* @author yyf
* description
* @date 2025/3/27 18:33
*/
@RestController
@RequestMapping("/es")
@Api(tags = "商品")
public class SearchController {
@Autowired
private IProductsService productsService;
@PostMapping("/list")
@ApiOperation(value = "查询所有商品")
public ResultData list(@RequestBody ProductsSearchDTO productsSearchDTO) {
// 创建客户端
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200))
);
try {
// 构建查询请求
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("name",productsSearchDTO.getName() ));
sourceBuilder.size(10);
searchRequest.source(sourceBuilder);
// 执行查询
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 处理结果
for (SearchHit hit : response.getHits().getHits()) {
// 获取文档内容,返回ResultData
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
return ResultData.success(sourceAsMap);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
client.close(); // 关闭客户端
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}

总结:
数据库模糊查询不走索引,在数据量较大的时候,查询性能很差。需要注意的是,数据库模糊查询随着表数据量的增多,查询性能的下降会非常明显,而搜索引擎的性能则不会随着数据增多而下降太多。目前仅10万不到的数据量差距就如此明显,如果数据量达到百万、千万、甚至上亿级别,这个性能差距会非常夸张。
其次,功能单一
数据库的模糊搜索功能单一,匹配条件非常苛刻,必须恰好包含用户搜索的关键字。而在搜索引擎中,用户输入出现个别错字,或者用拼音搜索、同义词搜索都能正确匹配到数据。
综上,在面临海量数据的搜索,或者有一些复杂搜索需求的时候,推荐使用专门的搜索引擎来实现搜索功能。