前言
本文介绍SpringBoot2.3整合Elasticsearch7.x完整版流程
废话不多说,直接开始
准备
1、首先确保安装docker和docker-compose,如还未安装docker以及docker-compose,请阅读 Centos7.X安装docker及docker-compose 来安装,若已经安装,请继续查看以下教程
2、Springboot版本:2.3.3.RELEASE;Elasticsearch版本:7.7.0 ;Kibana:7.7.0;jdk:1.8
安装Elasticsearch及Kibana
本文介绍以docker-compose方式来安装
mkdir -R /wilton/elasticsearch
cd /wilton/elasticsearch
编辑脚本:vim docker-compose.yml
version: '3'
services:
elasticsearch:
image: elasticsearch:7.7.0
container_name: elasticsearch
environment:
- "cluster.name=elastic" #设置集群名称为elastic
- "discovery.type=single-node" #以单一节点模式启动
- "ES_JAVA_OPTS=-Xms2048m -Xmx4096m" #设置使用jvm内存大小
volumes:
- /wilton/elasticsearch/data/plugins:/usr/share/elasticsearch/plugins #插件文件挂载
- /wilton/elasticsearch/data/data:/usr/share/elasticsearch/data #数据文件挂载
- /wilton/elasticsearch/data/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml #配置文件挂载
ports:
- 9200:9200
- 9300:9300
kibana:
image: kibana:7.7.0
container_name: kibana
links:
- elasticsearch:es #配置elasticsearch域名为es
depends_on:
- elasticsearch #kibana在elasticsearch启动之后再启动
environment:
- "elasticsearch.hosts=http://127.0.0.1:9200" #设置访问elasticsearch的地址
volumes:
- /wilton/kibana/data/config:/usr/share/kibana/config #配置文件挂载
ports:
- 5601:5601
elasticsearch.yml配置文件
cluster.name: "docker-master"
network.host: 0.0.0.0
kibana.yml配置文件
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://127.0.0.1:9200" ]
i18n.locale: "zh-CN"
启动
docker-compose up -d
**访问:**http://localhost:9200/
**访问 kibana:**http://localhost:5601/
SpringBoot整合 Spring Data Elasticsearch
Spring Data Elasticsearch是Spring提供的一种以Spring Data风格来操作数据存储的方式,它可以避免编写大量的样板代码。
SpringBoot版本:2.3
- 添加 pom 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
-
常用注解:
@Document :标示映射到Elasticsearch文档上的领域对象
//标示映射到Elasticsearch文档上的领域对象 public @interface Document { //索引库名次,mysql中数据库的概念 String indexName(); //文档类型,mysql中表的概念 String type() default ""; //默认分片数 short shards() default 5; //默认副本数量 short replicas() default 1; }
@Id :表示是文档的id,文档可以认为是mysql中表行的概念
//表示是文档的id,文档可以认为是mysql中表行的概念 public @interface Id { }
@Filed :文档中字段的类型、是否建立倒排索引、是否进行存储
public @interface Field { //文档中字段的类型 FieldType type() default FieldType.Auto; //是否建立倒排索引 boolean index() default true; //是否进行存储 boolean store() default false; //分词器名次 String analyzer() default ""; }
//为文档自动指定元数据类型 public enum FieldType { Text,//会进行分词并建了索引的字符类型 Integer, Long, Date, Float, Double, Boolean, Object, Auto,//自动判断字段类型 Nested,//嵌套对象类型 Ip, Attachment, Keyword//不会进行分词建立索引的类型 }
修改application.yml配置文件
修改application.yml文件,在spring节点下添加Elasticsearch相关配置。
spring:
data:
elasticsearch:
repositories:
enabled: true
cluster-nodes: 127.0.0.1:9300 # es的连接地址及端口号
cluster-name: elastic # es集群的名称
实体对象
User.java
package cn.wilton.framework.es.document;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.time.LocalDate;
/**
* @Description es人员信息
* @Author: Ranger
* @Date: 2020/12/21 17:04
* @Email: wilton.icp@gmail.com
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Document(indexName = "user",type = "docs",shards = 1,replicas = 0)
public class User {
private static final long serialVersionUID = -1L;
@Id
private Long id;
@Field(type = FieldType.Keyword, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String address;
@Field(type = FieldType.Keyword, analyzer = "ik_max_word")
private String mobile;
@Field(type = FieldType.Keyword, analyzer = "ik_max_word")
private String email;
@Field(type = FieldType.Date, format = DateFormat.date)
private LocalDate birthday;
@Field(type = FieldType.Keyword)
private String idCard;
@Field
private Company company;
}
Company.java
package cn.wilton.framework.es.document;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* @Description
* @Author: Ranger
* @Date: 2020/12/22 10:12
* @Email: wilton.icp@gmail.com
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Company {
@Field(type = FieldType.Keyword, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String address;
@Field(type = FieldType.Keyword, analyzer = "ik_max_word")
private String mobile;
@Field(type = FieldType.Keyword, analyzer = "ik_max_word")
private String email;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String nature;
@Field(type = FieldType.Text)
private String website;
}
官方接口:
ElasticsearchRepository<T, ID>
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.data.elasticsearch.repository;
import org.elasticsearch.index.query.QueryBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.lang.Nullable;
@NoRepositoryBean
public interface ElasticsearchRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
/** @deprecated */
@Deprecated
default <S extends T> S index(S entity) {
return this.save(entity);
}
/** @deprecated */
@Deprecated
<S extends T> S indexWithoutRefresh(S var1);
/** @deprecated */
Iterable<T> search(QueryBuilder var1);
/** @deprecated */
Page<T> search(QueryBuilder var1, Pageable var2);
/** @deprecated */
Page<T> search(Query var1);
Page<T> searchSimilar(T var1, @Nullable String[] var2, Pageable var3);
/** @deprecated */
@Deprecated
void refresh();
}
PagingAndSortingRepository<T, ID>
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.data.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort var1);
Page<T> findAll(Pageable var1);
}
CrudRepository<T, ID>
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.data.repository;
import java.util.Optional;
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1);
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
Optional<T> findById(ID var1);
boolean existsById(ID var1);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable<? extends T> var1);
void deleteAll();
}
自定义Repository
package cn.wilton.framework.es.repository;
import cn.wilton.framework.es.document.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.util.Streamable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* @Description
* @Author: Ranger
* @Date: 2020/12/22 10:21
* @Email: wilton.icp@gmail.com
*/
@Repository
public interface OrderRepository extends ElasticsearchRepository<User,Long> {
/**
* 根据名字查询数量
*
* @param name
* @return
*/
long countByName(String name);
/**
* 根据地址查询
*
* @param address
* @return
*/
long countByAddress(String address);
/**
* 根据名字删除,并返回删除数量
*
* @param name
* @return
*/
long deleteByName(String name);
/**
* 根据名字删除并返回删除对象
*
* @param name
* @return
*/
List<User> removeByName(String name);
/**
* 根据名字查询
*
* @param name
* @return
*/
List<User> findByName(String name);
/**
* 根据eamil和名字查询
*
* @param email
* @param name
* @return
*/
List<User> findByEmailAndName(String email, String name);
/**
* 根据email或者手机号查询
*
* @param email
* @param mobile
* @return
*/
List<User> findDistinctByEmailOrMobile(String email, String mobile);
/**
* 根据名字分页查询
*
* @param name
* @param page
* @return
*/
Page<User> findByName(String name, Pageable page);
/**
* 根据名字按照id倒序排列查询
*
* @param name
* @param page
* @return
*/
Page<User> findByNameOrderByIdDesc(String name, Pageable page);
/**
* 根据company对象下的name属性查询
*
* @param companyName
* @param page
* @return
*/
Page<User> findByCompanyName(String companyName, Pageable page);
/**
* 根据company对象下的name或者根据company对象下的nature属性查询
*
* @param companyName
* @param ompanyNature
* @return
*/
List<User> findByCompanyNameOrCompanyNature(String companyName, String ompanyNature);
/**
* 根据id范围查询
*
* @param start
* @param end
* @return
*/
List<User> findByIdBetween(Long start, Long end);
/**
* id小于参数查询
*
* @param id
* @return
*/
List<User> findByIdLessThan(Long id);
/**
* 名字模糊查询并且根据id范围查询
*
* @param name
* @param from
* @param to
* @return
*/
List<User> findByNameLikeAndIdBetween(String name, Long from, Long to);
/**
* 忽略email大小写查询
*
* @param email
* @return
*/
List<User> findByEmailIgnoreCaseLike(String email);
/**
* 查询前10
*
* @param name
* @param sort
* @return
*/
List<User> findFirst10ByName(String name, Sort sort);
/**
* 查询前3
*
* @param name
* @param pageable
* @return
*/
Slice<User> findTop3ByName(String name, Pageable pageable);
/**
* 根据名字查询前10分页
*
* @param name
* @param pageable
* @return
*/
Page<User> queryFirst10ByName(String name, Pageable pageable);
/**
* 小于参数id查询
*
* @param id
* @return
*/
Streamable<User> queryByIdLessThan(Long id);
/**
* 大于id参数查询
*
* @param id
* @return
*/
Streamable<User> queryByIdGreaterThan(Long id);
/**
* 根据名字异步查询
*
* @param name
* @return
*/
@Async
CompletableFuture<User> findOneByName(String name);
@Query("{\"range\":{\"id\":{\"from\":\"?0\",\"to\":\"?1\"}}}")
List<User> queryByIdSql(Long start, Long end);
@Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
List<User> queryByNameSql(String name);
}
测试接口:
package cn.wilton.framework.es;
import cn.wilton.framework.WiltonElasticsearchApplication;
import cn.wilton.framework.es.document.Company;
import cn.wilton.framework.es.document.User;
import cn.wilton.framework.es.repository.OrderRepository;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.util.Streamable;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
/**
* @Description
* @Author: Ranger
* @Date: 2020/12/22 11:09
* @Email: wilton.icp@gmail.com
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = WiltonElasticsearchApplication.class)
public class EsTest {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ElasticsearchRestTemplate esRestTemplate;
@Test
public void saveTest() {
Company c = new Company("百度在线网络技术(北京)有限公司", "北京市海淀区中关村街道", "18510367878", "123@baidu.com", "互联网",
"https://www.baidu.com");
User p = new User(1L, "李四", "北京市海淀区中关村街道", "165187413544", "123@baidu.com", LocalDate.of(1999, 2, 12),
"321302199902121478", c);
User save = orderRepository.save(p);
log.info("保存后的结果 {} ", JSONObject.toJSONString(save,true));
}
@Test
public void deleteByIdTest() {
long id = 0L;
orderRepository.deleteById(id);
log.info("删除数据id {}", id);
}
@Test
public void findAllSortTest() {
// 排序的列
Sort sort = Sort.by("birthday");
// 升序
log.info("升序 {}", JSONObject.toJSONString(orderRepository.findAll(sort),true));
// sort.descending() 倒序
log.info("倒序 {}", JSONObject.toJSONString(orderRepository.findAll(sort.descending()),true));
}
@Test
public void countByNameTest() {
String name = "良";
long count = orderRepository.countByName(name);
log.info("姓名:{},数量: {}", name, count);
}
@Test
public void countByAddressTest() {
String address = "深圳";
long count = orderRepository.countByAddress(address);
log.info("地址:{},数量: {}", address, count);
}
@Test
public void deleteByNameTest() {
String name = "张四";
long count = orderRepository.deleteByName(name);
log.info("删除姓名:{},删除数量: {}", name, count);
}
@Test
public void findByNameTest() {
String name = "李四";
List<User> list = orderRepository.findByName(name);
log.info("姓名:{},结果: {}", name, JSONObject.toJSONString(list, true));
}
@Test
public void findByEmailAndNameTest() {
String name = "阿良";
String email = "aliang@huawei.com";
List<User> list = orderRepository.findByEmailAndName(email, name);
log.info("email:{},name: {},结果: {}", email, name, JSONObject.toJSONString(list, true));
}
@Test
public void findDistinctByEmailOrMobile() {
String email = "liyiyi@huawei.com";
String mobile = "16518741234";
List<User> list = orderRepository.findDistinctByEmailOrMobile(email, mobile);
log.info("email:{},mobile: {},结果: {}", email, mobile, JSONObject.toJSONString(list, true));
}
@Test
public void findByNamePageTest() {
// 分页查询
String name = "李四";
// 从0开始
int page = 0;
// 必须大于0
int size = 2;
// 排序可以这样
// 方式1
// Sort sort = Sort.by("id");
// PageRequest.of(page, size, sort);
// 方式2
Sort.TypedSort<User> typedSort = Sort.sort(User.class);
Sort sort = typedSort.by(User::getId).ascending().and(typedSort.by(User::getBirthday).descending());
Pageable pageable = PageRequest.of(page, size, sort);
Page<User> result = orderRepository.findByName(name, pageable);
log.info("name: {}, 分页结果: {}", name, JSONObject.toJSONString(result, true));
}
@Test
public void findByNameNoPageTest() {
// 不分页
String name = "李四";
Pageable unpaged = Pageable.unpaged();
Page<User> result = orderRepository.findByName(name, unpaged);
log.info("name: {}, 分页结果: {}", name, JSONObject.toJSONString(result, true));
}
@Test
public void findByNameOrderByIdDescTest() {
String name = "李四";
// 从0开始
int page = 0;
// 必须大于0
int size = 2;
Pageable pageable = PageRequest.of(page, size);
Page<User> result = orderRepository.findByNameOrderByIdDesc(name, pageable);
log.info("name: {}, 分页结果: {}", name, JSONObject.toJSONString(result, true));
}
@Test
public void findByCompanyTest() {
// 从0开始
int page = 0;
// 必须大于0
int size = 2;
Pageable pageable = PageRequest.of(page, size);
String companyName = "华为";
Page<User> result = orderRepository.findByCompanyName(companyName, pageable);
log.info("分页结果: {}", JSONObject.toJSONString(result, true));
}
@Test
public void findByCompanyNameOrCompanyNatureTest() {
String companyName = "度";
String ompanyNature = "硬件";
List<User> list = orderRepository.findByCompanyNameOrCompanyNature(companyName, ompanyNature);
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
@Test
public void findByIdBetweenTest() {
long start = 1L;
long end = 3L;
List<User> list = orderRepository.findByIdBetween(start, end);
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
@Test
public void findByIdLessThanTest() {
long id = 3L;
List<User> list = orderRepository.findByIdLessThan(id);
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
@Test
public void findByIdBetweenAndNameTest() {
long start = 1L;
long end = 5L;
String name = "李";
List<User> list = orderRepository.findByNameLikeAndIdBetween(name, start, end);
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
@Test
public void findByEmailIgnoreCaseTest() {
String email = "Huawei";
List<User> list = orderRepository.findByEmailIgnoreCaseLike(email);
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
@Test
public void streamableTest() {
// 将结果合并
Streamable<User> streamable = orderRepository.queryByIdGreaterThan(5L)
.and(orderRepository.queryByIdLessThan(2L));
List<User> list = streamable.toList();
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
@Test
public void findOneByNameTest() throws InterruptedException, ExecutionException {
String name = "李四";
CompletableFuture<User> future = orderRepository.findOneByName(name);
User User = future.get();
log.info("结果: {}", JSONObject.toJSONString(User, true));
}
@Test
public void queryBySqlTest() {
List<User> list = orderRepository.queryByIdSql(1L, 3L);
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
@Test
public void queryByNameSqlTest() {
List<User> list = orderRepository.queryByNameSql("李四");
log.info("结果: {}", JSONObject.toJSONString(list, true));
}
}
Spring Data Elasticsearch 接口表达式说明
关键字 | 示例 | 查询信息 |
---|---|---|
And | findByNameAndPrice | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }} |
Or | findByNameOrPrice | { “query” : { “bool” : { “should” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }} |
Is | findByName | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }} |
Not | findByNameNot | { “query” : { “bool” : { “must_not” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }} |
Between | findByPriceBetween | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }} |
LessThan | findByPriceLessThan | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : false } } } ] } }} |
LessThanEqual | findByPriceLessThanEqual | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }} |
GreaterThan | findByPriceGreaterThan | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : false, “include_upper” : true } } } ] } }} |
GreaterThanEqual | findByPriceGreaterThan | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }} |
Before | findByPriceBefore | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }} |
After | findByPriceAfter | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }} |
Like | findByNameLike | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
StartingWith | findByNameStartingWith | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
EndingWith | findByNameEndingWith | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “*?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
Contains/Containing | findByNameContaining | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
In | findByNameIn(Collectionnames) | { “query” : { “bool” : { “must” : [ {“bool” : {“must” : [ {“terms” : {“name” : ["?","?"]}} ] } } ] } }} |
NotIn | findByNameNotIn(Collectionnames) | { “query” : { “bool” : { “must” : [ {“bool” : {“must_not” : [ {“terms” : {“name” : ["?","?"]}} ] } } ] } }} |
Near | findByStoreNear | Not Supported Yet ! |
TRUE | findByAvailableTrue | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }} |
FALSE | findByAvailableFalse | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “false”, “fields” : [ “available” ] } } ] } }} |
OrderBy | findByAvailableTrueOrderByNameDesc | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }, “sort”:[{“name”:{“order”:“desc”}}] } |