Skip to main content

搜索引擎组件(Search)

:::success 版本提示

该功能自4.5.0版本开始可用 :::

概述​

Rydeen Search Component 是一个现代化的搜索引擎组件,采用模块化架构设计,支持多种搜索引擎。该组件遵循 Spring Data 最新版本的最佳实践,为每种搜索引擎提供独立的、优化的接口实现。

主要特性​

  • 🏗️ 模块化架构:每种搜索引擎独立实现,互不干扰
  • 🚀 现代化设计:基于最新版本的 Spring Data 技术栈
  • 🔍 多种查询模式:支持 CriteriaQuery、StringQuery、NativeQuery
  • 📝 注解驱动:支持 @Query 注解,类似 JPA 的查询方式
  • 🎯 类型安全:提供 Lambda 表达式支持,编译时类型检查
  • ⚡ 性能优化:针对每种搜索引擎的特性进行优化
  • 🔧 灵活配置:支持自定义查询条件对象和链式查询构建

Elasticsearch 模块​

概述​

Elasticsearch 模块是当前最成熟的实现,基于 Spring Data Elasticsearch 5.5.2,提供完整的搜索功能支持。

快速开始​

依赖配置​

<dependency>
<groupId>cn.com.rydeen.component</groupId>
<artifactId>rydeen-component-search</artifactId>
</dependency>

基础配置​

# 基础配置
platform:
component:
search:

# 服务地址(支持单节点和多节点)
hosts: http://localhost:9200
# 多节点示例:http://es1:9200,http://es2:9200,http://es3:9200

# 认证信息(如果启用安全认证)
username: elastic
password: your_password

# 连接超时配置
connect-timeout: 5000 # 连接超时:5秒
socket-timeout: 60000 # 读取超时:60秒

# 连接池配置
pool:
max-total: 100 # 最大总连接数
max-per-route: 100 # 单节点最大连接数
connect-timeout: 5000 # 连接超时
socket-timeout: 60000 # 读取超时
connection-request-timeout: 5000 # 获取连接超时
keep-alive-time: 20000 # 连接保活时间
idle-connection-timeout: 30000 # 空闲连接超时
validate-after-inactivity: true # 连接有效性验证
enable-metrics: false # 连接池统计

# SSL安全配置
ssl:
enabled: false # 是否启用HTTPS
protocol: TLSv1.2 # SSL协议版本
keystore-path: /path/to/keystore.p12
keystore-password: keystore_password
keystore-type: PKCS12 # 证书类型:JKS, PKCS12, PEM
truststore-path: /path/to/truststore.p12
truststore-password: truststore_password
trust-all: false # 是否信任所有证书

# Elasticsearch特有配置
elasticsearch:
shards: 1 # 默认分片数
replicas: 1 # 默认副本数
refresh-interval: 1s # 刷新间隔
cluster:
health-check: true # 启用健康检查
health-check-timeout: 30000 # 健康检查超时
name: elasticsearch # 集群名称
minimum-master-nodes: 1 # 最小主节点数

# 生产环境配置示例
platform:
component:
search:
provider: ELASTICSEARCH
hosts: https://es-cluster.example.com:9200
username: ${ES_USERNAME:elastic}
password: ${ES_PASSWORD:}
connect-timeout: 3000
socket-timeout: 30000

pool:
max-total: 200
max-per-route: 100
connect-timeout: 3000
socket-timeout: 30000
connection-request-timeout: 3000
keep-alive-time: 60000
idle-connection-timeout: 60000
validate-after-inactivity: true
enable-metrics: true

ssl:
enabled: true
protocol: TLSv1.3
keystore-path: ${ES_KEYSTORE_PATH:/etc/elasticsearch/certs/client.p12}
keystore-password: ${ES_KEYSTORE_PASSWORD:}
keystore-type: PKCS12
truststore-path: ${ES_TRUSTSTORE_PATH:/etc/elasticsearch/certs/ca.p12}
truststore-password: ${ES_TRUSTSTORE_PASSWORD:}
trust-all: false

elasticsearch:
shards: 3
replicas: 2
refresh-interval: 30s
cluster:
health-check: true
health-check-timeout: 60000
name: production-cluster
minimum-master-nodes: 2

配置说明​

配置优先级​
  • 环境变量 > 配置文件 > 默认值
  • 敏感信息(密码、证书)建议使用环境变量
连接池调优建议​
  • 低并发应用:max-total: 50-100, max-per-route: 50-100
  • 中等并发应用:max-total: 100-150, max-per-route: 100-150
  • 高并发应用:max-total: 150-200, max-per-route: 100-150
超时时间配置​
  • 内网环境:connect-timeout: 3000-5000ms, socket-timeout: 30000-60000ms
  • 公网环境:connect-timeout: 5000-8000ms, socket-timeout: 60000-120000ms
  • 网络较差:connect-timeout: 8000-10000ms, socket-timeout: 120000ms以上
SSL安全配置​
  • 生产环境:必须启用SSL,使用TLSv1.2或TLSv1.3
  • 开发环境:可以禁用SSL或使用自签名证书
  • 证书管理:建议使用PKCS12格式,便于部署和管理

基本使用​

@Service
@RequiredArgsConstructor
public class OrderService {

private final ElasticsearchService elasticsearchService;

public PageResult<Order> searchOrders(String keyword, int page, int size) {
SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.page(page, size);

if (StringUtils.hasText(keyword)) {
wrapper.like(Order::getOrderName, keyword);
}

return elasticsearchService.search(wrapper);
}
}

核心组件​

SearchQueryWrapper - 通用查询包装器​

SearchQueryWrapper 是主要的查询构建器,支持链式调用和多种查询操作。

基础查询示例​
// 简单条件查询
SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.eq(Order::getStatus, "ACTIVE")
.ge(Order::getAmount, 100.0)
.orderByDesc(Order::getCreateTime)
.page(0, 10);

PageResult<Order> result = elasticsearchService.search(wrapper);
复杂条件查询​
// 多条件组合查询
SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.eq(Order::getStatus, "ACTIVE")
.between(Order::getCreateTime, startDate, endDate)
.in(Order::getCategory, Arrays.asList("ELECTRONICS", "CLOTHING"))
.like(Order::getDescription, "重要")
.orderByDesc(Order::getCreateTime)
.page(0, 20);

