Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API 进行封装 。
官方网站:Spring Data Elasticsearch
创建工程 elasticsearch-springdata-es
1.1引入依赖
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-data-elasticsearchartifactId>
- dependency>
1.2 配置文件application.properties
- # es服务地址
- elasticsearch.host=127.0.0.1
- # es服务端口
- elasticsearch.port=9200
- # 配置日志级别,开启debug日志
- logging.level.com.atguigu=debug
1.3主启动
- @SpringBootApplication
- public class SpringDataESApplication {
- public static void main(String[] args) {
- SpringApplication.run(SpringDataESApplication.class,args);
- }
- }
1.4配置类
ElasticsearchRestTemplate基于RestHighLevelClient客户端的。需要自定义配置类,继承AbstractElasticsearchConfiguration,并实现elasticsearchClient()抽象方法,创建RestHighLevelClient对象
- @ConfigurationProperties(prefix = "elasticsearch")
- @Configuration
- @Getter
- @Setter
- public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
- private String host ;
- private Integer port ;
-
- //重写父类方法
- @Override
- public RestHighLevelClient elasticsearchClient() {
- //高级客户端
- RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
- RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
- return restHighLevelClient;
- }
- }
1.5实体类
这要搁平时来说,实体类屁用没有,都懒得提,提了都降价。这次不一样了,Spring Data ES中
实体类可是顶大用啊,主要体现在里面注解就老多了,跟我们创建的索引库息息相关
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- @Document(indexName = "item",shards = 1, replicas = 1)
- public class Item {
- @Id
- private Long id;
-
- @Field(type = FieldType.Text, analyzer = "ik_max_word")
- private String title; //标题
-
- @Field(type = FieldType.Keyword)
- private String category;// 分类
-
- @Field(type = FieldType.Keyword)
- private String brand; // 品牌
-
- @Field(type = FieldType.Double)
- private Double price; // 价格
-
- @Field(index = false, type = FieldType.Keyword)
- private String images; // 图片地址
- }
@Document 作用在类,标记实体类为文档对象,属性解释如下:
- indexName:对应索引库名称
- shards:主分片数量
- replicas:复制分片数量
@Id 作用在成员变量,标记一个字段作为id主键
@Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
- type:字段类型,取值是枚举:FieldType
- index:是否索引,布尔类型,默认是true
- store:是否存储,布尔类型,默认是false
- analyzer:分词器名称:ik_max_word
1.6写一个测试类
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class SpringDataESTest {
- @Autowired
- private ElasticsearchRestTemplate elasticsearchTemplate;
-
- @Test
- public void test0(){
- System.out.println(elasticsearchTemplate);
- }
- }
运行,控制台打印

2.1创建索引库
- @Test
- public void testCreate() {
- // 创建索引库,会根据Item类的@Document注解信息来创建
- elasticsearchTemplate.createIndex(Item.class);
- // 配置映射,会根据Item类中的id、Field等字段来自动完成映射
- elasticsearchTemplate.putMapping(Item.class);
- }
Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。【不用在类上加任何的注解,我说的】
public interface ItemDao extends ElasticsearchRepository- {
}
2.2 添加文档
- @Autowired
- private ItemDao itemDao;
-
- @Test
- public void testAdd() {
- Item item = new Item(1L, "联想笔记本电脑小新Pro14 英特尔Evo平台", " 笔记本", "联想", 4599.00, "http://image.leyou.com/13123.jpg");
- //添加文档
- itemDao.save(item);
- }
2.3 修改文档
- @Test
- public void testUpdate() {
- Item item = new Item(1L, "小米手机", " 手机", "小米", 9499.00, "http://image.leyou.com/13123.jpg");
- //文档修改【id存在就是修改,否则就是插入】
- itemDao.save(item);
- }

2.4批量添加文档
- @Test
- public void testBatchADD() {
- List
- list = new ArrayList<>();
- list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.leyou.com/123.jpg"));
- list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
- // 接收对象集合,实现批量新增文档
- itemDao.saveAll(list);
- }

