首先要明确通过springdata操作es必须要将版本号和es的版本号对应上,否则会报错(倒不用完全一一对应,但版本号最好不要相差太多)。springdata引入的版本号由springboot的版本号决定,对应关系如下:
这里我用的版本号分别是:
es:elasticsearch:7.10.1
springboot:spring-boot-starter-parent:2.7.8
springdata:spring-boot-starter-data-elasticsearch:2.7.8
4.0.0 org.example elk 1.0-SNAPSHOT elk Demo project for Spring Boot 3.0.0 UTF-8 UTF-8 1.8 3.1.1 3.3.1 8.0.17 4.0 11.2.0.3 1.1.13 2.3.0 2.6 1.2.2 2.5 1.10 1.10 1.4.0 0.7.0 0.0.9 7.2.23 2.8.3 4.4 2.4.0 2.9.9 2.8.5 1.2.60 5.0.6 1.18.4 org.springframework.boot spring-boot-starter-parent 2.7.8 org.springframework.boot spring-boot-starter-test org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop org.springframework spring-context-support org.springframework.boot spring-boot-starter-data-redis redis.clients jedis org.projectlombok lombok 1.18.4 org.apache.kafka kafka-clients com.github.danielwegener logback-kafka-appender 0.2.0-RC1 net.logstash.logback logstash-logback-encoder 6.4 org.springframework.boot spring-boot-starter-data-elasticsearch com.alibaba fastjson ${fastjson.version} net.sf.json-lib json-lib 2.4 jdk15 ${project.artifactId} org.apache.maven.wagon wagon-ssh 2.8 org.springframework.boot spring-boot-maven-plugin true org.apache.maven.plugins maven-surefire-plugin true
# es连接地址 spring: elasticsearch: uris: 192.168.3.22:9200 #如果是集群,用“,”分割 data: elasticsearch: repositories: enabled: true # Tomcat server: tomcat: uri-encoding: UTF-8 max-threads: 1000 min-spare-threads: 30 port: 8087 connection-timeout: 5000ms servlet: context-path: /
package com.elk.escurd; 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; import org.springframework.data.elasticsearch.annotations.GeoPointField; import org.springframework.data.elasticsearch.core.geo.GeoPoint; import java.util.Date; @Data @Document(indexName = "invitation",createIndex = true) public class Invitation { @Id private Long id; // //指定字段的索引方式,index是否索引、store是否存储、字段的分词方式、搜索时关键字分词的方式、type指定该字段的值以什么样的数据类型来存储 // @Field(index=true,store=true,analyzer="ik_max_word",searchAnalyzer="ik_max_word",type=FieldType.Text) /* ik_smart:粗粒度分词 */ @Field(analyzer = "ik_smart", type = FieldType.Text) private String name; /* ik_max_word:细粒度分词 */ @Field(analyzer = "ik_max_word", type = FieldType.Text) private String country; @Field(type = FieldType.Integer) private Integer age; @Field(type = FieldType.Text) private String isDelete; @Field(type = FieldType.Text) private String status; @Field(type = FieldType.Text) private String sex; @Field(type = FieldType.Text) private String type; @Field(type = FieldType.Date) private Date createDate; //es中的位置字段,存储的是经纬度,方便进行范围搜索 @GeoPointField private GeoPoint address; }
package com.elk.escurd; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import org.springframework.stereotype.Repository; import java.util.List; /** * 此类和@Document中的createIndex = true配合,启动就可以将实体类变成索引注入到es中 * 不需要写具体的实现,函数名遵循命名规范即可自动实现 * 命名规则参考:https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#repositories.query-methods * 此类完成的是一些基础查询,复杂查询用ElasticsearchRestTemplate */ @Repository public interface InvitationRepository extends ElasticsearchRepository{ //根据名字查询 findBy+字段名 List findByName(String name); //根据地址查询 findBy+字段名 List findByAddress(String address); //根据地址和姓名查询 findBy+多个字段名之间And分隔 List findByAddressAndName(String address,String name); //查询id小于某个值的数据 findBy+比大小的字段+LessThan List findByIdLessThan(int id); //查询年龄在多少-多少之间的 findBy+条件字段+Between List findByAgeBetween(Integer minAge,Integer maxAge); }
package com.elk.escurd; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.IndexOperations; import org.springframework.data.elasticsearch.core.document.Document; import org.springframework.stereotype.Service; /** * 索引管理类 */ @Service public class IndexManage { @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; /** * 创建索引和mapping * es不支持修改mapping,如果想要修改mapping,只能备份原来的数据,删除原有索引重新创建 */ // @PostConstruct public boolean createIndex(){ IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Invitation.class); //如果存在索引,先删除索引 if (indexOperations.exists()){ indexOperations.delete(); } //创建索引 boolean a = indexOperations.create(); if (a){ //生成映射 Document mapping = indexOperations.createMapping(); //推送映射 boolean b = indexOperations.putMapping(mapping); return b; }else { return a; } } public boolean deleteIndex() { IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Invitation.class); boolean delete = indexOperations.delete(); return delete; } }
package com.elk.escurd; import com.alibaba.fastjson.JSON; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.query.*; import org.elasticsearch.search.sort.GeoDistanceSortBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import java.util.Date; import java.util.List; /** * @author 954L * @create 2023/2/8 15:13 */ @SpringBootTest class InvitationTest { /** * 基础操作 */ @Autowired private InvitationRepository invitationRepository; /** * 复杂操作 */ @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate; /** * 测试新增 */ @Test void testInsert() { Invitation invitation = new Invitation(); invitation.setId(3l); invitation.setName("龟儿子"); invitation.setCountry("我是美国佬"); invitation.setAge(20); invitation.setIsDelete("0"); invitation.setStatus("0"); invitation.setSex("2"); invitation.setType("2"); invitation.setCreateDate(new Date()); GeoPoint address =new GeoPoint(41.15998589289666,123.10144051130709); invitation.setAddress(address); invitationRepository.save(invitation); } /** * 查所有数据 */ @Test void testFindAll() { IterableinvitationIterable = invitationRepository.findAll(); invitationIterable.forEach(x -> System.out.println(JSON.toJSONString(x))); } /** * 修改指定数据 */ @Test void testUpdate() { Invitation invitation = invitationRepository.findById(1L).orElse(null); System.out.println("修改前名称:" + invitation.getName()); invitation.setName("龟儿子2"); invitationRepository.save(invitation); invitation = invitationRepository.findById(1L).orElse(null); System.out.println("修改后名称:" + invitation.getName()); } /** * 删除指定数据 */ @Test void testDelete() { invitationRepository.deleteById(1L); Invitation invitation = invitationRepository.findById(1L).orElse(null); System.out.println(invitation == null? "删除成功": "删除失败"); } /** * 通过name查询 */ @Test void testfindByName(){ List invitationList = invitationRepository.findByName("儿子"); invitationList.forEach(x -> System.out.println(JSON.toJSONString(x))); } /** * 通过name查询 */ @Test void testfindByName2() { MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("name", "儿子"); NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(matchQueryBuilder) .withFields("id", "name", "country").build(); SearchHits hits = elasticsearchRestTemplate.search(query, Invitation.class); List > searchHits = hits.getSearchHits(); System.out.println("共" + hits.getTotalHits() + "条"); searchHits.forEach(x -> System.out.println(JSON.toJSONString(x.getContent()))); } /** * 复杂查询:通过范围、性别、年龄、类型、按距离升序、按时间倒序分页查询 * @return */ @Test void getInvitationList(){ Integer page = 1; Integer size = 5; Double latitude = 41.1637913541259; Double longitude = 123.10181515177084; String sex = "3"; Integer minage = 5; Integer maxage = 25; String type = "10"; Pageable pageable = PageRequest.of(page - 1, size); //构建查询条件生成器 NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); //拼接条件 //指定字段范围查询 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("age").gte(minage).lte(maxage)); //指定字段查询 QueryBuilder isdeleteBuilder = QueryBuilders.termQuery("isDelete", "0"); boolQueryBuilder.must(isdeleteBuilder); QueryBuilder statusBuilder = QueryBuilders.termQuery("status","0"); // boolQueryBuilder.mustNot(statusBuilder); boolQueryBuilder.must(statusBuilder); if (!"3".equals(sex)&&null!=sex){ QueryBuilder sexBuilder = QueryBuilders.termQuery("sex",sex); boolQueryBuilder.must(sexBuilder); } if (!"10".equals(type)&&null!=type){ QueryBuilder typeBuilder = QueryBuilders.termQuery("type",type); boolQueryBuilder.must(typeBuilder); } //以某点为中心,搜索指定范围 GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("address"); distanceQueryBuilder.point(latitude, longitude); //查询单位:m distanceQueryBuilder.distance(10000, DistanceUnit.METERS); boolQueryBuilder.filter(distanceQueryBuilder); nativeSearchQueryBuilder.withQuery(boolQueryBuilder); //按距离升序 GeoDistanceSortBuilder distanceSortBuilder = new GeoDistanceSortBuilder("address", latitude, longitude); distanceSortBuilder.unit(DistanceUnit.KILOMETERS); distanceSortBuilder.order(SortOrder.ASC); nativeSearchQueryBuilder.withSort(distanceSortBuilder); //按时间倒序 SortBuilder timeSort = SortBuilders.fieldSort("createDate").order(SortOrder.DESC); nativeSearchQueryBuilder.withSort(timeSort); //分页 nativeSearchQueryBuilder.withPageable(pageable); NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build(); SearchHits hits = elasticsearchRestTemplate.search(searchQuery, Invitation.class); List > searchHits = hits.getSearchHits(); System.out.println("共" + hits.getTotalHits() + "条"); searchHits.forEach(x -> System.out.println(JSON.toJSONString(x.getContent()))); } }