PageResult<Order> result = elasticsearchService.search(wrapper);
嵌套字段查询​
// 查询嵌套数组中的字段
SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.nestedLike("orderItems", "productName", "iPhone")
.eq(Order::getStatus, "ACTIVE")
.page(0, 10);

PageResult<Order> result = elasticsearchService.search(wrapper);

StringQueryWrapper - JSON 字符串查询​

StringQueryWrapper 用于直接使用 Elasticsearch JSON 查询字符串,适合复杂查询场景。

基础使用​
StringQueryWrapper<Order> wrapper = StringQueryWrapper
.create(Order.class, "orders")
.page(page, size)
.stringQuery("""
{
"nested": {
"path": "orderItems",
"query": {
"match": {
"orderItems.productName": "iPhone"
}
}
}
}
""");

PageResult<Order> result = elasticsearchService.search(wrapper);
复杂聚合查询​
StringQueryWrapper<Order> wrapper = StringQueryWrapper
.create(Order.class, "orders")
.page(0, 0) // 聚合查询不需要分页
.stringQuery("""
{
"query": {
"bool": {
"must": [
{"term": {"status": "ACTIVE"}},
{"range": {"amount": {"gte": 100}}}
]
}
},
"aggs": {
"category_stats": {
"terms": {"field": "category"},
"aggs": {
"total_amount": {"sum": {"field": "amount"}}
}
}
}
}
""");

PageResult<Order> result = elasticsearchService.search(wrapper);

NativeQueryWrapper - 原生查询包装器​

NativeQueryWrapper 使用 Spring Data Elasticsearch 的 NativeQuery,提供类型安全的查询构建。

基础使用​
NativeQueryWrapper<Order> wrapper = NativeQueryWrapper
.create(Order.class, "orders")
.page(page, size)
.nativeQuery(q -> q
.bool(b -> b
.must(m -> m.term(t -> t.field("status").value("ACTIVE")))
.must(m -> m.range(r -> r.field("amount").gte(gte -> gte.value(100.0))))
);

PageResult<Order> result = elasticsearchService.search(wrapper);
嵌套查询​
NativeQueryWrapper<Order> wrapper = NativeQueryWrapper
.create(Order.class, "orders")
.page(page, size)
.nativeQuery(q -> q
.nested(n -> n
.path("orderItems")
.query(nq -> nq
.match(m -> m
.field("orderItems.productName")
.query("iPhone")
)
)
)
);

PageResult<Order> result = elasticsearchService.search(wrapper);

查询操作符​

基础比较操作符​

操作符方法描述示例
等于eq(column, value)精确匹配.eq(Order::getStatus, "ACTIVE")
不等于ne(column, value)不匹配.ne(Order::getStatus, "DELETED")
大于gt(column, value)大于指定值.gt(Order::getAmount, 100.0)
大于等于ge(column, value)大于等于指定值.ge(Order::getAmount, 100.0)
小于lt(column, value)小于指定值.lt(Order::getAmount, 1000.0)
小于等于le(column, value)小于等于指定值.le(Order::getAmount, 1000.0)
区间between(column, from, to)在指定范围内.between(Order::getCreateTime, start, end)

集合操作符​

操作符方法描述示例
包含in(column, values)在指定集合中.in(Order::getCategory, Arrays.asList("A", "B"))
不包含notIn(column, values)不在指定集合中.notIn(Order::getStatus, Arrays.asList("DELETED"))
文本操作符​
操作符方法描述示例
模糊匹配like(column, value)包含指定字符串.like(Order::getDescription, "重要")
存在exists(column)字段存在.exists(Order::getTags)
不存在notExists(column)字段不存在.notExists(Order::getDeletedAt)

高级功能​

高亮查询​

SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.like(Order::getDescription, "重要")
.highlight(Order::getDescription, Order::getOrderName) // 设置高亮字段
.page(0, 10);

PageResult<Order> result = elasticsearchService.search(wrapper);

// 获取高亮结果
Map<String, Map<String, List<String>>> highlights = result.getHighlights();

搜索建议​

SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.suggest(Order::getOrderName, "iPh", 5) // 字段名,前缀,建议数量
.page(0, 10);

PageResult<Order> result = elasticsearchService.search(wrapper);

// 获取建议结果
Map<String, List<String>> suggestions = result.getSuggestions();

聚合查询​

// 使用 dslSearch 进行聚合查询(推荐)
StringQueryWrapper<Order> wrapper = StringQueryWrapper
.create(Order.class, "orders")
.page(0, 0) // 聚合查询不需要分页
.stringQuery("""
{
"query": {
"term": {"status": "ACTIVE"}
},
"aggs": {
"category_stats": {
"terms": {"field": "category"},
"aggs": {
"total_amount": {"sum": {"field": "amount"}},
"avg_amount": {"avg": {"field": "amount"}}
}
},
"amount_stats": {
"stats": {"field": "amount"}
},
"status_distribution": {
"terms": {"field": "status"}
}
}
}
""");

PageResult<Order> result = elasticsearchService.dslSearch(wrapper);

// 获取聚合结果
Map<String, Object> aggregations = result.getAggregations();

复杂聚合查询示例​

// 多层级聚合查询
StringQueryWrapper<Order> wrapper = StringQueryWrapper
.create(Order.class, "orders")
.page(0, 0)
.stringQuery("""
{
"query": {
"bool": {
"must": [
{"range": {"createTime": {"gte": "2024-01-01"}}},
{"range": {"amount": {"gte": 100}}}
]
}
},
"aggs": {
"monthly_stats": {
"date_histogram": {
"field": "createTime",
"calendar_interval": "month",
"format": "yyyy-MM"
},
"aggs": {
"total_amount": {"sum": {"field": "amount"}},
"order_count": {"value_count": {"field": "id"}},
"category_breakdown": {
"terms": {"field": "category"},
"aggs": {
"category_amount": {"sum": {"field": "amount"}}
}
}
}
},
"top_products": {
"nested": {"path": "orderItems"},
"aggs": {
"product_stats": {
"terms": {"field": "orderItems.productName", "size": 10},
"aggs": {
"total_quantity": {"sum": {"field": "orderItems.quantity"}},
"total_revenue": {"sum": {"field": "orderItems.subtotal"}}
}
}
}
}
}
}
""");

PageResult<Order> result = elasticsearchService.dslSearch(wrapper);

使用 NativeQueryWrapper 进行聚合查询​

// 使用 NativeQueryWrapper 进行类型安全的聚合查询
NativeQueryWrapper<Order> wrapper = NativeQueryWrapper
.create(Order.class, "orders")
.page(0, 0)
.nativeQuery(q -> q
.query(qb -> qb
.term(t -> t.field("status").value("ACTIVE"))
)
.aggregations("category_stats", a -> a
.terms(t -> t.field("category"))
.aggregations("total_amount", sa -> sa
.sum(s -> s.field("amount"))
)
)
.aggregations("amount_stats", a -> a
.stats(s -> s.field("amount"))
)
);

PageResult<Order> result = elasticsearchService.nativeSearch(wrapper);

注解驱动查询​

根据Spring Data Elasticsearch Repository官方文档,Spring Data Elasticsearch提供了强大的Repository抽象,支持多种查询方式。

1. Repository接口定义​

基础Repository接口

// 基础Repository接口
public interface OrderRepository extends Repository<Order, String> {
// 基础方法
}

// CRUD Repository接口
public interface OrderRepository extends CrudRepository<Order, String> {
// 继承所有CRUD方法
}

// Elasticsearch专用Repository接口
public interface OrderRepository extends ElasticsearchRepository<Order, String> {
// 继承所有CRUD方法 + Elasticsearch特有方法
}

// 分页和排序Repository接口
public interface OrderRepository extends PagingAndSortingRepository<Order, String> {
// 继承分页和排序方法
}

// List版本Repository接口(推荐)
public interface OrderRepository extends ListCrudRepository<Order, String> {
// 返回List而不是Iterable
}

Repository配置

@Configuration
@EnableElasticsearchRepositories(
basePackages = "cn.com.rydeen.repository",
basePackageClasses = OrderRepository.class,
namedQueriesLocation = "classpath:elasticsearch-queries.properties"
)
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

@Override
public RestHighLevelClient elasticsearchClient() {
// 客户端配置
return new RestHighLevelClient(/* 配置 */);
}
}