2.5删除文档
- @Test
- public void testDelete() {
- //删除文档
- itemDao.deleteById(1L);
- }
2.6根据id查询文档
- @Test
- public void testQueryByID(){
- Optional
- optional = itemDao.findById(2L);
- //根据id查询文档
- System.out.println(optional.get());
- }
2.7查询全部并按照价格降序排序
- @Test
- public void testFindALL(){
- // 查询全部,并按照价格降序排序
- Iterable
- items = itemDao.findAll(Sort.by(Sort.Direction.DESC, "price"));
- items.forEach(item-> System.out.println(item));
- }
Spring Data 的另一个强大功能,是根据方法名称自动实现功能。
比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类
测试类中测试方法
@Test public void testFindByTitile(){ // 查询全部,并按照价格降序排序 Iterable- items = itemDao.findByTitle("手机");
items.forEach(item-> System.out.println(item)); }首先,要知道索引库中所有的文档如下:
运行后控制台打印如下:效果他不就有了吗
但是方法名称要符合一定的约定
| Keyword | Sample(方法命名) | Elasticsearch Query String(执行方式) |
| And | findByNameAndPrice | {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
| Or | findByNameOrPrice | {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
| Is | findByName | {"bool" : {"must" : {"field" : {"name" : "?"}}}} |
| Not | findByNameNot | {"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
| Between | findByPriceBetween | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
| LessThanEqual | findByPriceLessThan | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
| GreaterThanEqual | findByPriceGreaterThan | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
| Before | findByPriceBefore | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
| After | findByPriceAfter | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
| Like | findByNameLike | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
| StartingWith | findByNameStartingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
| EndingWith | findByNameEndingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
| Contains/Containing | findByNameContaining | {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}} |
| In | findByNameIn(Collection | {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
| NotIn | findByNameNotIn(Collection | {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
| Near | findByStoreNear | Not Supported Yet ! |
| True | findByAvailableTrue | {"bool" : {"must" : {"field" : {"available" : true}}}} |
| False | findByAvailableFalse | {"bool" : {"must" : {"field" : {"available" : false}}}} |
| OrderBy | findByAvailableTrueOrderByNameDesc | {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}} |
虽然基本查询和自定义方法已经很强大了,但是如果是复杂查询(模糊、通配符、词条查询等)就显得力不从心了。此时,我们只能使用原生查询。
在这之前,我要吐槽。Elasticsearch 的Java客户端用起来费老劲了,昨天用Kibana他不香吗,这是越整合越倒流了。增加了学习的成本不说,还整合的像坨粑粑。
人家jedis整合redis,方法名就是命令名,多简单。人家JDBC整合mysql,都是自己写sql,然后用占位符赋值。结果你它吗整合ES就整合成这个鬼样子。
建议直接看5、6,还算有点用的。其他的就
2.1添加索引
- @Test
- public void addIndex() throws Exception {
- //1.使用client获取操作索引的对象 indicesClient 操作索引库的客户端
- IndicesClient indicesClient = client.indices();
- //2.请求对象
- CreateIndexRequest createRequest = new CreateIndexRequest("abc");
- //发起创建索引库的请求
- CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
- //3.根据返回值判断结果
- System.out.println(response.isAcknowledged());
- }
2.2 添加索引和映射
- /**
- * 添加索引 创建索引库并指定映射
- */
- @Test
- public void addIndexAndMapping() throws IOException {
- //1.使用client获取操作索引的对象
- IndicesClient indicesClient = client.indices();
- //2.具体操作,获取返回值
- CreateIndexRequest createRequest = new CreateIndexRequest("aaa");
- //2.1 设置mappings
- String mapping = "{\n" +
- " \"properties\" : {\n" +
- " \"address\" : {\n" +
- " \"type\" : \"text\",\n" +
- " \"analyzer\" : \"ik_max_word\"\n" +
- " },\n" +
- " \"age\" : {\n" +
- " \"type\" : \"long\"\n" +
- " },\n" +
- " \"name\" : {\n" +
- " \"type\" : \"keyword\"\n" +
- " }\n" +
- " }\n" +
- " }";
- createRequest.mapping(mapping,XContentType.JSON);
- CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);
- //3.根据返回值判断结果
- System.out.println(response.isAcknowledged());
- }
- }
2.4删除索引
2.5判断索引是否存在
3.1 添加文档(使用map作为数据)
3.2添加文档(使用对象作为数据)
3.3修改文档
3.4根据id查询文档
3.5根据id删除文档
3.6批量操作脚本
Bulk 批量操作是将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数(带宽)。
4.1查询所有matchAll
4.2term查询
4.3matchQuery词条分词查询
4.4模糊查询-脚本
4.5范围查询-脚本
4.6queryString查询-脚本
4.7布尔查询
4.8聚合查询
目的:将数据库中Goods表的数据导入到ElasticSearch中
ElasticSearch的索引一旦创建,只允许添加字段,不允许改变字段。因为改变字段,需要重建倒排索引,影响内部缓存结构,性能太低。
解决办法:重建一个新的索引,并将原有索引的数据导入到新索引中。
原索引库 :student_index_v1
新索引库 :student_index_v2
# 2:将student_index_v1 数据拷贝到 student_index_v2 POST _reindex { "source": { "index": "student_index_v1" }, "dest": { "index": "student_index_v2" } }
这里我也提出过一个问题,虽然我知道答案
问题:老师,索引库拷贝的时候,课堂上演示的是把date类型的“2020-11-11”拷贝到了新的索引库的Text类型。那要是把Text类型的"2020年11月11号",拷贝到新的索引库的date类型,是不可以的吗
肯定是会报错的。不得不说,Elasticsearch中值得诟病的地方真它奈奈的多。除了查到快点,我都想给它一大嘴巴子!