首先要明确通过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
1、引入依赖,只关注springboot和es的部分就行
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>elk</artifactId>
<version>1.0-SNAPSHOT</version>
<name>elk</name>
<description>Demo project for Spring Boot</description>
<properties>
<maven-jar-plugin.version>3.0.0</maven-jar-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<mybatisplus.version>3.3.1</mybatisplus.version>
<mysql.version>8.0.17</mysql.version>
<mssql.version>4.0</mssql.version>
<oracle.version>11.2.0.3</oracle.version>
<druid.version>1.1.13</druid.version>
<quartz.version>2.3.0</quartz.version>
<***mons.lang.version>2.6</***mons.lang.version>
<***mons.fileupload.version>1.2.2</***mons.fileupload.version>
<***mons.io.version>2.5</***mons.io.version>
<***mons.codec.version>1.10</***mons.codec.version>
<***mons.configuration.version>1.10</***mons.configuration.version>
<shiro.version>1.4.0</shiro.version>
<jwt.version>0.7.0</jwt.version>
<kaptcha.version>0.0.9</kaptcha.version>
<qiniu.version>7.2.23</qiniu.version>
<aliyun.oss.version>2.8.3</aliyun.oss.version>
<qcloud.cos.version>4.4</qcloud.cos.version>
<swagger.version>2.4.0</swagger.version>
<joda.time.version>2.9.9</joda.time.version>
<gson.version>2.8.5</gson.version>
<fastjson.version>1.2.60</fastjson.version>
<hutool.version>5.0.6</hutool.version>
<lombok.version>1.18.4</lombok.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.8</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!-- Kafka资源的引入 -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</dependency>
<dependency>
<groupId>***.github.danielwegener</groupId>
<artifactId>logback-kafka-appender</artifactId>
<version>0.2.0-RC1</version>
</dependency>
<dependency>
<groupId>***.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.4</version>
</dependency>
<!--es-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--json-->
<dependency>
<groupId>***.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>***.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
<version>2.8</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
<!-- 跳过单元测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、修改配置文件
# 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: /
3、定义实体类
package ***.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;
}
4、定义基础操作接口:InvitationRepository
package ***.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<Invitation, Long> {
//根据名字查询 findBy+字段名
List<Invitation> findByName(String name);
//根据地址查询 findBy+字段名
List<Invitation> findByAddress(String address);
//根据地址和姓名查询 findBy+多个字段名之间And分隔
List<Invitation> findByAddressAndName(String address,String name);
//查询id小于某个值的数据 findBy+比大小的字段+LessThan
List<Invitation> findByIdLessThan(int id);
//查询年龄在多少-多少之间的 findBy+条件字段+Between
List<Invitation> findByAgeBetween(Integer minAge,Integer maxAge);
}
5、定义索引管理类:IndexManage
package ***.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;
}
}
6、测试类
package ***.elk.escurd;
import ***.alibaba.fastjson.JSON;
import org.elasticsearch.***mon.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() {
Iterable<Invitation> invitationIterable = 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<Invitation> 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<Invitation> hits = elasticsearchRestTemplate.search(query, Invitation.class);
List<SearchHit<Invitation>> 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<Invitation> hits = elasticsearchRestTemplate.search(searchQuery, Invitation.class);
List<SearchHit<Invitation>> searchHits = hits.getSearchHits();
System.out.println("共" + hits.getTotalHits() + "条");
searchHits.forEach(x -> System.out.println(JSON.toJSONString(x.getContent())));
}
}