2. @Query 注解使用​

@Query 注解允许直接编写 Elasticsearch JSON 查询语句,支持多种查询场景。

基础查询示例

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 简单查询
@Query("{\"term\": {\"status\": \"?0\"}}")
List<Order> findByStatus(String status);

// 嵌套查询
@Query("""
{
"nested": {
"path": "orderItems",
"query": {
"match": {
"orderItems.productName": "?0"
}
}
}
}
""")
List<Order> findByProductName(String productName);

// 复杂查询
@Query("""
{
"bool": {
"must": [
{"term": {"status": "?0"}},
{"range": {"amount": {"gte": "?1"}}}
]
}
}
""")
List<Order> findByStatusAndMinAmount(String status, Double minAmount);

// 使用 SpEL 表达式
@Query("""
{
"bool": {
"must": [
{"term": {"status": "#{#status}"}},
{"range": {"amount": {"gte": "#{#minAmount}"}}}
]
}
}
""")
List<Order> findByStatusAndMinAmountSpEL(@Param("status") String status,
@Param("minAmount") Double minAmount);

// 聚合查询
@Query("""
{
"query": {"term": {"status": "?0"}},
"aggs": {
"category_stats": {
"terms": {"field": "category"},
"aggs": {
"total_amount": {"sum": {"field": "amount"}}
}
}
}
}
""")
SearchHits<Order> findByStatusWithAggregations(String status);

// 高亮查询
@Query("""
{
"query": {"match": {"description": "?0"}},
"highlight": {
"fields": {
"description": {},
"orderName": {}
}
}
}
""")
SearchHits<Order> findByDescriptionWithHighlight(String description);
}

3. 查询方法派生​

根据Spring Data Elasticsearch查询方法官方文档,支持通过方法名自动派生查询。

基础查询方法

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 精确匹配
List<Order> findByStatus(String status);
List<Order> findByStatusAndAmount(String status, Double amount);
List<Order> findByStatusOrAmount(String status, Double amount);

// 范围查询
List<Order> findByAmountBetween(Double minAmount, Double maxAmount);
List<Order> findByAmountGreaterThan(Double amount);
List<Order> findByAmountLessThan(Double amount);
List<Order> findByAmountGreaterThanEqual(Double amount);
List<Order> findByAmountLessThanEqual(Double amount);

// 存在性查询
List<Order> findByDescriptionExists();
List<Order> findByDescriptionNotExists();

// 空值查询
List<Order> findByDescriptionIsNull();
List<Order> findByDescriptionIsNotNull();

// 集合查询
List<Order> findByCategoryIn(Collection<String> categories);
List<Order> findByCategoryNotIn(Collection<String> categories);

// 文本查询
List<Order> findByDescriptionLike(String description);
List<Order> findByDescriptionNotLike(String description);
List<Order> findByDescriptionStartingWith(String prefix);
List<Order> findByDescriptionEndingWith(String suffix);
List<Order> findByDescriptionContaining(String text);

// 日期查询
List<Order> findByCreateTimeAfter(LocalDateTime time);
List<Order> findByCreateTimeBefore(LocalDateTime time);
List<Order> findByCreateTimeBetween(LocalDateTime start, LocalDateTime end);
}

嵌套字段查询

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 嵌套字段查询
List<Order> findByOrderItemsProductName(String productName);
List<Order> findByOrderItemsPriceBetween(Double minPrice, Double maxPrice);
List<Order> findByOrderItemsProductIdIn(Collection<String> productIds);

// 嵌套字段存在性查询
List<Order> findByOrderItemsExists();
List<Order> findByOrderItemsNotExists();
}

地理位置查询

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 地理位置查询
List<Order> findByDeliveryLocationNear(Point point, Distance distance);
List<Order> findByDeliveryLocationWithin(Point point, Distance distance);
List<Order> findByDeliveryLocationWithin(GeoJsonPolygon polygon);
}

