spring-data-elasticsearch
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpring Data Elasticsearch - Quick Reference
Spring Data Elasticsearch - 快速参考
Full Reference: See advanced.md for aggregations, autocomplete/suggestions, bulk operations, index management, and Testcontainers integration.
Deep Knowledge: Usewith technology:mcp__documentation__fetch_docsfor comprehensive documentation.spring-data-elasticsearch
完整参考:查看advanced.md了解聚合查询、自动补全/搜索建议、批量操作、索引管理和Testcontainers集成相关内容。
深度知识:调用工具,指定技术参数为mcp__documentation__fetch_docs即可获取完整官方文档。spring-data-elasticsearch
Dependencies
依赖引入
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>Configuration
配置项
yaml
spring:
elasticsearch:
uris: http://localhost:9200
username: ${ELASTICSEARCH_USERNAME:}
password: ${ELASTICSEARCH_PASSWORD:}
connection-timeout: 5s
socket-timeout: 30syaml
spring:
elasticsearch:
uris: http://localhost:9200
username: ${ELASTICSEARCH_USERNAME:}
password: ${ELASTICSEARCH_PASSWORD:}
connection-timeout: 5s
socket-timeout: 30sDocument Mapping
文档映射
java
@Document(indexName = "products")
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "standard")
private String name;
@Field(type = FieldType.Text, analyzer = "standard")
private String description;
@Field(type = FieldType.Keyword)
private String category;
@Field(type = FieldType.Double)
private BigDecimal price;
@Field(type = FieldType.Integer)
private Integer stock;
@Field(type = FieldType.Boolean)
private boolean active;
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime createdAt;
@Field(type = FieldType.Nested)
private List<ProductAttribute> attributes;
@Field(type = FieldType.Keyword)
private List<String> tags;
}java
@Document(indexName = "products")
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "standard")
private String name;
@Field(type = FieldType.Text, analyzer = "standard")
private String description;
@Field(type = FieldType.Keyword)
private String category;
@Field(type = FieldType.Double)
private BigDecimal price;
@Field(type = FieldType.Integer)
private Integer stock;
@Field(type = FieldType.Boolean)
private boolean active;
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime createdAt;
@Field(type = FieldType.Nested)
private List<ProductAttribute> attributes;
@Field(type = FieldType.Keyword)
private List<String> tags;
}Repository Pattern
仓库模式
java
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
List<Product> findByCategory(String category);
List<Product> findByNameContaining(String name);
List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
List<Product> findByActiveTrue();
// Pagination
Page<Product> findByCategory(String category, Pageable pageable);
// Sorting
List<Product> findByCategoryOrderByPriceAsc(String category);
// Count / Exists / Delete
long countByCategory(String category);
boolean existsByName(String name);
void deleteByCategory(String category);
}java
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
List<Product> findByCategory(String category);
List<Product> findByNameContaining(String name);
List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
List<Product> findByActiveTrue();
// 分页
Page<Product> findByCategory(String category, Pageable pageable);
// 排序
List<Product> findByCategoryOrderByPriceAsc(String category);
// 计数/存在判断/删除
long countByCategory(String category);
boolean existsByName(String name);
void deleteByCategory(String category);
}Custom Queries
自定义查询
java
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
@Query("""
{
"multi_match": {
"query": "?0",
"fields": ["name^3", "description", "tags"],
"type": "best_fields",
"fuzziness": "AUTO"
}
}
""")
Page<Product> fullTextSearch(String query, Pageable pageable);
}java
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
@Query("""
{
"multi_match": {
"query": "?0",
"fields": ["name^3", "description", "tags"],
"type": "best_fields",
"fuzziness": "AUTO"
}
}
""")
Page<Product> fullTextSearch(String query, Pageable pageable);
}ElasticsearchOperations
ElasticsearchOperations 使用
java
@Service
@RequiredArgsConstructor
public class ProductSearchService {
private final ElasticsearchOperations elasticsearchOperations;
public SearchHits<Product> search(ProductSearchCriteria criteria) {
BoolQuery.Builder boolQuery = new BoolQuery.Builder();
if (StringUtils.hasText(criteria.getQuery())) {
boolQuery.must(MultiMatchQuery.of(m -> m
.query(criteria.getQuery())
.fields("name^3", "description", "tags")
.fuzziness("AUTO")
)._toQuery());
}
if (StringUtils.hasText(criteria.getCategory())) {
boolQuery.filter(TermQuery.of(t -> t
.field("category")
.value(criteria.getCategory())
)._toQuery());
}
NativeQuery query = NativeQuery.builder()
.withQuery(boolQuery.build()._toQuery())
.withPageable(PageRequest.of(criteria.getPage(), criteria.getSize()))
.build();
return elasticsearchOperations.search(query, Product.class);
}
}java
@Service
@RequiredArgsConstructor
public class ProductSearchService {
private final ElasticsearchOperations elasticsearchOperations;
public SearchHits<Product> search(ProductSearchCriteria criteria) {
BoolQuery.Builder boolQuery = new BoolQuery.Builder();
if (StringUtils.hasText(criteria.getQuery())) {
boolQuery.must(MultiMatchQuery.of(m -> m
.query(criteria.getQuery())
.fields("name^3", "description", "tags")
.fuzziness("AUTO")
)._toQuery());
}
if (StringUtils.hasText(criteria.getCategory())) {
boolQuery.filter(TermQuery.of(t -> t
.field("category")
.value(criteria.getCategory())
)._toQuery());
}
NativeQuery query = NativeQuery.builder()
.withQuery(boolQuery.build()._toQuery())
.withPageable(PageRequest.of(criteria.getPage(), criteria.getSize()))
.build();
return elasticsearchOperations.search(query, Product.class);
}
}Best Practices
最佳实践
| Do | Don't |
|---|---|
| Use appropriate field types | Map everything as text |
| Define proper analyzers | Use default for all |
| Use filters for exact matches | Use match for keywords |
| Paginate large result sets | Fetch all documents at once |
| 推荐做法 | 不推荐做法 |
|---|---|
| 使用合适的字段类型 | 所有字段都映射为text类型 |
| 定义适配业务的分词器 | 所有场景都使用默认分词器 |
| 精确匹配场景使用过滤器 | 对keyword类型字段使用match查询 |
| 大结果集使用分页查询 | 一次性拉取所有文档 |
When NOT to Use This Skill
不适用本技能的场景
- Raw Elasticsearch API - Use skill for REST API
elasticsearch - ELK stack setup - Use skill
elasticsearch - Primary database - Elasticsearch is for search, not ACID transactions
- 原生Elasticsearch API开发:使用技能处理REST API相关需求
elasticsearch - ELK栈搭建部署:使用技能
elasticsearch - 作为主数据库使用:Elasticsearch是专为搜索设计的,不支持ACID事务
Anti-Patterns
反模式
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Refresh after each write | Performance degradation | Use refresh_interval, batch |
| Deep pagination with from/size | Memory issues | Use search_after |
| Mapping all as text | Poor search, high disk | Use appropriate field types |
| No index lifecycle | Disk exhaustion | Configure ILM policies |
| Fetching all fields | Wasted bandwidth | Use source filtering |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 每次写入后强制刷新索引 | 性能严重下降 | 使用refresh_interval配置,采用批量写入 |
| 使用from/size实现深度分页 | 内存占用过高 | 使用search_after方案 |
| 所有字段都映射为text类型 | 搜索效果差、磁盘占用高 | 选择适配的字段类型 |
| 未配置索引生命周期策略 | 磁盘空间耗尽 | 配置ILM策略 |
| 查询时拉取所有字段 | 浪费带宽 | 使用source过滤指定返回字段 |
Quick Troubleshooting
快速排障
| Problem | Diagnostic | Fix |
|---|---|---|
| Connection failed | Check Elasticsearch running | Start ES, check URI, SSL |
| Index not found | Check index name | Create index, check @Document |
| Mapping conflict | Check field types | Reindex with correct mapping |
| Search returns nothing | Check analyzer | Test with _analyze API |
| Version conflict | Check @Version | Handle OptimisticLockingFailureException |
| 问题 | 排查方向 | 修复方案 |
|---|---|---|
| 连接失败 | 检查Elasticsearch是否正常运行 | 启动ES服务,检查URI、SSL配置 |
| 索引不存在 | 检查索引名称是否正确 | 创建索引,核对@Document注解配置 |
| 映射冲突 | 检查字段类型定义 | 使用正确的映射重建索引 |
| 搜索无返回结果 | 检查分词器配置 | 使用_analyze API测试分词效果 |
| 版本冲突 | 检查@Version注解配置 | 处理OptimisticLockingFailureException异常 |
Production Checklist
生产环境检查清单
- Cluster configured (3+ nodes)
- Shards and replicas set
- Index lifecycle management
- Proper mapping defined
- Analyzers configured
- Bulk operations for indexing
- Monitoring enabled
- Security enabled
- 集群配置完成(3个及以上节点)
- 分片和副本数量已设置
- 索引生命周期管理已配置
- 正确的索引映射已定义
- 分词器已按需配置
- 索引采用批量操作写入
- 监控已开启
- 安全策略已配置