spring-data-elasticsearch

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Spring 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: Use
mcp__documentation__fetch_docs
with technology:
spring-data-elasticsearch
for comprehensive documentation.
完整参考:查看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: 30s
yaml
spring:
  elasticsearch:
    uris: http://localhost:9200
    username: ${ELASTICSEARCH_USERNAME:}
    password: ${ELASTICSEARCH_PASSWORD:}
    connection-timeout: 5s
    socket-timeout: 30s

Document 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

最佳实践

DoDon't
Use appropriate field typesMap everything as text
Define proper analyzersUse default for all
Use filters for exact matchesUse match for keywords
Paginate large result setsFetch all documents at once
推荐做法不推荐做法
使用合适的字段类型所有字段都映射为text类型
定义适配业务的分词器所有场景都使用默认分词器
精确匹配场景使用过滤器对keyword类型字段使用match查询
大结果集使用分页查询一次性拉取所有文档

When NOT to Use This Skill

不适用本技能的场景

  • Raw Elasticsearch API - Use
    elasticsearch
    skill for REST API
  • ELK stack setup - Use
    elasticsearch
    skill
  • Primary database - Elasticsearch is for search, not ACID transactions
  • 原生Elasticsearch API开发:使用
    elasticsearch
    技能处理REST API相关需求
  • ELK栈搭建部署:使用
    elasticsearch
    技能
  • 作为主数据库使用:Elasticsearch是专为搜索设计的,不支持ACID事务

Anti-Patterns

反模式

Anti-PatternProblemSolution
Refresh after each writePerformance degradationUse refresh_interval, batch
Deep pagination with from/sizeMemory issuesUse search_after
Mapping all as textPoor search, high diskUse appropriate field types
No index lifecycleDisk exhaustionConfigure ILM policies
Fetching all fieldsWasted bandwidthUse source filtering
反模式问题解决方案
每次写入后强制刷新索引性能严重下降使用refresh_interval配置,采用批量写入
使用from/size实现深度分页内存占用过高使用search_after方案
所有字段都映射为text类型搜索效果差、磁盘占用高选择适配的字段类型
未配置索引生命周期策略磁盘空间耗尽配置ILM策略
查询时拉取所有字段浪费带宽使用source过滤指定返回字段

Quick Troubleshooting

快速排障

ProblemDiagnosticFix
Connection failedCheck Elasticsearch runningStart ES, check URI, SSL
Index not foundCheck index nameCreate index, check @Document
Mapping conflictCheck field typesReindex with correct mapping
Search returns nothingCheck analyzerTest with _analyze API
Version conflictCheck @VersionHandle 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个及以上节点)
  • 分片和副本数量已设置
  • 索引生命周期管理已配置
  • 正确的索引映射已定义
  • 分词器已按需配置
  • 索引采用批量操作写入
  • 监控已开启
  • 安全策略已配置

Reference Documentation

参考文档