4. 查询关键字参考​

根据Spring Data查询关键字官方文档,支持以下查询关键字:

逻辑操作符

关键字示例Elasticsearch查询
AndfindByStatusAndAmountbool.must
OrfindByStatusOrAmountbool.should
NotfindByStatusNotbool.must_not

比较操作符

关键字示例Elasticsearch查询
IsfindByStatusIsterm
EqualsfindByStatusEqualsterm
BetweenfindByAmountBetweenrange
LessThanfindByAmountLessThanrange.lt
LessThanEqualfindByAmountLessThanEqualrange.lte
GreaterThanfindByAmountGreaterThanrange.gt
GreaterThanEqualfindByAmountGreaterThanEqualrange.gte

字符串操作符

关键字示例Elasticsearch查询
LikefindByDescriptionLikewildcard
NotLikefindByDescriptionNotLikebool.must_not.wildcard
StartingWithfindByDescriptionStartingWithprefix
EndingWithfindByDescriptionEndingWithwildcard
ContainingfindByDescriptionContainingmatch

集合操作符

关键字示例Elasticsearch查询
InfindByCategoryInterms
NotInfindByCategoryNotInbool.must_not.terms
ExistsfindByDescriptionExistsexists
NotExistsfindByDescriptionNotExistsbool.must_not.exists

5. 分页和排序​

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 分页查询
Page<Order> findByStatus(String status, Pageable pageable);

// 排序查询
List<Order> findByStatusOrderByCreateTimeDesc(String status);
List<Order> findByStatusOrderByAmountAscCreateTimeDesc(String status);

// 分页+排序
Page<Order> findByStatus(String status, Pageable pageable);

// 使用示例
Pageable pageable = PageRequest.of(0, 20, Sort.by("createTime").descending());
Page<Order> page = repository.findByStatus("ACTIVE", pageable);
}

6. 投影查询​

根据Spring Data投影官方文档,支持投影查询来优化数据传输。

接口投影

// 定义投影接口
public interface OrderSummary {
String getOrderId();
String getStatus();
Double getAmount();
LocalDateTime getCreateTime();

// 计算属性
default String getDisplayName() {
return getOrderId() + " - " + getStatus();
}
}

// 使用投影
@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {
List<OrderSummary> findByStatus(String status);
Page<OrderSummary> findByStatus(String status, Pageable pageable);
}

类投影

// 定义投影类
public class OrderProjection {
private final String orderId;
private final String status;
private final Double amount;

public OrderProjection(String orderId, String status, Double amount) {
this.orderId = orderId;
this.status = status;
this.amount = amount;
}

// getter方法
public String getOrderId() { return orderId; }
public String getStatus() { return status; }
public Double getAmount() { return amount; }
}

// 使用投影
@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {
List<OrderProjection> findByStatus(String status);
}

动态投影

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {
<T> List<T> findByStatus(String status, Class<T> type);
}

// 使用示例
List<OrderSummary> summaries = repository.findByStatus("ACTIVE", OrderSummary.class);
List<OrderProjection> projections = repository.findByStatus("ACTIVE", OrderProjection.class);

7. 自定义Repository实现​

根据Spring Data自定义实现官方文档,支持自定义Repository实现。

自定义Repository接口

public interface OrderRepositoryCustom {
List<Order> findComplexOrders(OrderSearchCriteria criteria);
Map<String, Object> getOrderStatistics(String status);
}

自定义Repository实现

public class OrderRepositoryImpl implements OrderRepositoryCustom {

@Autowired
private ElasticsearchTemplate template;

@Override
public List<Order> findComplexOrders(OrderSearchCriteria criteria) {
// 自定义查询逻辑
NativeQuery query = NativeQuery.builder()
.withQuery(q -> q
.bool(b -> b
.must(m -> m.term(t -> t.field("status").value(criteria.getStatus())))
.must(m -> m.range(r -> r.field("amount").gte(gte -> gte.value(criteria.getMinAmount()))))
)
.build();

return template.search(query, Order.class).getSearchHits().stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
}

@Override
public Map<String, Object> getOrderStatistics(String status) {
// 自定义聚合查询
NativeQuery query = NativeQuery.builder()
.withQuery(q -> q.term(t -> t.field("status").value(status)))
.withAggregations("category_stats", a -> a.terms(t -> t.field("category")))
.withAggregations("amount_stats", a -> a.stats(s -> s.field("amount")))
.build();

SearchHits<Order> searchHits = template.search(query, Order.class);
// 处理聚合结果...
return new HashMap<>();
}
}

组合Repository接口

@Repository
public interface OrderRepository extends
ElasticsearchRepository<Order, String>,
OrderRepositoryCustom {

// 继承所有方法
}

8. 领域事件发布​

根据Spring Data领域事件官方文档,支持发布领域事件。

实体事件

@Document(indexName = "orders")
public class Order extends AbstractAggregateRoot<Order> {

@Id
private String id;
private String orderId;
private String status;

public void activate() {
this.status = "ACTIVE";
// 发布领域事件
registerEvent(new OrderActivatedEvent(this.orderId));
}

public void cancel() {
this.status = "CANCELLED";
// 发布领域事件
registerEvent(new OrderCancelledEvent(this.orderId));
}
}

事件监听器

@Component
public class OrderEventListener {

@EventListener
public void handleOrderActivated(OrderActivatedEvent event) {
log.info("订单 {} 已激活", event.getOrderId());
// 处理订单激活逻辑
}

@EventListener
public void handleOrderCancelled(OrderCancelledEvent event) {
log.info("订单 {} 已取消", event.getOrderId());
// 处理订单取消逻辑
}
}

9. 空值处理​

根据Spring Data空值处理官方文档,支持多种空值处理策略。

空值处理配置

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 默认行为:忽略空参数
List<Order> findByStatusAndAmount(String status, Double amount);

// 显式处理空值
@Query("""
{
"bool": {
"must": [
{"term": {"status": "?0"}},
{"range": {"amount": {"gte": "?1"}}}
]
}
}
""")
List<Order> findByStatusAndAmountExplicit(String status, Double amount);
}

