一、 Java操作Elaticsearch
1、 Elasticsearch提供的客户端API
依赖的名称叫做transport,rest-high-level。依赖的版本和Elasticsearch的版本是对应的。
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>7.6.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
</dependency>
2 、Spring Data Elasticsearch
使用Spring Data 下二级子项目Spring Data Elasticsearch进行操作。支持POJO方法操作Elasticsearch。相比Elasticsearch提供的API更加简单更加方便。
二、 Spring Data Elasticsearch项目环境搭建
1、 创建项目
2、 修改POM文件添加依赖
目前使用spring-boot-starter-parent版本为2.3.9RELEASE
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
3、 修改配置文件
在老版本中通过9300内部端口访问。通过TransportClient进行访问的。
从Elasticsearch 8.x开始要放弃Transport。
所以从Spring Data Elasticsearch 4.0 开始都是基于Rest进行访问。
spring:
elasticsearch:
rest:
uris: http://192.168.80.128:9200
三、 ElasticsearchTemplate的使用
1、创建实体
@Document指定实体类和索引对应关系
indexName:索引名称
type: 索引类型(从ES 7.0 开始已经过时了)
shards: 主分片数量。从ES 7开始默认1
replicas:复制分片数量。从ES 7开始默认1
@Id 指定主键
@Field指定普通属性
type: 对应Elasticsearch中属性类型。使用FiledType枚举可以快速获取。测试发现没有type属性可能出现无法自动创建类型问题,所以一定要有type属性。
text类型能被分词
keywords不能被分词
index: 是否创建索引。作为搜索条件时index必须为true
analyzer:指定分词器类型。
/**
* indexName:索引的名称
* shards:主分片的数量默认是1
* replicas:副本分片的数量 默认是1
* */
@NoArgsConstructor
@AllArgsConstructor
@Data
@Document(indexName = "stu_index",shards = 1,replicas = 0)
public class Student implements Serializable {
@Id
@Field(type = FieldType.Long,name = "id")
private Long id;
@Field(type = FieldType.Text,name = "name",analyzer = "ik_smart")
private String name;
@Field(type = FieldType.Integer,name = "age")
private Integer age;
@Field(type = FieldType.Keyword,name = "sex")
private String sex;
@Field(type = FieldType.Date,format = DateFormat.date,name = "birth")
private Date birth;
@Field(type = FieldType.Boolean,name = "isFlag")
private Boolean isFlag;
@Field(type = FieldType.Text,name = "hobby",analyzer = "ik_smart")
private String[] hobby;
}
2、初始化索引
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
//ES虽然提供了代码方式添加和删除索引 但是我们一般不使用,我们会在kibana中直接创建
@Test
void stuDemo(){
//创建索引
IndexOperations indexOperations = esTemplate.indexOps(Student.class);
//创建当前索引
indexOperations.create();
//createMapping:根据指定对象创建Mapping映射
//putMapping:给指定索引追加mapping关系
indexOperations.putMapping(indexOperations.createMapping(Student.class));
}
}
3、删除索引
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
//ES虽然提供了代码方式添加和删除索引 但是我们一般不使用,我们会在kibana中直接创建
@Test
void stuDelDemo(){
//删除指定的索引
IndexOperations indexOperations = esTemplate.indexOps(Student.class);
indexOperations.delete();
}
}
4、添加文档
如果索引和类型不存在,也可以执行进行新增,新增后自动创建索引和类型。但是field通过动态mapping进行映射,elaticsearch根据值类型进行判断每个属性类型,默认每个属性都是standard分词器,ik分词器是不生效的。所以一定要先通过代码进行初始化或直接在elasticsearch中通过命令创建所有field的mapping
4.1 新增单个文档
如果对象的id属性没有赋值,让ES自动生成主键,存储时id属性没有值,_id存储document的主键值。
如果对象的id属性明确设置值,存储时id属性为设置的值,ES中document对象的_id也是设置的值。
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
/*------------------------------ES文档的CURD操作--------------------------------------*/
@Test
void docAddDemo(){
//如果当前ID属性没有指定ES中对应索引的_ID的值会自动生成,如果我们设置了ID这个时候_id就会使用自定义的id的值
//如果ID相同,这个时候就是一个修改的操作 全量替换
Student student = new Student();
student.setId(1L);
student.setName("郑明月");
student.setAge(22);
student.setSex("女性");
student.setBirth(new Date());
student.setIsFlag(false);
student.setHobby(new String[]{"联盟","炉石"});
Student stu1 = esTemplate.save(student);
//添加文档
System.out.println(stu1);
}
4.2 批量新增
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docAddMore(){
//批量添加文档
Student student = new Student();
student.setId(2L);
student.setName("王旭");
student.setAge(20);
student.setSex("男性");
student.setBirth(new Date());
student.setIsFlag(true);
student.setHobby(new String[]{"联盟","换装"});
Student stu1 = esTemplate.save(student);
Student student2 = new Student();
student2.setId(3L);
student2.setName("泰旭");
student2.setAge(23);
student2.setSex("男性");
student2.setBirth(new Date());
student2.setIsFlag(false);
student2.setHobby(new String[]{"联盟","韩剧"});
Student stu2 = esTemplate.save(student2);
List<Student> list=new ArrayList<>();
list.add(stu1);
list.add(stu2);
Iterable<Student> students = esTemplate.save(list);
System.out.println(students);
}
}
5、删除操作
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
/*删除的实现*/
@Test
void docDeleteDemo(){
//返回的是当前删除元素的ID
String s = esTemplate.delete("3", Student.class);
//esTemplate.delete("3", IndexCoordinates.of("stu_index"));
System.out.println(s);
}
}
6、修改操作
修改操作就是新增代码,只要保证主键id已经存在,新增就是修改
修改部分数据如下操作:
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
/*
* 修改操作:
* 【A】:全量修改 save()操作
* 【B】:部分修改 update()操作
* */
@Test
void docUpdateDemo(){
UpdateQuery query = UpdateQuery
.builder("1")//修改索引的ID
.withDocument(Document.parse("{\"age\":23,\"sex\":\"男\"}"))//修改的字段 如果有多个使用Json拼接
.build();
UpdateResponse a = esTemplate.update(query, IndexCoordinates.of("stu_index"));
System.out.println(a.getResult());//执行结果的类型为Update
}
}
7、查询操作
7.1 根据主键查询
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
/*查询的实现*/
@Test
void docFindDemo(){
//只可以根据ID查询
Student student = esTemplate.get("1", Student.class);
System.out.println(student);
}
}
7.2 全字段模糊查询
去所有field中查询指定条件。
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
/*------------基于query String search进行查询-------------*/
@Test
void docFindDemo02(){
//查询的条件
Query query = new NativeSearchQuery(QueryBuilders.queryStringQuery("name:'旭'"));
SearchHits<Student> search = esTemplate.search(query, Student.class);
/* List<SearchHit<Student>> searchHits = search.getSearchHits();
List<Student> list=new ArrayList<>();
for (SearchHit<Student> searchHit : searchHits) {
list.add(searchHit.getContent());
}*/
List<Student> list = QueryESUtil.QueryES(search);
System.out.println(list);
}
}
抽取其中集合添加的操作为com.bjsxt.util.QueryESUtil方法,如下
public class QueryESUtil {
public static<T> List<T> QueryES(SearchHits<T> search){
List<SearchHit<T>> searchHits = search.getSearchHits();
List<T> list=new ArrayList<>();
for (SearchHit<T> searchHit : searchHits) {
list.add(searchHit.getContent());
}
return list;
}
}
7.3 使用match_all查询所有文档
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docSearchDemo(){
//match_all查询所有文档
Query query = new NativeSearchQuery(QueryBuilders.matchAllQuery());
SearchHits<Student> search = esTemplate.search(query, Student.class);
List<Student> list = QueryESUtil.QueryES(search);
for (Student student : list) {
System.out.println(student);
}
}
}
9.4 使用match查询文档
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docSearchDemo2(){
//match具体匹配某一个数据
Query query=new NativeSearchQuery(QueryBuilders.matchQuery("name","王旭"));
SearchHits<Student> search = esTemplate.search(query, Student.class);
List<Student> list = QueryESUtil.QueryES(search);
for (Student student : list) {
System.out.println(student);
}
}
}
7.5 使用match_phrase查询文档
短语搜索是对条件不分词,但是文档中属性根据配置实体类时指定的分词类型进行分词。
如果属性使用ik分词器,从分词后的索引数据中进行匹配。
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docSearchDemo3(){
//match_phrase用户输入不拆分查询
Query query=new NativeSearchQuery(QueryBuilders.matchPhraseQuery("name","王旭"));
SearchHits<Student> search = esTemplate.search(query, Student.class);
List<Student> list = QueryESUtil.QueryES(search);
for (Student student : list) {
System.out.println(student);
}
}
}
7.6 使用range查询文档
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docSearchDemo4(){
//range范围查询
Query query=new NativeSearchQuery(QueryBuilders.rangeQuery("age").gte(21).lte(30));
SearchHits<Student> search = esTemplate.search(query, Student.class);
List<Student> list = QueryESUtil.QueryES(search);
for (Student student : list) {
System.out.println(student);
}
}
}
7.7 多条件查询
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docSearchDemo5(){
//多条件查询 查询姓名中含有旭并且年龄范围在21岁到25岁之间的数据
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//存放每一个条件
List<QueryBuilder> listQuery = new ArrayList<>();
listQuery.add(QueryBuilders.matchQuery("name","旭"));
listQuery.add(QueryBuilders.rangeQuery("age").gte(21).lte(25));
//多个条件的组合放到list集合中
boolQueryBuilder.must().addAll(listQuery);
boolQueryBuilder.mustNot().addAll(listQuery);
boolQueryBuilder.should().addAll(listQuery);
Query query = new NativeSearchQuery(boolQueryBuilder);
SearchHits<Student> search = esTemplate.search(query, Student.class);
List<Student> list = QueryESUtil.QueryES(search);
//遍历students中的数据
for (Student student : list) {
System.out.println(student);
}
}
}
7.8 分页与排序
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docSearchDemo6(){
//分页和排序
Query query=new NativeSearchQuery(QueryBuilders.matchAllQuery());
//设置分页的条件 注意 第一个参数代表的是页数(从0开始 0代表的是第一页)
// 第二个参数代表的是每页条数
// 我们进行分页查询的时候 给前台响应的数据 也要包含总条数 search.getTotalHits();
query.setPageable(PageRequest.of(0,2));
//排序的实现
query.addSort(Sort.by(Sort.Direction.DESC,"age"));
SearchHits<Student> search = esTemplate.search(query, Student.class);
//search.getTotalHits();
List<Student> list = QueryESUtil.QueryES(search);
for (Student student : list) {
System.out.println(student);
}
}
}
如果实体类中主键只有@Id注解,String id对应ES中是text类型,text类型是不允许被排序,所以如果必须按照主键进行排序时需要在实体类中设置主键类型
@Id @Field(type = FieldType.Keyword) private String id; |
---|
7.9 高亮查询
@SpringBootTest
class SpringdateEsApplicationTests {
/**
* ElasticsearchTemplate:底层基于transport 已经过时
* ElasticsearchRestTemplate:底层基于rest-high-level
*/
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void docSearchDemo7(){
//高亮查询
Query query=new NativeSearchQuery(QueryBuilders.matchQuery("name","泰旭"));
//设置高亮的条件
HighlightBuilder highlightBuilder = new HighlightBuilder();
//高亮的字段
highlightBuilder.field("name");
//高亮字段的前缀
highlightBuilder.preTags("<span style='color:red'>");
//高亮字段的后缀
highlightBuilder.postTags("</span>");
//设置高亮查询
HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
//把高亮查询设置到query对象中
query.setHighlightQuery(highlightQuery);
SearchHits<Student> search = esTemplate.search(query, Student.class);
List<Student> students=new ArrayList<>();
for (SearchHit<Student> searchHit : search) {
Student student = searchHit.getContent();
List<String> name = searchHit.getHighlightField("name");
//把之前的name进行高亮替换
student.setName(name.get(0));
students.add(student);
}
for (Student student : students) {
System.out.println(student);
}
}
}