Java操作ElasticSearch有两种方式:
a、ElasticSearch提供的客户端API
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>transport</artifactId><version>7.6.2</version></dependency>
b、Spring Data ElasticSearch
使用Spring Data 下二级子项目Spring Data Elasticsearch进行操作。支持POJO方法操作Elasticsearch。相比Elasticsearch提供的API更加简单更加方便。
在这里我们使用SpringData
1、SpringData ElasticSearch 项目环境搭建
1-1 创建项目
1-2 修改pom.xml
使用spring-boot-starter-parent版本为2.3.3.RELEASE,对应spring-data-elasticsearch版本为4.0.3
在项目pom.xml中添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>
1-3 修改配置文件
在老版本中通过9300内部端口访问。通过TransportClient进行访问的。
从Elasticsearch 8.x开始要放弃Transport。
所以从Spring Data Elasticsearch 4.0 开始都是基于Rest进行访问。
spring:elasticsearch:rest:uris: http://192.168.163.12:9200
1-4 新建索引和指定mapping

package com.alan.es.pojo;import lombok.Data;import org.springframework.data.annotation.Id;import org.springframework.data.elasticsearch.annotations.Document;import org.springframework.data.elasticsearch.annotations.Field;import org.springframework.data.elasticsearch.annotations.FieldType;/*** 商品实体类* 自定义mapping关系是通过实体类进行控制的* @Document 注解 org.springframework.data.elasticsearch.annotations* 定义类和索引的关系*/@Document(indexName = "index_item",shards = 1,replicas = 1)@Data@NoArgsConstructor@AllArgsConstructorpublic class Item {/*** 在SpringData ES中要求所有的属性都必须要有对应注解,否则自动映射*/@Id//ID起名“id”和“_id” 都可以private String id;/*非主键都用Field注解如果希望Document和实体类中都叫做 title,那么 @Field(value = "")value不用填值,如果希望名字不一样,那就需要填值*/@Field(type = FieldType.Text,analyzer = "ik_max_word")private String title;@Field(type = FieldType.Long)private Long price;@Field(type = FieldType.Keyword)private String catName;}
@Document指定实体类和索引对应关系indexName:索引名称type: 索引类型(从ES 7.0 开始已经过时了)shards: 主分片数量。从ES 7开始默认1replicas:复制分片数量。从ES 7开始默认1@Id 指定主键@Field指定普通属性type: 对应Elasticsearch中属性类型。使用FiledType枚举可以快速获取。测试发现没有type属性可能出现无法自动创建类型问题,所以一定要有type属性。text类型能被分词keywords不能被分词index: 是否创建索引。作为搜索条件时index必须为trueanalyzer:指定分词器类型。
2、 ElasticsearchTemplate的使用
2-1 初始化索引
@SpringBootTestclass EsDemoApplicationTests {@Autowiredprivate ElasticsearchRestTemplate elasticsearchRestTemplate;@Testvoid contextLoads() {//调用代码,实现映射关系// SpringData 4.x操作索引都是通过IndexOptions进行操作IndexOperations operations = elasticsearchRestTemplate.indexOps(Item.class);//创建索引operations.create();//createMapping 根据实体类获取映射关系//putMapping 把映射关系添加到索引中Document mapping = operations.createMapping();boolean b = operations.putMapping(mapping);System.out.println("索引创建:" + b);}}
2-2 删除索引
/*** 删除索引*/@Testvoid deleteIndex(){IndexOperations operations = elasticsearchRestTemplate.indexOps(Item.class);operations.delete();}
2-3 添加文档
如果索引和类型不存在,也可以执行新增,新增后自动创建索引和类型。但是field通过动态mapping进行映射,ElasticSearch根据值类型进行判断每个属性类型,默认每个属性都是standard分词器,ik分词器是不生效的。所以一定要先通过diamante进行初始化或者直接通过命令创建所有field的mapping。
2-3-1 新增单个文档
如果对象的id属性没有赋值,让ES自动生成主键,存储时ID属性没有值,_Id存储document的主键值。
如果存储对象的id属性明确设置了值,存储时id属性为设置的值,ES中document对象的_id也是设置的值。
/*** 新增单个文档*/@Testvoid addDocument(){Item item = new Item("id", "标题", 1110000l, "catName");Item save = elasticsearchRestTemplate.save(item);System.out.println("新增单个文档:"+save);}/*** 新增单个文档(不指定ID)*/@Testvoid addDocument(){Item item = new Item(null, "标题", 1110000l, "catName");Item save = elasticsearchRestTemplate.save(item);System.out.println("新增单个文档:"+save);}//输出:新增单个文档:Item{id='BiAkpIAB5cT0lDi1vRvj', title='标题', price=1110000, catName='catName'}
2-3-2 批量新增
/*** 批量新增文档*/@Testvoid addDocuments(){List<Item> list = new ArrayList<>();list.add(new Item("id0000002","标题2",12l,"catName2"));list.add(new Item("id0000003","标题3",13l,"catName3"));list.add(new Item("id0000004","华为手机",4999l,"华为手机!功能牛逼!"));list.add(new Item("id0000005","小米手机",4999l,"小米手机,为发烧而生!"));Iterable<Item> save = elasticsearchRestTemplate.save(list);System.out.println("批量新增文档:"+save);}
2-4 删除文档
/*** 删除文档*/@Testvoid deleteDocument(){String id = elasticsearchRestTemplate.delete("id", Item.class);System.out.println("删除文档:"+id);}
2-5 修改文档
修改的API就是新增的API,只要保证主键ID是已存在的ID,那么新增就是修改
2-6 查询
2.6-1 根据主键查询
/*** 根据主键查询*/@Testvoid searchById(){Item res = elasticsearchRestTemplate.get("id0000002", Item.class);System.out.println("res:"+res);}
2-6-2 模糊查询
/*** 模糊查询* 去所有field中查询指定条件。*/@Testvoid search(){QueryStringQueryBuilder queryBuilder = QueryBuilders.queryStringQuery("标题");Query query = new NativeSearchQuery(queryBuilder);// 相当于最外层hitsSearchHits<Item> searchHits = elasticsearchRestTemplate.search(query,Item.class);// 里层的hitsList<SearchHit<Item>> list = searchHits.getSearchHits();// 进行转换List<Item> listResult = new ArrayList<>();list.forEach(sh -> {listResult.add(sh.getContent());});System.out.println(listResult);}
2-6-3 使用match_all 查询所有文档
/*** 使用match_all 查询所有文档*/@Testvoid matchAllSearch(){Query query = new NativeSearchQuery(QueryBuilders.matchAllQuery());SearchHits<Item> searchHits = elasticsearchRestTemplate.search(query, Item.class);List<Item> listRes = new ArrayList<>();searchHits.forEach(sh->{listRes.add(sh.getContent());});System.out.println("res:"+listRes);}
2-6-4 使用match查询文档
/*** 使用match 查询文档*/@Testvoid matchSearch(){Query query = new NativeSearchQuery(QueryBuilders.matchQuery("title","华为"));SearchHits<Item> searchHits = elasticsearchRestTemplate.search(query, Item.class);List<SearchHit<Item>> searchHitList = searchHits.getSearchHits();List<Item> res = new ArrayList<>();searchHitList.forEach(sh->{res.add(sh.getContent());});System.out.println("res:"+res);}
2-6-5 使用 match_phrase 查询文档
短语搜索是对条件不分词,但是文档中属性根据配置实体类时指定的分词类型进行分词。
如果属性使用ik分词器,从分词后的索引数据进行匹配。
/*** 短语搜索是对条件不分词,但是文档中属性根据配置实体类时指定的分词类型进行分词。* 如果属性使用ik分词器,从分词后的索引数据进行匹配。*/@Testvoid matchPhrase(){Query searchQuery = new NativeSearchQuery(QueryBuilders.matchPhraseQuery("title","手机"));SearchHits<Item> searchHits = elasticsearchRestTemplate.search(searchQuery, Item.class);List<SearchHit<Item>> searchHitList = searchHits.getSearchHits();List<Item> res = new ArrayList<>();searchHitList.forEach(sh->{res.add(sh.getContent());});System.out.println("res:"+res);}
2-6-6 使用range查询文档
@Testvoid range(){Query searchQuery = new NativeSearchQuery(QueryBuilders.rangeQuery("price").gte(100).lte(5000));SearchHits<Item> searchHits = elasticsearchRestTemplate.search(searchQuery, Item.class);List<SearchHit<Item>> searchHitList = searchHits.getSearchHits();List<Item> res = new ArrayList<>();searchHitList.forEach(sh->{res.add(sh.getContent());});System.out.println("res:"+res);}
2-6-7 多条件查询
@Testvoid mustShould(){BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();List<QueryBuilder> listQuery = new ArrayList<>();listQuery.add(QueryBuilders.matchPhraseQuery("title","手机"));listQuery.add(QueryBuilders.rangeQuery("price").gte(1000).lte(6000));// boolQueryBuilder.should().addAll(listQuery);// ||boolQueryBuilder.must().addAll(listQuery);// &&Query query = new NativeSearchQuery(boolQueryBuilder);SearchHits<Item> search = elasticsearchRestTemplate.search(query, Item.class);List<Item> res = new ArrayList<>();for (SearchHit<Item> searchHit : search.getSearchHits()) {res.add(searchHit.getContent());}System.out.println("res:"+res);}
2-6-8 分页与排序
/*** 分页与排序*/@Testvoid PageSort(){Query query = new NativeSearchQuery(QueryBuilders.matchAllQuery());query.setPageable(PageRequest.of(0,2));query.addSort(Sort.by(Sort.Direction.DESC,"price"));SearchHits<Item> search = elasticsearchRestTemplate.search(query, Item.class);for (SearchHit<Item> searchHit : search.getSearchHits()) {System.out.println("res:"+searchHit.getContent());}}
如果实体类中主键只有@Id注解,String id对应ES中是text类型,text类型是不允许被排序,所以如果必须按照主键进行排序时需要在实体类中设置主键类型<br />
2-6-9 高亮显示
@Testvoid highLight(){NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(QueryBuilders.matchQuery("catName", "手机"));// 排序nativeSearchQuery.addSort(Sort.by(Sort.Direction.DESC,"price"));// 分页nativeSearchQuery.setPageable(PageRequest.of(0,2));// 设置高亮条件HighlightBuilder hlBuilder = new HighlightBuilder();// 哪个属性高亮hlBuilder.field("catName");// 高亮内容前缀hlBuilder.preTags("<span style='color:red'>");// 高亮内容后缀hlBuilder.postTags("</span>");// 高亮查询HighlightQuery hlQuery = new HighlightQuery(hlBuilder);// 应用高亮nativeSearchQuery.setHighlightQuery(hlQuery);// 外层hitsSearchHits<Item> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Item.class);// 查询出的总条数System.out.println(searchHits.getTotalHits());// 里层hitsList<SearchHit<Item>> searchHitList = searchHits.getSearchHits();List<Item> list = new ArrayList<>();searchHitList.forEach(sh ->{// 取出非高亮数据Item peo = sh.getContent();// 获取高亮数据String hlContent = sh.getHighlightField("catName").get(0);// 用高亮数据替换非高亮数据peo.setCatName(hlContent);list.add(peo);});System.out.println(list);}