空值安全查询

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 使用Optional处理可能为空的值
Optional<Order> findByOrderId(String orderId);

// 使用@Nullable注解
List<Order> findByStatusAndAmount(@Nullable String status, @Nullable Double amount);
}

10. 查询返回类型​

根据Spring Data查询返回类型官方文档,支持多种返回类型。

支持的返回类型

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 单个实体
Order findByOrderId(String orderId);
Optional<Order> findByOrderId(String orderId);

// 集合
List<Order> findByStatus(String status);
Set<Order> findByStatus(String status);
Iterable<Order> findByStatus(String status);

// 分页
Page<Order> findByStatus(String status, Pageable pageable);
Slice<Order> findByStatus(String status, Pageable pageable);

// 流式查询
Stream<Order> findByStatus(String status);

// 计数
long countByStatus(String status);
boolean existsByOrderId(String orderId);

// 删除
long deleteByStatus(String status);
void removeByStatus(String status);
}

异步查询

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 异步查询
@Async
CompletableFuture<Order> findByOrderId(String orderId);

@Async
CompletableFuture<List<Order>> findByStatus(String status);

@Async
CompletableFuture<Page<Order>> findByStatus(String status, Pageable pageable);
}

11. 查询方法最佳实践​

@Repository
public interface OrderRepository extends ElasticsearchRepository<Order, String> {

// 1. 使用有意义的方法名
List<Order> findByStatusAndCreateTimeBetweenOrderByAmountDesc(
String status, LocalDateTime start, LocalDateTime end);

// 2. 使用@Query注解处理复杂查询
@Query("""
{
"bool": {
"must": [
{"term": {"status": "?0"}},
{"range": {"createTime": {"gte": "?1", "lte": "?2"}}}
]
}
}
""")
List<Order> findActiveOrdersInDateRange(LocalDateTime start, LocalDateTime end);

// 3. 使用投影优化数据传输
List<OrderSummary> findOrderSummariesByStatus(String status);

// 4. 使用分页避免大量数据查询
Page<Order> findByStatus(String status, Pageable pageable);

// 5. 使用流式查询处理大量数据
Stream<Order> streamByStatus(String status);
}

实体映射与配置​

1. 基础映射注解​

根据Spring Data Elasticsearch官方文档,Spring Data Elasticsearch提供了丰富的映射注解来配置实体类:

@Document(indexName = "orders", createIndex = true)
public class Order {

@Id
private String id;

@Field(type = FieldType.Keyword)
private String orderId;

@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String description;

@Field(type = FieldType.Date, format = {DateFormat.basic_date, DateFormat.basic_time})
private LocalDateTime createTime;

@Field(type = FieldType.Nested)
private List<OrderItem> orderItems;

@Field(type = FieldType.GeoPoint)
private Point deliveryLocation;

@Field(type = FieldType.Object)
private Map<String, Object> metadata;
}
常用字段类型​
类型说明适用场景
FieldType.Text全文搜索字段商品描述、评论内容
FieldType.Keyword精确匹配字段状态、分类、ID
FieldType.Date日期时间字段创建时间、更新时间
FieldType.Nested嵌套对象字段订单项、标签列表
FieldType.GeoPoint地理位置字段配送地址、门店位置
FieldType.Object动态对象字段扩展属性、元数据

2. 日期格式映射​

@Field(type = FieldType.Date,
format = {DateFormat.basic_date, DateFormat.basic_time},
pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;

// 或者使用自定义格式
@Field(type = FieldType.Date,
format = {},
pattern = "dd.MM.yyyy")
private LocalDate orderDate;

3. 地理位置类型​

// 使用 Point 类型
@Field(type = FieldType.GeoPoint)
private Point deliveryLocation;

// 使用 GeoJson 类型
@Field(type = FieldType.GeoShape)
private GeoJsonPoint location;

// 在代码中使用
Point point = new Point(-118.3026284, 34.118347);
GeoJsonPoint geoPoint = new GeoJsonPoint(-118.3026284, 34.118347);

4. 集合和映射类型​

// 集合类型
@Field(type = FieldType.Nested)
private List<OrderItem> orderItems;

@Field(type = FieldType.Keyword)
private Set<String> tags;

// 映射类型
@Field(type = FieldType.Object)
private Map<String, Address> knownLocations;

@Field(type = FieldType.Object)
private Map<String, Object> metadata;

实体生命周期管理​

1. 实体回调​

根据Spring Data Elasticsearch实体回调文档,Spring Data Elasticsearch支持多种实体生命周期回调:

@Document(indexName = "orders")
public class Order {

@Id
private String id;

private String orderId;
private LocalDateTime createTime;
private LocalDateTime updateTime;

// 保存前回调
@PrePersist
public void prePersist() {
if (createTime == null) {
createTime = LocalDateTime.now();
}
updateTime = LocalDateTime.now();
}

// 更新前回调
@PreUpdate
public void preUpdate() {
updateTime = LocalDateTime.now();
}

// 保存后回调
@PostPersist
public void postPersist() {
log.info("订单 {} 已保存到ES", orderId);
}

// 加载后回调
@PostLoad
public void postLoad() {
log.debug("订单 {} 已从ES加载", orderId);
}
}

2. 自定义回调实现​

@Component
public class OrderAuditCallback implements BeforeConvertCallback<Order> {

@Override
public Order onBeforeConvert(Order order, IndexCoordinates indexCoordinates) {
if (order.getCreateTime() == null) {
order.setCreateTime(LocalDateTime.now());
}
order.setUpdateTime(LocalDateTime.now());
return order;
}
}

@Component
public class OrderAfterLoadCallback implements AfterLoadCallback<Order> {

@Override
public Order onAfterLoad(Order order, IndexCoordinates indexCoordinates) {
// 处理加载后的逻辑,如计算字段、格式化等
if (order.getOrderItems() != null) {
order.setTotalItems(order.getOrderItems().size());
}
return order;
}
}

审计功能​

1. 启用审计​

根据Spring Data Elasticsearch审计文档,可以通过配置启用审计功能:

@Configuration
@EnableElasticsearchAuditing
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

@Override
public RestHighLevelClient elasticsearchClient() {
// 客户端配置
return new RestHighLevelClient(/* 配置 */);
}

@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.map(Authentication::getName);
}
}

