Loading...
Loading...
Compare original and translation side by side
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
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>spring:
elasticsearch:
uris: http://localhost:9200
username: ${ELASTICSEARCH_USERNAME:}
password: ${ELASTICSEARCH_PASSWORD:}
connection-timeout: 5s
socket-timeout: 30sspring:
elasticsearch:
uris: http://localhost:9200
username: ${ELASTICSEARCH_USERNAME:}
password: ${ELASTICSEARCH_PASSWORD:}
connection-timeout: 5s
socket-timeout: 30s@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;
}@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;
}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);
}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);
}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);
}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);
}@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);
}
}@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);
}
}| 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查询 |
| 大结果集使用分页查询 | 一次性拉取所有文档 |
elasticsearchelasticsearchelasticsearchelasticsearch| 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过滤指定返回字段 |
| 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异常 |