第三章:使用SpringBoot操作ElasticSearch

222 阅读9分钟

使用SpringBoot操作ElasticSearch

我正在参加「掘金·启航计划」

使用SpringBoot连接操作ES的方法有很多

官方表示

TransportClient在 ES7中已经不推荐使用,在ES8中将删除

强烈建议您使用High Level REST Client而不是TransportClient

所以本次的示例使用 spring-boot-starter-data-elasticsearch 进行操作

官方的版本建议 建议根据官方来

Spring Data ElasticsearchElasticsearchSpring Boot
4.0.x7.6.22.3.x
3.2.x6.8.62.2.x
3.1.x6.2.22.1.x
3.0.x5.5.02.0.x
2.1.x2.4.01.5.x

一、准备阶段

1. 创建SpringBoot项目 名称为 elastic-search-study-spring-boot

2. 添加 pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

3. 创建实体类 User 用于数据的传输

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

import java.io.Serializable;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "yufire", type = "user")
public class User implements Serializable {
    @Id
    private String id;
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String name;
    @Field
    private Integer age;
    @Field
    private Date birthday;
    @Field
    private String desc;

    private static final long serialVersionUID = -2193182831746543212L;

}
  • @Document 文档
    • indexName 索引名称
    • type 类型名称
  • @Id 表明这个字段在 es中是id
  • @Field 表示这个字段是es中的普通属性
    • 也可以指定这个字段的类型 和 分词类型
    • 不指定类型也可以 会根据 Java 类型来自动指定

二、继承接口

Spring Data存储库抽象中的中央接口是Repository , 它需要类型以及类的ID类型作为类型参数来进行管理

我们只需要继承 ElasticsearchRepository<T,ID>接口即可


1. 创建一个接口 userDao

在 继承接口后边指定 类型,和类型ID的类型

package cn.yufire.esstudy3.dao;

import cn.yufire.esstudy3.pojo.User;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ESDao extends ElasticsearchRepository<User, String> {
    
}

2. 创建索引 创建类型 添加数据

如果es中没有该索引 会根据实体类中配置的索引信息来自动创建索引和类型

 @Resource
private ESDao esDao;

@Test
void contextLoads() {
    User user = new User();
    user.setId("1");
    user.setName("张三");
    user.setAge(22);
    user.setBirthday(new Date());
    user.setDesc("第一位用户");

    User save = esDao.save(user);
    System.out.println(save);
}

控制台

User(id=1, name=张三, age=22, birthday=Sat Jul 25 11:55:18 CST 2020, desc=第一位用户)

可以发现es中多了个 索引并且数据也别添加了进去

img


3. 批量添加数据

对象可以从数据库中查询出来 在添加到 es 中去

只需要调用 saveAll 方法即可

// 创建四个对象
User user1 = new User("2", "小花", 20, new Date(), "第二名测试用户");
User user2 = new User("3", "小绿", 21, new Date(), "第三名测试用户");
User user3 = new User("4", "小红", 22, new Date(), "第四名测试用户");
User user4 = new User("5", "小大", 28, new Date(), "第五名测试用户");

// 把数据添加到 list中去
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);

// 添加到 es中去
Iterable<User> addUsers = esDao.saveAll(users);
System.out.println(addUsers);

控制台

User(id=2, name=小花, age=20, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第二名测试用户)
User(id=3, name=小绿, age=21, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第三名测试用户) User(id=4, name=小红, age=22, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第四名测试用户) 
User(id=5, name=小大, age=28, birthday=Sat Jul 25 12:00:27 CST 2020, desc=第五名测试用户) 

4. 修改数据

在 ES 中 修改数据就是保存一个新的数据然后覆盖以前的数据 只要 Id 一致 就可以完成修改

User user1 = new User("2", "小花花", 20, new Date(), "第二名测试用户");
esDao.save(user1);

5. 删除数据

 User user1 = new User("2", "小花花", 20, new Date(), "第二名测试用户");

// 根据id 删除
esDao.deleteById("2");
// 根据对象删除
esDao.delete(user1);
// 根据集合删除
esDao.deleteAll(你的集合);
// 删除 该索引中所有的数据
esDao.deleteAll();

6. match查询

Iterable<User> search = esDao.search(QueryBuilders.matchQuery("name", "小"));

7. term查询

Iterable<User> search = esDao.search(QueryBuilders.termQuery("name", "小"));

8. range查询

Iterable<User> search = esDao.search(QueryBuilders.rangeQuery("age").gt(20).lt(30));

9. 多条件查询

// 创建 bool子句对象
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

boolQueryBuilder.must(QueryBuilders.matchQuery("name", "小"));
boolQueryBuilder.mustNot(QueryBuilders.termQuery("age", 18));

boolQueryBuilder.should(QueryBuilders.matchQuery("name", "小"));
boolQueryBuilder.should(QueryBuilders.termQuery("age", 18));

boolQueryBuilder.filter(QueryBuilders.rangeQuery("age").gt(20).lt(30));