2. 审计注解使用​

@Document(indexName = "orders")
@EntityListeners(AuditingEntityListener.class)
public class Order {

@Id
private String id;

@CreatedDate
@Field(type = FieldType.Date)
private LocalDateTime createTime;

@LastModifiedDate
@Field(type = FieldType.Date)
private LocalDateTime updateTime;

@CreatedBy
@Field(type = FieldType.Keyword)
private String createdBy;

@LastModifiedBy
@Field(type = FieldType.Keyword)
private String lastModifiedBy;

@Version
private Long version;
}

高级映射特性​

1. Join类型支持​

根据Spring Data Elasticsearch Join类型文档,支持父子文档关系:

@Document(indexName = "orders")
public class Order {

@Id
private String id;

@Field(type = FieldType.Keyword)
private String orderId;

@JoinTypeRelations(
relations = {
@JoinTypeRelation(parent = "order", children = {"orderItem", "orderLog"})
}
)
private JoinField<String> relation;
}

@Document(indexName = "orders")
public class OrderItem {

@Id
private String id;

@Field(type = FieldType.Keyword)
private String productId;

@JoinTypeRelations(
relations = {
@JoinTypeRelation(parent = "order", children = {"orderItem", "orderLog"})
}
)
private JoinField<String> relation;
}

2. 路由值配置​

根据Spring Data Elasticsearch路由文档,支持自定义路由策略:

@Document(indexName = "orders")
public class Order {

@Id
private String id;

@Field(type = FieldType.Keyword)
private String orderId;

@Routing
private String tenantId; // 使用租户ID作为路由值

@Field(type = FieldType.Keyword)
private String status;
}

// 在查询时指定路由
SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.routing("tenant_001") // 指定路由值
.eq(Order::getStatus, "ACTIVE");

3. 类型别名​

@Document(indexName = "orders")
@TypeAlias("order") // 自定义类型别名
public class Order {

@Id
private String id;

@Field(type = FieldType.Keyword)
private String orderId;
}

// 配置类型别名
@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

@Override
protected Set<Class<?>> getInitialEntitySet() {
return Set.of(Order.class);
}
}

自定义转换器​

1. 类型转换器​

@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

@Bean
@Override
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
return new ElasticsearchCustomConversions(
Arrays.asList(new AddressToMap(), new MapToAddress())
);
}

@WritingConverter
static class AddressToMap implements Converter<Address, Map<String, Object>> {

@Override
public Map<String, Object> convert(Address source) {
Map<String, Object> target = new LinkedHashMap<>();
target.put("city", source.getCity());
target.put("street", source.getStreet());
target.put("location", Arrays.asList(source.getLatitude(), source.getLongitude()));
return target;
}
}

@ReadingConverter
static class MapToAddress implements Converter<Map<String, Object>, Address> {

@Override
public Address convert(Map<String, Object> source) {
Address address = new Address();
address.setCity((String) source.get("city"));
address.setStreet((String) source.get("street"));

@SuppressWarnings("unchecked")
List<Double> coords = (List<Double>) source.get("location");
if (coords != null && coords.size() == 2) {
address.setLatitude(coords.get(0));
address.setLongitude(coords.get(1));
}

return address;
}
}
}

2. 字段值转换器​

@Document(indexName = "orders")
public class Order {

@Id
private String id;

@Field(type = FieldType.Keyword)
private String orderId;

@ValueConverter(OrderStatusConverter.class)
private OrderStatus status;
}

// 自定义字段值转换器
public class OrderStatusConverter implements ValueConverter<OrderStatus, String> {

@Override
public String write(OrderStatus value) {
return value != null ? value.getCode() : null;
}

@Override
public OrderStatus read(String value) {
return OrderStatus.fromCode(value);
}
}

索引管理​

1. 索引创建与配置​

@Service
public class IndexManagementService {

@Autowired
private ElasticsearchTemplate template;

public void createOrderIndex() {
IndexOperations indexOps = template.indexOps(Order.class);

if (!indexOps.exists()) {
// 创建索引
indexOps.create();

// 创建映射
Document document = Document.create();
document.put("mappings", createOrderMappings());
indexOps.putMapping(document);
}
}

private Map<String, Object> createOrderMappings() {
Map<String, Object> mappings = new HashMap<>();
// 详细的映射配置...
return mappings;
}
}

2. 索引别名管理​

@Service
public class IndexAliasService {

@Autowired
private ElasticsearchTemplate template;

public void createAlias(String indexName, String aliasName) {
IndexOperations indexOps = template.indexOps(IndexCoordinates.of(indexName));

AliasQuery aliasQuery = new AliasQuery();
aliasQuery.setAliasName(aliasName);

indexOps.addAlias(aliasQuery);
}

public void switchAlias(String oldIndex, String newIndex, String aliasName) {
// 原子性切换别名
AliasActions aliasActions = new AliasActions();
aliasActions.add(new AliasAction.Add(IndexCoordinates.of(newIndex), aliasName));
aliasActions.add(new AliasAction.Remove(IndexCoordinates.of(oldIndex), aliasName));

template.execute(aliasActions);
}
}

API 接口概览​

分类方法名描述返回类型参数
索引管理createIndex创建索引(Elasticsearch)或集合(SolrCloud)boolean(String indexName, String mappingJson)
deleteIndex删除索引(Elasticsearch)或集合(SolrCloud)boolean(String indexName)
indexExists判断索引/集合是否存在boolean(String indexName)
文档操作save保存单条文档<T> T(String indexName, T document)
deleteById根据ID删除单条文档boolean(String indexName, String id)
findByDocId根据ID查询单条文档<T> Optional<T>(String indexName, String docId, Class<T> clazz)
findOne根据查询条件查找单个文档<T> T(SearchQueryWrapper<T> wrapper)
批量操作saveBatch批量保存文档<T> List<T>(String indexName, List<T> documents)
deleteBatchByIds批量根据ID删除文档boolean(String indexName, List<String> ids)
基础查询count查询文档总数long(SearchQueryWrapper<T> wrapper)
deleteByCondition根据查询条件删除文档long(SearchQueryWrapper<T> wrapper)
高级查询search高级查询(支持常规查询、布尔查询、嵌套查询、脚本查询等)<T> PageResult<T>(SearchQueryWrapper<T> wrapper)
原生查询dslSearch原生查询(使用搜索引擎原生DSL语法)<T> PageResult<T>(StringQueryWrapper<T> wrapper)
nativeSearch原生查询(使用搜索引擎原生API语法)<T> PageResult<T>(NativeQueryWrapper<T> wrapper)

