一、环境搭建
(1)导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent><dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
(2)编辑配置文件
# 注意: 地址一定不要有末尾字符 '/'
# ElasticsearchRestTemplate客户端的配置
spring:
elasticsearch:
rest:
uris: http://192.168.8.128:9200 # ES服务器所在位置。集群多节点地址用逗号分隔。默认http://localhost:9200
# data: # ElasticsearchTemplate客户端配置。所有的方法API和ElasticsearchRestTemplate完全相同。
# elasticsearch:
# cluster-name: docker-cluster # ES集群的名称。已过期
# cluster-nodes: 192.168.137.128:9300 # ES集群多节点地址。多个地址用逗号分隔。在7版本后已过期
(3)编辑实体类型
/**
* Document - 描述类型和索引的映射。
* indexName - 索引名称
* shards - 主分片数量。默认值 1。
* replicas - 副本分片数量。默认值 1。
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "stu_index", shards = 1, replicas = 0)
public class Student implements Serializable {
/**
* Field - 当前属性和索引中的字段映射规则。
* name - 字段名称。默认和当前类型属性名一致。
* type - 字段类型。默认使用FieldType.AUTO。自动识别。
* analyzer - 分词器。所有的Text类型字段默认使用standard分词器。
* index - 是否创建索引。默认值 true。
* format - 如果字段类型是date日期类型。此属性必须配置,用于设置日期格式化规则,使用DateFormat类型中的常量定义。
*/
@Id
@Field(name = "id", type = FieldType.Keyword)
private Long id;
@Field(name = "name", type = FieldType.Text, analyzer = "ik_max_word")
private String name;
@Field(name = "gender", type = FieldType.Keyword)
private String gender;
@Field(name = "age", type = FieldType.Integer)
private int age;
@Field(name = "hobbies", type = FieldType.Text, analyzer = "ik_smart")
private List<String> hobbies;
@Field(name = "isMarried", type = FieldType.Boolean)
private boolean isMarried;
@Field(name = "books", type = FieldType.Text, analyzer = "ik_smart", index = false)
private String[] books;
@Field(name = "birth", type = FieldType.Date, format = DateFormat.basic_date_time)
private Date birth;
}
二、使用
(1)创建索引
@SpringBootTest
public class TestDataES {
/**
* Spring Data Elasticsearch客户端类型 - ElasticsearchRestTemplate客户端对象,
* 由spring-boot-starter-data-elasticsearch启动时,自动装配创建。
* 要求:
* 1. 当前应用中,必须有启动类型,才能自动装配创建。
* 2. 创建客户端时,根据配置文件,实现服务器连接。默认连接的ES服务器集群是:http://localhost:9200
*/
@Autowired
private ElasticsearchRestTemplate restTemplate;
/**
* 创建索引
*/
@Test
public void testCreateIndex(){
/*
* 在spring data elasticsearch中,所有的索引相关操作都封装在一个操作对象 IndexOperations 中。
* 基于一个实体类型的类对象,创建索引操作对象。实体类型中,有使用注解描述的索引和映射相关信息。可以做为索引的创建的设置。
*/
IndexOperations indexOps = restTemplate.indexOps(Student.class);
// 创建索引
indexOps.create();
}
}
(2)设置映射
/**
* 设置映射
* 在商业开发中,几乎不会使用框架创建索引或设置映射。因为这是架构或者管理员的工作。且不适合使用代码实现
*/
@Test
public void testCreateIndexAndPutMapping(){
IndexOperations indexOps = restTemplate.indexOps(Student.class);
// 设置映射
indexOps.putMapping(indexOps.createMapping());
}
(3)删除索引
/**
* 删除索引
*/
@Test
public void testDeleteIndex(){
restTemplate.indexOps(Student.class).delete();
}
(4)新增单一文档
/**
* 新增单一文档
*/
@Test
public void testAddDocument() throws Exception{
// 创建要新增的实体对象。手工赋予主键,返回结果就是新增的数据对象。
Student student = new Student();
student.setId(1L);
student.setName("张三");
student.setGender("男");
student.setAge(25);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
student.setBirth(sdf.parse("1996-01-01"));
student.setBooks(new String[]{"Java面向对象思想","Spring In Action"});
student.setHobbies(Arrays.asList("吃饭","上班", "睡觉"));
student.setMarried(true);
// 保存数据到Elasticsearch
student = restTemplate.save(student);
System.out.println(student);
System.out.println("====================================================");
// 自动生成主键,返回的结果中包含Elasticsearch生成的主键。
student = new Student();
student.setName("李四");
student.setGender("男");
student.setAge(21);
student.setBirth(sdf.parse("2000-01-01"));
student.setBooks(new String[]{"火影忍者","海贼王"});
student.setHobbies(Arrays.asList("玩游戏","睡觉", "看动漫"));
student.setMarried(false);
student = restTemplate.save(student);
System.out.println(student);
}
(5)批量新增文档
/**
* 批量新增文档
*/
@Test
public void testBatchAdd() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
List<Student> list = new ArrayList<>();
list.add(new Student(2L, "郭德纲", "男", 48, Arrays.asList("郭麒麟"), true, new String[]{"德云逗笑社"}, sdf.parse("1973-01-18")));
list.add(new Student(3L, "于谦", "男", 52, Arrays.asList("郭麒麟","抽烟","喝酒","烫头"), true, new String[]{"德云逗笑社"}, sdf.parse("1969-01-24")));
restTemplate.save(list);
System.out.println("批量新增结束");
}
(6)删除文档
/**
* 删除文档
*/
@Test
public void testDeleteDocument(){
// 删除类型对应的索引中的指定主键数据,返回删除数据的主键。注意:Elasticsearch中的文档主键都是字符串类型的。
String response = restTemplate.delete("1", Student.class);
System.out.println(response);
}
(7)更新文档
/**
* 更新文档。
* save方法,可以覆盖同主键数据。案例省略
* update更新,部分更新
*/
@Test
public void testUpdate(){
UpdateQuery query =
UpdateQuery.builder("2")
.withDocument(Document.parse("{\"hobbies\":[\"郭麒麟\", \"郭汾阳\"]}"))
.build();
UpdateResponse response =
restTemplate.update(query, IndexCoordinates.of("stu_index"));
System.out.println(response.getResult());
}
三、查询
(1)主键查询文档
/**
* 主键查询文档
*/
@Test
public void testGetById(){
Student student = restTemplate.get("3", Student.class);
System.out.println(student);
}
(2)全字段搜索
/**
* querystring 搜索。所有的搜索的套路一致。具体方法如下:
* SearchHits search(Query query, Class<T> clazz);
* 参数query - 具体的条件。是spring data elasticsearch框架封装的类型。
* 一般使用构建器创建搜索条件。
* NativeSearchQueryBuilder().build()
* .withQuery(QueryBuilder) - 提供具体的条件。此方法的参数类型是QueryBuilder
* 这个QueryBuilder类型由elasticsearch提供的java客户端jar包定义。
* QueryBuilder对象,一般使用构建器创建,QueryBuilders
* 参数clazz - 返回结果每个document封装的对象类型。
* 返回return - 搜索的结果。是一个完整的搜索结果对象。包含总数据个数,当前搜索集合,是否超时等。
*
* SearchHits类型实现了Iterable接口,可迭代。容器中每个对象的类型是SearchHit。
* SearchHit类型中包含一个完整的搜索对象,由元数据和源数据组成。
*/
@Test
public void testQuerystring(){
QueryBuilder queryBuilder =
QueryBuilders.queryStringQuery("郭");
Query query =
new NativeSearchQueryBuilder()
.withQuery(queryBuilder) // 提供具体的条件。
.build();
SearchHits<Student> hits =
restTemplate.search(query, Student.class);
System.out.println("搜索结果数据个数是: " + hits.getTotalHits());
for(SearchHit<Student> hit : hits){
// 源数据,就是保存在Elasticsearch中的数据
Student content = hit.getContent();
System.out.println("源数据:" + content);
}
}
(3)搜索全部
/**
* 搜索全部
*/
@Test
public void testMatchAll(){
SearchHits<Student> hits = restTemplate.search(
new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.build(), Student.class);
for(SearchHit<Student> hit : hits){
System.out.println(hit.getContent());
}
}
(4)匹配搜索
/**
* match搜索
*/
@Test
public void testQueryByMatch(){
SearchHits<Student> hits = restTemplate.search(
new NativeSearchQueryBuilder()
.withQuery(
QueryBuilders.matchQuery(
"name",
"于谦")
)
.build(),
Student.class
);
print(hits);
}
private void print(SearchHits<Student> hits){
for(SearchHit<Student> hit : hits){
System.out.println(hit.getContent());
}
}
(5)短语搜索
/**
* match phrase搜索
*/
@Test
public void testQueryByMatchPhrase(){
SearchHits<Student> hits = restTemplate.search(
new NativeSearchQueryBuilder()
.withQuery(
QueryBuilders.matchPhraseQuery(
"hobbies",
"烫头"
)
)
.build(),
Student.class
);
print(hits);
}
(6)范围搜索
/**
* range搜索
*/
@Test
public void testQueryByRange(){
SearchHits<Student> hits =
restTemplate.search(
new NativeSearchQueryBuilder()
.withQuery(
QueryBuilders.rangeQuery("age")
.lte(35).gte(30)
)
.build(),
Student.class
);
print(hits);
}
(7)范围搜索
/**
* bool搜索。多条件
*/
@Test
public void testQueryByBool(){
/*
* bool搜索条件中包含3个条件集合。List<QueryBuilder>,分别命名为must(必须匹配的条件),mustNot(必须不匹配的条件),should(任意匹配的条件)。
* 每个条件集合管理方案都提供了两个方法API。以must举例如下:
* must() - 获取must必要条件集合,返回List<QueryBuilder>。
* must(QueryBuilder) - 把参数添加到must条件集合中,返回BoolQueryBuilder类型对象,即当前对象本身(this)。
*/
BoolQueryBuilder queryBuilder =
QueryBuilders.boolQuery();
List<QueryBuilder> must = queryBuilder.must();
must.add(QueryBuilders.matchQuery("name", "郭"));
queryBuilder.should(QueryBuilders.rangeQuery("age").gt(30))
.should(QueryBuilders.rangeQuery("age").lt(60));
// 搜索
SearchHits<Student> hits =
restTemplate.search(
new NativeSearchQueryBuilder()
.withQuery(queryBuilder)
.build(),
Student.class
);
print(hits);
}
(8)分页和排序
/**
* 分页+排序
* 在spring data系列框架中,分页和排序可以混合处理。使用通用定义spring data commons中的Page接口实现。应用接口的实现类型PageRequest处理。
* PageRequest类型中,提供静态方法of(int page, int size[, Sort sort]);
* page - 查询第几页,页码数字从0开始计数。
* size - 查询多少行。
* Sort - 具体的排序规则。可选参数。
*/
@Test
public void testQueryByPageAndSort(){
SearchHits<Student> hits =
restTemplate.search(
new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withPageable(
PageRequest.of(0, 2,
Sort.by(
Sort.Order.desc("age"),
Sort.Order.asc("id")
)
)
)
.build(),
Student.class
);
print(hits);
}
(9)高亮处理
/**
* 搜索结果高亮处理
* 提供高亮字段,字段名,是否分段(每段几个字符,返回几段),前后缀。
* 创建Query对象,做搜索处理。
*/
@Test
public void testQueryHighLight(){
// 创建高亮字段,必须和具体的字段名绑定。
HighlightBuilder.Field field1 = new HighlightBuilder.Field("name");
// 高亮前缀
field1.preTags("<span style='color: red'>");
// 高亮后缀
field1.postTags("</span>");
// 分段的每段字符数
field1.fragmentSize(Integer.MAX_VALUE);
// 分段后,返回几段
field1.numOfFragments(1);
Query query =
new NativeSearchQueryBuilder()
.withQuery(
QueryBuilders.matchQuery(
"name",
"于谦")
)
.withHighlightFields(field1)
.build();
SearchHits<Student> hits =
restTemplate.search(query, Student.class);
for (SearchHit<Student> hit : hits){
// 获取源数据
Student content = hit.getContent();
// 找高亮数据
List<String> hl = hit.getHighlightField("name");
// 判断是否有高亮数据
if(hl != null && hl.size() > 0){
// 有高亮数据
(hl.get(0));
}
System.out.println(content);
}
}