Spring Data Elasticsearch 使用指南
版本说明
本文档基于 spring-boot-starter-data-elasticsearch 2.7.18 版本
核心概念
Spring Data Elasticsearch 提供了多个接口来定义对 Elasticsearch 索引的操作:
1. 核心操作接口
1.1 IndexOperations - 索引操作接口
用于索引级别的操作,如创建、删除索引等。
@Component
public class IndexExample {
@Autowired
private ElasticsearchOperations elasticsearchOperations;
public void indexOperations() {
// 获取索引操作对象
IndexOperations indexOps = elasticsearchOperations.indexOps(Product.class);
// 创建索引
boolean created = indexOps.create();
// 创建索引并设置映射
indexOps.createWithMapping();
// 删除索引
boolean deleted = indexOps.delete();
// 判断索引是否存在
boolean exists = indexOps.exists();
// 刷新索引
indexOps.refresh();
// 设置索引映射
indexOps.putMapping();
}
}
对应的 DSL 操作:
// 创建索引
PUT /product
// 删除索引
DELETE /product
// 检查索引是否存在
HEAD /product
// 刷新索引
POST /product/_refresh
1.2 DocumentOperations - 文档操作接口
用于基于 ID 的文档存储、更新和检索操作。
@Component
public class DocumentExample {
@Autowired
private ElasticsearchOperations elasticsearchOperations;
public void documentOperations() {
Product product = new Product("1", "笔记本电脑", 5999.0);
// 保存文档
Product saved = elasticsearchOperations.save(product);
// 批量保存
List<Product> products = Arrays.asList(
new Product("2", "手机", 3999.0),
new Product("3", "平板", 2999.0)
);
Iterable<Product> savedProducts = elasticsearchOperations.save(products);
// 根据ID获取文档
Product found = elasticsearchOperations.get("1", Product.class);
// 根据ID删除文档
String deletedId = elasticsearchOperations.delete("1", Product.class);
// 删除实体
String deletedEntity = elasticsearchOperations.delete(product);
// 更新文档
UpdateQuery updateQuery = UpdateQuery.builder("1")
.withDocument(Document.create()
.append("price", 5599.0))
.build();
UpdateResponse response = elasticsearchOperations.update(updateQuery, IndexCoordinates.of("product"));
}
}
对应的 DSL 操作:
// 保存/更新文档
PUT /product/_doc/1
{
"name": "笔记本电脑",
"price": 5999.0
}
// 批量操作
POST /_bulk
{"index":{"_index":"product","_id":"2"}}
{"name":"手机","price":3999.0}
{"index":{"_index":"product","_id":"3"}}
{"name":"平板","price":2999.0}
// 获取文档
GET /product/_doc/1
// 删除文档
DELETE /product/_doc/1
// 部分更新
POST /product/_update/1
{
"doc": {
"price": 5599.0
}
}
1.3 SearchOperations - 搜索操作接口
用于搜索多个实体的操作。
@Component
public class SearchExample {
@Autowired
private ElasticsearchOperations elasticsearchOperations;
public void searchOperations() {
// 使用 CriteriaQuery 搜索
Criteria criteria = new Criteria("name").contains("电脑");
Query query = new CriteriaQuery(criteria);
SearchHits<Product> searchHits = elasticsearchOperations.search(query, Product.class);
// 分页搜索
Pageable pageable = PageRequest.of(0, 10);
query.setPageable(pageable);
SearchHits<Product> pagedHits = elasticsearchOperations.search(query, Product.class);
// 计数
long count = elasticsearchOperations.count(query, Product.class);
// 滚动搜索
query.setPageable(PageRequest.of(0, 100));
SearchScrollHits<Product> scroll = elasticsearchOperations.searchScrollStart(1000, query, Product.class, IndexCoordinates.of("product"));
String scrollId = scroll.getScrollId();
while (scroll.hasSearchHits()) {
// 处理当前批次
scroll = elasticsearchOperations.searchScrollContinue(scrollId, 1000, Product.class, IndexCoordinates.of("product"));
}
elasticsearchOperations.searchScrollClear(scrollId);
}
}
对应的 DSL 操作:
// 基本搜索
POST /product/_search
{
"query": {
"match": {
"name": "电脑"
}
}
}
// 分页搜索
POST /product/_search
{
"from": 0,
"size": 10,
"query": {
"match": {
"name": "电脑"
}
}
}
// 计数
POST /product/_count
{
"query": {
"match": {
"name": "电脑"
}
}
}
// 滚动搜索
POST /product/_search?scroll=1m
{
"size": 100,
"query": {
"match_all": {}
}
}
1.4 ElasticsearchOperations - 综合操作接口
组合了 DocumentOperations 和 SearchOperations 的功能。
@Configuration
@EnableElasticsearchRepositories
public class ElasticsearchConfig {
@Bean
public ElasticsearchOperations elasticsearchOperations(ElasticsearchClient elasticsearchClient) {
return new ElasticsearchTemplate(elasticsearchClient);
}
}
2. 查询类型详解
2.1 CriteriaQuery - 条件查询
基于条件的查询,无需了解 Elasticsearch 查询语法。
@Component
public class CriteriaQueryExample {
@Autowired
private ElasticsearchOperations operations;
public void criteriaExamples() {
// 简单查询
Criteria criteria = new Criteria("price").is(42.0);
Query query = new CriteriaQuery(criteria);
// 范围查询
criteria = new Criteria("price").greaterThan(100.0).lessThan(500.0);
// AND 条件(链式)
criteria = new Criteria("category").is("电子产品")
.and("price").lessThan(5000);
// OR 条件(子查询)
criteria = new Criteria("category").is("电子产品")
.subCriteria(
new Criteria().or("brand").is("Apple")
.or("brand").is("Samsung")
);
// 模糊查询
criteria = new Criteria("name").contains("手机");
// 前缀查询
criteria = new Criteria("name").startsWith("苹果");
// 通配符查询
criteria = new Criteria("name").expression("*手机*");
// 存在查询
criteria = new Criteria("description").exists();
// 空值查询
criteria = new Criteria("description").isNull();
// IN 查询
criteria = new Criteria("category").in("手机", "平板", "电脑");
// NOT 查询
criteria = new Criteria("category").not().is("配件");
}
}
对应的 DSL:
// 简单查询
{ "term": { "price": 42.0 } }
// 范围查询
{ "range": { "price": { "gt": 100.0, "lt": 500.0 } } }
// AND 条件
{
"bool": {
"must": [
{ "term": { "category": "电子产品" } },
{ "range": { "price": { "lt": 5000 } } }
]
}
}
// OR 条件
{
"bool": {
"must": [
{ "term": { "category": "电子产品" } }
],
"should": [
{ "term": { "brand": "Apple" } },
{ "term": { "brand": "Samsung" } }
],
"minimum_should_match": 1
}
}
2.2 StringQuery - 字符串查询
直接使用 JSON 格式的 Elasticsearch 查询。
@Component
public class StringQueryExample {
@Autowired
private ElasticsearchOperations operations;
public void stringQueryExamples() {
// Match 查询
String matchQuery = """
{
"match": {
"name": {
"query": "苹果手机",
"operator": "and"
}
}
}
""";
Query query = new StringQuery(matchQuery);
// Bool 查询
String boolQuery = """
{
"bool": {
"must": [
{ "match": { "category": "手机" } }
],
"filter": [
{ "range": { "price": { "gte": 3000, "lte": 8000 } } }
]
}
}
""";
query = new StringQuery(boolQuery);
// 聚合查询
String aggQuery = """
{
"match_all": {}
}
""";
String aggString = """
{
"categories": {
"terms": {
"field": "category.keyword",
"size": 10
}
}
}
""";
query = new StringQuery(aggQuery);
// 注意:聚合需要使用 NativeQuery
}
}
2.3 NativeQuery - 原生查询
用于复杂查询,支持所有 Elasticsearch 功能。
@Component
public class NativeQueryExample {
@Autowired
private ElasticsearchOperations operations;
public void nativeQueryExamples() {
// 基本查询
Query query = NativeQuery.builder()
.withQuery(q -> q
.match(m -> m
.field("name")
.query("手机")
)
)
.build();
// 复合查询
query = NativeQuery.builder()
.withQuery(q -> q
.bool(b -> b
.must(must -> must
.match(m -> m.field("category").query("手机"))
)
.filter(f -> f
.range(r -> r
.field("price")
.gte(JsonData.of(3000))
.lte(JsonData.of(8000))
)
)
)
)
.build();
// 带聚合的查询
query = NativeQuery.builder()
.withAggregation("avgPrice", Aggregation.of(a -> a
.avg(avg -> avg.field("price"))
))
.withAggregation("categories", Aggregation.of(a -> a
.terms(t -> t
.field("category.keyword")
.size(10)
)
))
.build();
// 高亮查询
query = NativeQuery.builder()
.withQuery(q -> q
.match(m -> m.field("name").query("手机"))
)
.withHighlightQuery(new HighlightQuery(
new Highlight(List.of(new HighlightField("name"))),
null
))
.build();
// 排序和分页
query = NativeQuery.builder()
.withQuery(q -> q.matchAll(m -> m))
.withSort(Sort.by(Sort.Direction.DESC, "price"))
.withPageable(PageRequest.of(0, 20))
.build();
// 指定返回字段
query = NativeQuery.builder()
.withQuery(q -> q.matchAll(m -> m))
.withSourceFilter(new FetchSourceFilter(
new String[]{"name", "price"},
new String[]{}
))
.build();
}
}
对应的 DSL:
// 复合查询
{
"query": {
"bool": {
"must": [
{ "match": { "category": "手机" } }
],
"filter": [
{ "range": { "price": { "gte": 3000, "lte": 8000 } } }
]
}
}
}
// 聚合查询
{
"aggs": {
"avgPrice": {
"avg": { "field": "price" }
},
"categories": {
"terms": {
"field": "category.keyword",
"size": 10
}
}
}
}
// 高亮查询
{
"query": {
"match": { "name": "手机" }
},
"highlight": {
"fields": {
"name": {}
}
}
}
3. 搜索结果类型
3.1 SearchHit
包含单个搜索结果的详细信息。
public void handleSearchHit() {
SearchHits<Product> searchHits = operations.search(query, Product.class);
for (SearchHit<Product> hit : searchHits) {
// 获取文档 ID
String id = hit.getId();
// 获取得分
float score = hit.getScore();
// 获取排序值
List<Object> sortValues = hit.getSortValues();
// 获取高亮字段
Map<String, List<String>> highlightFields = hit.getHighlightFields();
// 获取实体对象
Product product = hit.getContent();
}
}
3.2 SearchHits
包含整个搜索结果的信息。
public void handleSearchHits() {
SearchHits<Product> searchHits = operations.search(query, Product.class);
// 总命中数
long totalHits = searchHits.getTotalHits();
// 总命中数关系(EQUAL_TO, GREATER_THAN_OR_EQUAL_TO)
TotalHitsRelation relation = searchHits.getTotalHitsRelation();
// 最高得分
float maxScore = searchHits.getMaxScore();
// 获取聚合结果
Aggregations aggregations = searchHits.getAggregations();
// 获取建议结果
Suggest suggest = searchHits.getSuggest();
}
3.3 SearchPage
支持 Spring Data 分页的搜索结果。
public void handleSearchPage() {
Pageable pageable = PageRequest.of(0, 10);
Query query = new CriteriaQuery(criteria).setPageable(pageable);
SearchHits<Product> searchHits = operations.search(query, Product.class);
SearchPage<Product> page = SearchHitSupport.searchPageFor(searchHits, pageable);
// 分页信息
int totalPages = page.getTotalPages();
long totalElements = page.getTotalElements();
int currentPage = page.getNumber();
int pageSize = page.getSize();
// 获取内容
List<Product> content = page.getContent();
}
4. 实体映射注解
@Document(indexName = "product")
@Setting(replicas = 1, shards = 3)
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Keyword)
private String category;
@Field(type = FieldType.Double)
private Double price;
@Field(type = FieldType.Date, format = DateFormat.date_time)
private Date createTime;
@Field(type = FieldType.Nested)
private List<Tag> tags;
@CompletionField(maxInputLength = 100)
private Completion suggest;
// getter/setter
}
5. Repository 支持
@Repository
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
// 方法名查询
List<Product> findByName(String name);
List<Product> findByPriceBetween(Double min, Double max);
List<Product> findByCategoryAndPriceLessThan(String category, Double price);
// @Query 注解查询
@Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
List<Product> searchByName(String name);
// 高亮查询
@Highlight(fields = {
@HighlightField(name = "name"),
@HighlightField(name = "description")
})
List<SearchHit<Product>> findByNameContaining(String keyword);
}
6. 配置示例
# application.yml
spring:
elasticsearch:
uris: http://localhost:9200
username: elastic
password: password
connection-timeout: 5s
socket-timeout: 30s
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.example.repository")
public class ElasticsearchConfig extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder()
.connectedTo("localhost:9200")
.withBasicAuth("elastic", "password")
.withConnectTimeout(Duration.ofSeconds(5))
.withSocketTimeout(Duration.ofSeconds(30))
.build();
}
}
7. 实际应用示例
7.1 商品搜索服务
@Service
public class ProductSearchService {
@Autowired
private ElasticsearchOperations operations;
public Page<Product> searchProducts(String keyword, Double minPrice, Double maxPrice,
String category, Pageable pageable) {
// 构建查询条件
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (StringUtils.hasText(keyword)) {
boolQuery.must(QueryBuilders.multiMatchQuery(keyword, "name", "description")
.type(MultiMatchQueryBuilder.Type.BEST_FIELDS));
}
if (minPrice != null || maxPrice != null) {
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
if (minPrice != null) rangeQuery.gte(minPrice);
if (maxPrice != null) rangeQuery.lte(maxPrice);
boolQuery.filter(rangeQuery);
}
if (StringUtils.hasText(category)) {
boolQuery.filter(QueryBuilders.termQuery("category.keyword", category));
}
// 构建原生查询
NativeQuery query = NativeQuery.builder()
.withQuery(q -> q._toQuery(boolQuery))
.withPageable(pageable)
.withSort(Sort.by("_score").descending()
.and(Sort.by("createTime").descending()))
.build();
SearchHits<Product> searchHits = operations.search(query, Product.class);
return SearchHitSupport.searchPageFor(searchHits, pageable);
}
public Map<String, Long> getCategoryAggregation() {
NativeQuery query = NativeQuery.builder()
.withQuery(q -> q.matchAll(m -> m))
.withAggregation("categories",
Aggregation.of(a -> a
.terms(t -> t
.field("category.keyword")
.size(50)
)
)
)
.build();
SearchHits<Product> searchHits = operations.search(query, Product.class);
Map<String, Long> categoryCount = new HashMap<>();
if (searchHits.hasAggregations()) {
Aggregations aggregations = searchHits.getAggregations();
List<StringTermsBucket> buckets = aggregations.get("categories")
.sterms().buckets().array();
for (StringTermsBucket bucket : buckets) {
categoryCount.put(bucket.key()._toJsonString(), bucket.docCount());
}
}
return categoryCount;
}
}
7.2 日志分析服务
@Service
public class LogAnalysisService {
@Autowired
private ElasticsearchOperations operations;
public List<LogEntry> searchLogs(String level, String message,
LocalDateTime startTime, LocalDateTime endTime) {
Criteria criteria = new Criteria();
if (StringUtils.hasText(level)) {
criteria = criteria.and("level").is(level);
}
if (StringUtils.hasText(message)) {
criteria = criteria.and("message").contains(message);
}
if (startTime != null || endTime != null) {
Criteria timeCriteria = new Criteria("timestamp");
if (startTime != null) {
timeCriteria = timeCriteria.greaterThanEqual(startTime);
}
if (endTime != null) {
timeCriteria = timeCriteria.lessThanEqual(endTime);
}
criteria = criteria.and(timeCriteria);
}
Query query = new CriteriaQuery(criteria)
.addSort(Sort.by(Sort.Direction.DESC, "timestamp"));
SearchHits<LogEntry> hits = operations.search(query, LogEntry.class);
return hits.stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
}
}
8. 最佳实践
-
索引设计
- 合理设置分片和副本数
- 选择合适的分词器
- 避免过度嵌套
-
查询优化
- 使用 filter 替代 query(filter 可缓存)
- 合理使用分页,避免深度分页
- 使用 source filtering 减少传输数据
-
批量操作
- 使用 bulk API 进行批量写入
- 控制批量大小(通常 1000-5000 个文档)
-
错误处理
- 捕获并处理 ElasticsearchException
- 实现重试机制
- 记录详细日志
-
性能监控
- 监控查询响应时间
- 监控索引大小和文档数
- 定期优化索引
9. 常见问题
-
版本兼容性
- Spring Boot 2.7.x 对应 Elasticsearch 7.17.x
- 注意客户端版本与服务端版本匹配
-
中文分词
- 安装 IK 分词器
- 配置自定义词典
-
深度分页问题
- 使用 scroll API 或 search_after
- 避免使用大的 from 值
-
聚合查询限制
- text 字段不能直接聚合
- 使用 keyword 类型或 fielddata
10. 总结
Spring Data Elasticsearch 提供了多种方式来操作 Elasticsearch:
- CriteriaQuery:适合简单查询,易于理解
- StringQuery:适合已有查询 DSL 的场景
- NativeQuery:适合复杂查询,功能最全面
- Repository:适合简单的 CRUD 操作
选择合适的查询方式,结合实际业务需求,可以高效地实现各种搜索功能。
11. Spring Data Elasticsearch 类架构详解
11.1 核心接口层
Operations 接口家族
1. IndexOperations - 索引操作接口
- 作用:管理 Elasticsearch 索引的生命周期
- 主要方法:
create()- 创建索引 → ES:PUT /index_namecreateWithMapping()- 创建索引并设置映射 → ES:PUT /index_namewith mappingsdelete()- 删除索引 → ES:DELETE /index_nameexists()- 检查索引是否存在 → ES:HEAD /index_namerefresh()- 刷新索引 → ES:POST /index_name/_refreshputMapping()- 更新映射 → ES:PUT /index_name/_mappinggetSettings()- 获取索引设置 → ES:GET /index_name/_settings
// 使用示例
IndexOperations indexOps = elasticsearchOperations.indexOps(Product.class);
if (!indexOps.exists()) {
indexOps.createWithMapping();
}
2. DocumentOperations - 文档操作接口
- 作用:处理单个或批量文档的 CRUD 操作
- 主要方法:
save(T entity)- 保存单个文档 → ES:PUT /index/_doc/{id}save(Iterable<T> entities)- 批量保存 → ES:POST /_bulkget(String id, Class<T>)- 根据 ID 获取 → ES:GET /index/_doc/{id}exists(String id, Class<T>)- 检查文档存在 → ES:HEAD /index/_doc/{id}delete(String id, Class<T>)- 删除文档 → ES:DELETE /index/_doc/{id}update(UpdateQuery)- 部分更新 → ES:POST /index/_update/{id}
// 使用示例
Product product = new Product("1", "手机", 3999.0);
Product saved = documentOperations.save(product);
Product retrieved = documentOperations.get("1", Product.class);
3. SearchOperations - 搜索操作接口
- 作用:执行各种搜索查询和聚合操作
- 主要方法:
count(Query)- 统计文档数 → ES:POST /index/_countsearch(Query)- 执行搜索 → ES:POST /index/_searchsearchScrollStart()- 开始滚动搜索 → ES:POST /index/_search?scroll=1msearchScrollContinue()- 继续滚动 → ES:POST /_search/scrollmultiSearch()- 多重搜索 → ES:POST /_msearch
// 使用示例
Query query = new CriteriaQuery(new Criteria("category").is("手机"));
SearchHits<Product> hits = searchOperations.search(query, Product.class);
4. ElasticsearchOperations - 综合操作接口
- 作用:整合所有操作接口,提供统一的访问入口
- 继承关系:继承自 DocumentOperations 和 SearchOperations
- 额外功能:
indexOps()- 获取 IndexOperations 实例getElasticsearchConverter()- 获取转换器
11.2 实现类层
1. ElasticsearchTemplate
- 作用:使用新版 Elasticsearch Java API Client 的实现
- 特点:
- 基于
co.elastic.clients.elasticsearch.ElasticsearchClient - 支持 Elasticsearch 7.17+ 和 8.x
- 提供类型安全的 API
- 推荐在新项目中使用
- 基于
@Configuration
public class ElasticsearchConfig {
@Bean
public ElasticsearchOperations elasticsearchOperations(ElasticsearchClient client) {
return new ElasticsearchTemplate(client);
}
}
2. ElasticsearchRestTemplate(已废弃)
- 作用:使用旧版 High Level REST Client 的实现
- 特点:
- 基于
RestHighLevelClient - 在 Spring Data Elasticsearch 5.x 中已标记为废弃
- 建议迁移到 ElasticsearchTemplate
- 基于
11.3 查询类体系
1. Query 接口
- 作用:所有查询类的基础接口
- 通用功能:
- 设置分页(Pageable)
- 设置返回字段(Source Filter)
- 设置查询选项(Track Total Hits 等)
2. CriteriaQuery
- 作用:基于条件的查询,适合不熟悉 ES 语法的开发者
- 特点:
- 链式 API 设计
- 自动转换为 ES 查询 DSL
- 支持复杂的 AND/OR 组合
// 复杂查询示例
Criteria criteria = new Criteria("category").is("手机")
.and("price").between(3000, 8000)
.and(new Criteria("brand").in("Apple", "Samsung")
.or("features").contains("5G"));
3. StringQuery
- 作用:直接使用 JSON 字符串查询
- 使用场景:
- 已有现成的查询 DSL
- 从 Kibana 复制的查询
- 动态构建的查询字符串
String queryJson = """
{
"bool": {
"must": [
{"match": {"name": "手机"}},
{"range": {"price": {"gte": 3000}}}
]
}
}
""";
Query query = new StringQuery(queryJson);
4. NativeQuery
- 作用:使用 Elasticsearch Java API 构建的原生查询
- 优势:
- 支持所有 ES 功能
- 类型安全
- IDE 自动补全
- 支持复杂聚合
NativeQuery query = NativeQuery.builder()
.withQuery(q -> q
.bool(b -> b
.must(m -> m.match(mt -> mt.field("name").query("手机")))
.filter(f -> f.range(r -> r.field("price").gte(JsonData.of(3000))))
)
)
.withAggregation("avgPrice", a -> a
.avg(avg -> avg.field("price"))
)
.build();
5. Criteria 类
- 作用:构建查询条件的核心类
- 支持的操作:
- 比较操作:
is,equals,not - 范围操作:
greaterThan,lessThan,between - 文本操作:
contains,startsWith,endsWith - 集合操作:
in,notIn - 存在性:
exists,isNull - 逻辑组合:
and,or,subCriteria
- 比较操作:
11.4 结果类体系
1. SearchHit
- 作用:封装单个搜索结果
- 包含信息:
- 文档 ID
- 相关性得分(score)
- 排序值(sortValues)
- 高亮结果(highlightFields)
- 内部命中(innerHits)
- 实体内容(content)
2. SearchHits
- 作用:封装整个搜索响应
- 包含信息:
- 总命中数和关系类型
- 最高得分
- SearchHit 列表
- 聚合结果
- 建议结果
- 滚动 ID(如果是滚动查询)
3. SearchPage
- 作用:支持 Spring Data 分页的搜索结果
- 实现:
Page<T>接口 - 用途:与 Spring Data 的分页机制无缝集成
11.5 Repository 层
1. ElasticsearchRepository<T, ID>
- 作用:提供基础的 CRUD 和搜索功能
- 继承自:Spring Data 的
CrudRepository和PagingAndSortingRepository - 额外方法:
search(QueryBuilder)- 使用查询构建器搜索search(Query)- 使用 Query 对象搜索searchSimilar()- 相似文档搜索
2. SimpleElasticsearchRepository
- 作用:ElasticsearchRepository 的默认实现
- 内部使用:ElasticsearchOperations 完成实际操作
@Repository
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
// 方法名查询 - 自动生成查询
List<Product> findByNameContainingAndPriceBetween(String name, Double minPrice, Double maxPrice);
// 自定义查询
@Query("{\"bool\": {\"must\": [{\"match\": {\"name\": \"?0\"}}]}}")
Page<Product> searchByName(String name, Pageable pageable);
}
11.6 配置类
1. ElasticsearchConfiguration
- 作用:配置基类,简化配置过程
- 提供:
- 客户端配置方法
- Bean 定义模板
- 转换器配置
2. ClientConfiguration
- 作用:配置 Elasticsearch 客户端连接
- 配置项:
- 连接地址和端口
- 认证信息
- 超时设置
- SSL/TLS 配置
- 自定义请求头
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200", "localhost:9201") // 集群地址
.usingSsl() // 启用 SSL
.withBasicAuth("elastic", "password") // 基础认证
.withConnectTimeout(Duration.ofSeconds(5)) // 连接超时
.withSocketTimeout(Duration.ofSeconds(30)) // 套接字超时
.withHeaders(() -> { // 自定义头
HttpHeaders headers = new HttpHeaders();
headers.add("X-Custom-Header", "value");
return headers;
})
.build();
11.7 注解体系
1. @Document
- 作用:标记实体类映射到 ES 索引
- 属性:
indexName- 索引名称createIndex- 是否自动创建索引versionType- 版本控制类型
2. @Field
- 作用:配置字段映射
- 属性:
type- 字段类型(text, keyword, long 等)analyzer- 分析器format- 日期格式fielddata- 是否启用 fielddata
3. @Id
- 作用:标记文档 ID 字段
11.8 类之间的协作关系
-
客户端创建流程:
ElasticsearchConfiguration → ClientConfiguration → ElasticsearchClient → ElasticsearchTemplate -
查询执行流程:
Repository方法 → Query对象 → ElasticsearchOperations → ElasticsearchClient → Elasticsearch -
结果处理流程:
Elasticsearch响应 → SearchHits → SearchHit → Entity对象 -
索引管理流程:
ElasticsearchOperations.indexOps() → IndexOperations → 索引操作
这个类体系设计遵循了单一职责原则,每个接口和类都有明确的职责,通过组合和继承实现了功能的复用和扩展。
12. 执行流程详解
12.1 查询执行的完整流程
查询从客户端发起到返回结果,经历了以下几个关键步骤:
- 方法调用:客户端通过 Repository 方法或直接使用 ElasticsearchOperations
- 查询构建:根据选择的查询类型(CriteriaQuery、StringQuery、NativeQuery)构建查询
- 查询转换:ElasticsearchConverter 将查询对象转换为 Elasticsearch DSL
- 请求发送:通过 ElasticsearchClient 发送 HTTP 请求到 Elasticsearch
- 结果处理:将 ES 响应转换为 SearchHits 和实体对象
12.2 不同查询类型的执行路径
CriteriaQuery 路径
- 优点:易于理解,不需要了解 ES 语法
- 缺点:功能有限,复杂查询难以表达
- 适用场景:简单的条件查询、快速开发
// 执行路径示例
Criteria criteria = new Criteria("name").contains("手机");
// Criteria → CriteriaQueryProcessor → ES Query DSL
StringQuery 路径
- 优点:灵活,可以使用任何 ES 查询
- 缺点:容易出错,没有编译时检查
- 适用场景:已有查询 DSL、动态查询
// 执行路径示例
String json = "{\"match\": {\"name\": \"手机\"}}";
// JSON String → 直接传递 → ES Query DSL
NativeQuery 路径
- 优点:类型安全,功能完整,IDE 支持
- 缺点:需要了解 ES Java API
- 适用场景:复杂查询、聚合分析
// 执行路径示例
NativeQuery query = NativeQuery.builder()
.withQuery(q -> q.match(m -> m.field("name").query("手机")))
.build();
// QueryBuilder → ElasticsearchConverter → ES Query DSL
12.3 性能优化建议
-
查询优化
- 使用 filter 代替 query(filter 可缓存)
- 合理设置返回字段(source filtering)
- 避免深度分页(使用 scroll 或 search_after)
-
批量操作
- 使用 bulk API 进行批量写入
- 控制批次大小(1000-5000 文档)
- 异步处理大量数据
-
连接池配置
ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo("localhost:9200") .withConnectTimeout(Duration.ofSeconds(5)) .withSocketTimeout(Duration.ofSeconds(30)) .withConnectionRequestTimeout(Duration.ofSeconds(10)) .build(); -
缓存策略
- 对不常变化的数据启用查询缓存
- 使用 Redis 缓存热点数据
- 实现本地缓存减少 ES 压力
12.4 常见问题排查
-
版本不兼容
Spring Boot 2.7.x → Spring Data Elasticsearch 4.4.x → Elasticsearch 7.17.x Spring Boot 3.0.x → Spring Data Elasticsearch 5.0.x → Elasticsearch 8.x -
字段映射错误
- 检查 @Field 注解配置
- 确认分析器是否正确
- 验证字段类型匹配
-
查询无结果
- 检查索引名称
- 验证查询条件
- 查看 ES 日志
-
性能问题
- 监控查询耗时
- 优化索引结构
- 调整 JVM 参数
通过理解这个完整的类体系和执行流程,您可以更好地使用 Spring Data Elasticsearch 构建高效的搜索应用。
核心类图
查询执行完整流程
不同查询类型执行路径-流程图
评论区