API 接口列表​

索引管理接口​

创建索引​
属性值
方法名createIndex
描述创建索引(Elasticsearch)或集合(SolrCloud)
返回类型boolean

参数表

参数名类型必填描述
indexNameString是索引/集合名称
mappingJsonString否mapping或schema定义(ES为JSON,Solr可忽略)

返回值

  • true: 创建成功
  • false: 已存在或不支持

使用示例

boolean created = elasticsearchService.createIndex("users", mappingJson);
删除索引​
属性值
方法名deleteIndex
描述删除索引(Elasticsearch)或集合(SolrCloud)
返回类型boolean

参数表

参数名类型必填描述
indexNameString是索引/集合名称

返回值

  • true: 删除成功
  • false: 不存在或不支持

使用示例

boolean deleted = elasticsearchService.deleteIndex("users");
检查索引是否存在​
属性值
方法名indexExists
描述判断索引/集合是否存在
返回类型boolean

参数表

参数名类型必填描述
indexNameString是索引/集合名称

返回值

  • true: 存在
  • false: 不存在

使用示例

boolean exists = elasticsearchService.indexExists("users");

文档操作接口​

保存单条文档​
属性值
方法名save
描述将文档保存到指定的索引或集合中,如果文档已存在则更新
返回类型<T> T

参数表

参数名类型必填描述
indexNameString是索引/集合名称
documentT是文档对象

返回值

  • 保存后的文档对象(带ID)

使用示例

User user = new User("张三", 25);
User saved = elasticsearchService.save("users", user);
根据ID删除单条文档​
属性值
方法名deleteById
描述根据文档ID删除指定的文档
返回类型boolean

参数表

参数名类型必填描述
indexNameString是索引/集合名称
idString是文档ID

返回值

  • true: 删除成功
  • false: 删除失败

使用示例

boolean deleted = elasticsearchService.deleteById("users", "user_001");
根据ID查询单条文档​
属性值
方法名findByDocId
描述根据文档ID查询指定的文档
返回类型<T> Optional<T>

参数表

参数名类型必填描述
indexNameString是索引/集合名称
docIdString是文档ID
clazzClass<T>是文档类型Class

返回值

  • Optional<T>: 包装的文档对象

使用示例

Optional<User> user = elasticsearchService.findByDocId("users", "user_001", User.class);
根据查询条件查找单个文档​
属性值
方法名findOne
描述根据查询条件查找单个文档
返回类型<T> T

参数表

参数名类型必填描述
wrapperSearchQueryWrapper<T>是查询参数构建器

返回值

  • 匹配的文档对象,如果未找到返回null

使用示例

User user = elasticsearchService.findOne(
SearchQueryWrapper.create(User.class, "users")
.eq(User::getName, "张三")
);

批量操作接口​

批量保存文档​
属性值
方法名saveBatch
描述批量保存多个文档到指定的索引或集合中
返回类型<T> List<T>

参数表

参数名类型必填描述
indexNameString是索引/集合名称
documentsList<T>是文档列表

返回值

  • 保存后的文档列表

使用示例

List<User> users = Arrays.asList(
new User("张三", 25),
new User("李四", 30)
);
List<User> savedUsers = elasticsearchService.saveBatch("users", users);
批量根据ID删除文档​
属性值
方法名deleteBatchByIds
描述根据文档ID列表批量删除文档
返回类型boolean

参数表

参数名类型必填描述
indexNameString是索引/集合名称
idsList<String>是文档ID列表

返回值

  • true: 删除成功
  • false: 删除失败

使用示例

List<String> ids = Arrays.asList("1", "2", "3");
boolean deleted = elasticsearchService.deleteBatchByIds("users", ids);

基础查询接口​

查询文档总数​
属性值
方法名count
描述根据查询条件统计匹配的文档总数
返回类型long

参数表

参数名类型必填描述
wrapperSearchQueryWrapper<T>是查询参数构建器

返回值

  • 匹配文档总数

使用示例

long count = elasticsearchService.count(
SearchQueryWrapper.create(User.class, "users")
.eq(User::getStatus, "active")
);
根据查询条件删除文档​
属性值
方法名deleteByCondition
描述根据指定的查询条件删除匹配的所有文档
返回类型long

参数表

参数名类型必填描述
wrapperSearchQueryWrapper<T>是查询参数构建器

返回值

  • 删除的文档数量

使用示例

long deletedCount = elasticsearchService.deleteByCondition(
SearchQueryWrapper.create(User.class, "users")
.eq(User::getStatus, "inactive")
);

高级查询接口​

高级查询​
属性值
方法名search
描述高级查询,支持常规查询、布尔查询、嵌套查询、脚本查询等
返回类型<T> PageResult<T>

参数表

参数名类型必填描述
wrapperSearchQueryWrapper<T>是查询参数构建器

返回值

  • 分页结果PageResult

使用示例

PageResult<User> result = elasticsearchService.search(
SearchQueryWrapper.create(User.class, "users")
.eq(User::getStatus, "active")
.ge(User::getAge, 18)
.like(User::getName, "张")
.page(0, 10)
.orderByDesc(User::getCreateTime)
);

原生查询接口​

DSL查询​
属性值
方法名dslSearch
描述原生查询,使用搜索引擎原生DSL语法
返回类型<T> PageResult<T>

参数表

参数名类型必填描述
wrapperStringQueryWrapper<T>是查询参数构建器

返回值

  • 查询结果PageResult

使用示例

PageResult<Order> result = elasticsearchService.dslSearch(
StringQueryWrapper.create(Order.class, "orders")
.page(0, 10)
.stringQuery("""
{
"nested": {
"path": "orderItems",
"query": {
"match": {
"orderItems.productName": "iPhone"
}
}
}
}
""")
);
Native查询​
属性值
方法名nativeSearch
描述原生查询,使用搜索引擎原生API语法
返回类型<T> PageResult<T>