esDao.search(boolQueryBuilder);

10. 分页查询

只需要在 查询对象后边多加上一个参数(分页请求) 并指定页码即可

esDao.search(boolQueryBuilder, PageRequest.of(0, 10));

11. 排序查询

只需要在后边添加排序参数 以及排序的字段即可

Iterable<User> search = esDao.search(boolQueryBuilder, PageRequest.of(0, 2, Sort.by(Sort.Order.asc("age"))));

三、高级REST客户端

官方强烈推荐使用的 操作ES方式

1. 创建一个 RestClient

  • RestClient.builder 内可以配置多个主机 已完成集群操作
  • 因为是Rest客户端所以时使用HTTP方式进行操作 所以要使用 9200端口
/**
 * <p>
 * Description: ES 高级客户端配置类
 * </p>
 *
 * @author yufire
 * @version v1.0.0
 * @see cn.yufire.esstudy3.config
 * @since 2020-07-25 21:10:42
 */
@Configuration
public class ESRestClientConfig {

    /**
     * 因为是Rest客户端所以时使用HTTP方式进行操作
     * 所以要使用 9200端口
     *
     * @return
     */
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("elastic", 9200, "http")
                        //new HttpHost("elastic", 9200, "http")
                ));
    }
}


一、索引操作

1. 创建索引

  • 如果 只是想单纯的创建索引 那么可以把 设置 类型 和 其他配置的那两行代码删掉即可!
// 创建一个创建索引的请求 并且指定索引名称
CreateIndexRequest createIndexRequest = new CreateIndexRequest("yufire");
String json = "{\n" +
    "      \"properties\": {\n" +
    "        \"name\":{\n" +
    "          \"type\": \"text\",\n" +
    "          \"analyzer\":\"ik_smart\"\n" +
    "        },\n" +
    "        \"age\":{\n" +
    "          \"type\": \"integer\"\n" +
    "        },\n" +
    "        \"birthday\":{\n" +
    "          \"type\": \"date\"\n" +
    "        },\n" +
    "        \"desc\":{\n" +
    "          \"type\":\"text\",\n" +
    "          \"analyzer\":\"ik_max_word\"\n" +
    "        }\n" +
    "      }\n" +
    "    }";
// 设置类型 属性JSON字符串 设置数据的格式
createIndexRequest.mapping("user", json, XContentType.JSON);
// 设置索引的一些配置 分片 和 副本 数量
createIndexRequest.settings(Settings.builder()
                            .put("index.number_of_shards", 3)
                            .put("index.number_of_replicas", 2));

// 发送创建索引的请求
CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest);
// 判断是否创建成功
System.out.println(createIndexResponse.isAcknowledged());

2. 删除索引

// 创建删除索引的请求 并且指定索引名
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("yufire");
// 一次删除多个索引的操作
deleteIndexRequest.indices("yufire", "yufire1", "yufire2", "....");
// 发送请求
DeleteIndexResponse delete = client.indices().delete(deleteIndexRequest);
// 判断是否删除成功
System.out.println(delete.isAcknowledged());

二、文档操作

1. 添加文档

 // 创建 添加文档的请求 并指定索引名称和类型名称
IndexRequest indexRequest = new IndexRequest("yufire", "user");

// 创建一个对象
User user = new User("1", "小王", 22, new Date(), "第一名用户");
// 添加到请求中去 要转换为JSON格式 ,数据的格式
indexRequest.source(JSON.toJSONString(user), XContentType.JSON);

// 发送请求
IndexResponse index = client.index(indexRequest);

// 获取状态码 201 为创建成功
System.out.println(index.status().getStatus());

2. 根据Id获取文档

// 创建请求 字段索引 类型 id
GetRequest getRequest = new GetRequest("yufire", "user", "kUlYhnMBP9XWT10qGJVe");
// 发送请求
GetResponse documentFields = client.get(getRequest);
// 获取到结果 在转换为 User类型
User user = JSONObject.parseObject(documentFields.getSourceAsString(), User.class);
System.out.println(user);

3. 修改文档

  • 在 ES 中 修改文档其实就是覆盖文档 以达到修改的效果
 // 创建请求 字段索引 类型 id
UpdateRequest updateRequest = new UpdateRequest("yufire", "user", "lElqhnMBP9XWT10qAJVt");

// 放入要修改的文档
User user = new User("1", "小王吧", 22, new Date(), "第一名用户");
updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);

// 发送请求
UpdateResponse delete = client.update(updateRequest);
// 获取响应状态码 200 为修改成功
System.out.println(delete.status().getStatus());

4. 删除文档

 // 创建请求 字段索引 类型 id
DeleteRequest deleteRequest = new DeleteRequest("yufire", "user", "kUlYhnMBP9XWT10qGJVe");
// 发送请求
DeleteResponse delete = client.delete(deleteRequest);
// 获取响应状态码 200 为删除成功
System.out.println(delete.status().getStatus());

三、批量操作