参数表

参数名类型必填描述
wrapperNativeQueryWrapper<T>是查询参数构建器

返回值

  • 查询结果PageResult

使用示例

PageResult<Order> result = elasticsearchService.nativeSearch(
NativeQueryWrapper.create(Order.class, "orders")
.page(0, 10)
.nativeQuery(q -> q
.bool(b -> b
.must(m -> m.term(t -> t.field("status").value("ACTIVE")))
.must(m -> m.range(r -> r.field("amount").gte(gte -> gte.value(100.0))))
)
);

数据结构说明​

SearchQueryWrapper​

查询参数构建器,用于构建复杂的搜索条件。推荐使用此方式构建查询,内部会自动处理build操作。

基础查询方法​
方法名描述示例
eq(column, value)等于.eq(User::getName, "张三")
ne(column, value)不等于.ne(User::getStatus, "inactive")
in(column, values)包含在集合中.in(User::getCategory, Arrays.asList("A", "B"))
notIn(column, values)不包含在集合中.notIn(User::getStatus, Arrays.asList("deleted"))
gt(column, value)大于.gt(User::getAge, 18)
ge(column, value)大于等于.ge(User::getAge, 18)
lt(column, value)小于.lt(User::getAge, 65)
le(column, value)小于等于.le(User::getAge, 65)
between(column, from, to)区间查询.between(User::getCreateTime, startDate, endDate)
like(column, value)模糊匹配.like(User::getName, "张")
exists(column)字段存在.exists(User::getEmail)
notExists(column)字段不存在.notExists(User::getDeletedAt)
排序和分页方法​
方法名描述示例
orderByAsc(column)升序排序.orderByAsc(User::getCreateTime)
orderByDesc(column)降序排序.orderByDesc(User::getUpdateTime)
page(page, size)分页设置.page(0, 10)
高亮和建议方法​
方法名描述示例
highlight(columns...)设置高亮字段.highlight(User::getName, User::getDescription)
suggest(column, prefix)设置建议查询.suggest(User::getName, "张")
suggest(column, prefix, size)设置建议查询(指定数量).suggest(User::getName, "张", 5)

StringQueryWrapper​

JSON字符串查询包装器,用于直接使用Elasticsearch JSON查询字符串。

字段名类型描述
stringQueryString原生查询JSON字符串

使用示例

StringQueryWrapper<Order> wrapper = StringQueryWrapper
.create(Order.class, "orders")
.page(0, 10)
.stringQuery("""
{
"bool": {
"must": [
{"term": {"status": "ACTIVE"}},
{"range": {"amount": {"gte": 100}}}
]
}
}
""");

NativeQueryWrapper​

类型安全的原生查询包装器,使用Spring Data Elasticsearch的NativeQuery。

字段名类型描述
nativeQueryQueryElasticsearch原生查询对象

使用示例

NativeQueryWrapper<Order> wrapper = NativeQueryWrapper
.create(Order.class, "orders")
.page(0, 10)
.nativeQuery(q -> q
.bool(b -> b
.must(m -> m.term(t -> t.field("status").value("ACTIVE")))
.must(m -> m.range(r -> r.field("amount").gte(gte -> gte.value(100.0))))
);

PageResult​

统一分页结果对象。

字段名类型描述
contentList<T>当前页数据列表
totallong总条数
pageint当前页码(从0开始)
sizeint每页大小
aggregationsMap<String, Object>聚合结果
highlightsMap<String, Map<String, List<String>>>高亮内容
suggestionsMap<String, List<String>>建议/自动补全结果

使用示例​

基本查询示例​

@Service
@RequiredArgsConstructor
public class UserService {

private final ElasticsearchService elasticsearchService;

public PageResult<User> searchUsers(String keyword, int page, int size) {
SearchQueryWrapper<User> wrapper = SearchQueryWrapper
.create(User.class, "users")
.page(page, size);

if (StringUtils.hasText(keyword)) {
wrapper.like(User::getName, keyword);
}

return elasticsearchService.search(wrapper);
}

public User findUserByName(String name) {
return elasticsearchService.findOne(
SearchQueryWrapper.create(User.class, "users")
.eq(User::getName, name)
);
}

public long countActiveUsers() {
return elasticsearchService.count(
SearchQueryWrapper.create(User.class, "users")
.eq(User::getStatus, "active")
);
}
}

复杂查询示例​

// 多条件组合查询
SearchQueryWrapper<Order> wrapper = SearchQueryWrapper
.create(Order.class, "orders")
.eq(Order::getStatus, "ACTIVE")
.between(Order::getCreateTime, startDate, endDate)
.in(Order::getCategory, Arrays.asList("ELECTRONICS", "CLOTHING"))
.like(Order::getDescription, "重要")
.orderByDesc(Order::getCreateTime)
.page(0, 20)
.highlight(Order::getDescription, Order::getOrderName);

PageResult<Order> result = elasticsearchService.search(wrapper);

聚合查询示例​

// 使用 dslSearch 进行聚合查询
StringQueryWrapper<Order> wrapper = StringQueryWrapper
.create(Order.class, "orders")
.page(0, 0) // 聚合查询不需要分页
.stringQuery("""
{
"query": {
"term": {"status": "ACTIVE"}
},
"aggs": {
"category_stats": {
"terms": {"field": "category"},
"aggs": {
"total_amount": {"sum": {"field": "amount"}},
"avg_amount": {"avg": {"field": "amount"}}
}
},
"amount_stats": {
"stats": {"field": "amount"}
}
}
}
""");

PageResult<Order> result = elasticsearchService.dslSearch(wrapper);
Map<String, Object> aggregations = result.getAggregations();

批量操作示例​

// 批量保存
List<User> users = Arrays.asList(
new User("张三", 25),
new User("李四", 30)
);
List<User> savedUsers = elasticsearchService.saveBatch("users", users);

// 批量删除
List<String> ids = Arrays.asList("1", "2", "3");
boolean deleted = elasticsearchService.deleteBatchByIds("users", ids);