1. 批量新增

 // 创建批量操作请求
BulkRequest bulkRequest = new BulkRequest();
// 创建四个对象
User user1 = new User("2", "小花", 20, new Date(), "第二名测试用户");
User user2 = new User("3", "小绿", 21, new Date(), "第三名测试用户");
User user3 = new User("4", "小红", 22, new Date(), "第四名测试用户");
User user4 = new User("5", "小大", 28, new Date(), "第五名测试用户");

// 把数据添加到 list中去 模拟走数据库中查询的结果
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);

// 把数据添加值请求中
for (User user : users) {
    bulkRequest.add(new IndexRequest("yufire", "user")
                    .source(JSON.toJSONString(user), XContentType.JSON)
                   );
}

// 发送请求
BulkResponse bulk = client.bulk(bulkRequest);
// 查询是否失败 false 为没有失败 就是成功
System.out.println(bulk.hasFailures());

2. 批量删除

3. 批量修改

只需要批量添加不同的请求类型即可


四、搜索

1. 多id搜索

// 创建搜索请求对象
SearchRequest searchRequest = new SearchRequest("yufire");
//创建搜索参数构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

// 根据多个id进行查询 , 指定索引类型
IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery("user");
// 添加id
idsQueryBuilder.addIds("l0mlhnMBP9XWT10qw5Vl", "mEmlhnMBP9XWT10qw5Vl", "lElqhnMBP9XWT10qAJVt");

// 条件放入条件构造器
searchSourceBuilder.query(idsQueryBuilder);
// 把条件构造器放入 请求对象中
searchRequest.source(searchSourceBuilder);
// 发送搜索请求
SearchResponse search = client.search(searchRequest);

// 解析结果
for (SearchHit hit : search.getHits().getHits()) {
    // 把结果转换为 User类型
    User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
    System.out.println(user);
}

2. mathc查询

// 创建 match搜索对象
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "小");

// 条件放入条件构造器
searchSourceBuilder.query(matchQueryBuilder);

3. term搜索

// 创建 term搜索
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "小");
// 条件放入条件构造器
searchSourceBuilder.query(termQueryBuilder);

4. range搜索

// 创建 range 搜索条件
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gt(18).lt(20);
// 条件放入条件构造器
searchSourceBuilder.query(rangeQueryBuilder);

5. 多条件搜索

BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.matchQuery("name", "小"));
boolQueryBuilder.should(QueryBuilders.termQuery("name", "小"));
boolQueryBuilder.must(QueryBuilders.rangeQuery("age").gt(18).lt(20));
boolQueryBuilder.filter(QueryBuilders.matchQuery("name", "小绿"));
// 把条件构造器放入 请求对象中
searchRequest.source(searchSourceBuilder);

6. 排序

// 添加排序选项
searchSourceBuilder.sort(new FieldSortBuilder("age").order(SortOrder.ASC));

7. 分页

// 设置起始页码
searchSourceBuilder.from(0);
// 设置页面容量
searchSourceBuilder.size(2);

五、高亮查询(重点)

  • 高亮一般用于查询关键字的突出显示
  • 用来提醒用户 提升用户体验 是我们的程序更加的美好!

img

以下是代码

// 创建搜索请求对象
SearchRequest searchRequest = new SearchRequest("yufire");
//创建搜索参数构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


// 创建 match搜索对象
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "小");

// 条件放入条件构造器
searchSourceBuilder.query(matchQueryBuilder);


// 创建高亮条件构造器
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 设置高亮字段
highlightBuilder.field("name");
// 设置 前缀标签 结果会出现在 两个标签的中间
highlightBuilder.preTags("<span color='red'>");
// 设置 后缀标签 结果会出现在 两个标签的中间
highlightBuilder.postTags("</span>");
// 放入搜索构造中去
searchSourceBuilder.highlighter(highlightBuilder);

// 把条件构造器放入 请求对象中
searchRequest.source(searchSourceBuilder);
// 发送搜索请求
SearchResponse search = client.search(searchRequest);

// 解析结果
for (SearchHit hit : search.getHits().getHits()) {
    // 定义替换结果后的Map
    Map<String, Object> sourceAsMap = null;
    // 获取到高亮字段
    HighlightField name = hit.getHighlightFields().get("name");
    // 循环查询结果中符合条件的数据条数
    for (Text fragment : name.getFragments()) {
        // 获取到高亮字段 替换后的结果
        String highField = fragment.toString();
        // 获取到原先的数据集合
        sourceAsMap = hit.getSourceAsMap();
        // 循环替换
        sourceAsMap.put("name", highField);
    }
    // 输出替换后的结果
    System.out.println(sourceAsMap);
}

控制台

  • 可以看到 name属性的数据已经变成我们替换为高亮结果了
{birthday=1595691876949, name=<span color='red'>小</span>大, id=5, age=28, desc=第五名测试用户}
{birthday=1595691876949, name=<span color='red'>小</span>绿, id=3, age=21, desc=第三名测试用户}