一、Elasticsearch 介绍
Elaticsearch简称为ES,是一个开源的可扩展的分布式全文检索引擎服务器,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。ES使用Java开发并使用Lucene作为其核心来实现索引和搜索的功能,它通过简单的RestfulAPI和JavaAPI来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Elasticsearch官网:https://www.elastic.co/cn/products/
1.1 使用场景
- 分布式的搜索引擎
- 分布式:Elasticsearch自动将海量数据分散到多台服务器上去存储和检索
- 搜索:百度、谷歌,站内搜索
- 全文检索
- 提供模糊搜索等自动度很高的查询方式,并进行相关性排名,高亮等功能
- 数据分析引擎(分组聚合)
- 电商网站,最近一周笔记本电脑这种商品销量排名top10的商家有哪些?新闻网站,最近1个月访问量排名top3的新闻板块是哪些
- 对海量数据进行近实时的处理
- 海量数据的处理:因为是分布式架构,Elasticsearch可以采用大量的服务器去存储和检索数据,自然而然就可以实现海量数据的处理 近实时:Elasticsearch可以实现秒级别的数据搜索和分析
1.2 相关概念
索引库[index]-----------------------------------Database 数据库
类型[type]----------------------------------Table 数据表
文档[Document]--------------------------Row 行
字段[Field]-------------------------Column 列
映射[mapping]-------------------表结构
详细说明
概念 | 说明 |
---|---|
索引 | 索引包含一堆相关业务,结构相似的文档数据,比如说建立一个商品product索引库,里面就存放了所有的商品数据。 |
类型 | 类型是索引库中的一个逻辑数据分类,一个类型下的文档,都有相同的字段,类似于数据库中的表。比如商品类型,里面存放了所有的商品文档数据。6.0版本以前每个索引里可以是一个或多个类型,6.0版本以后一个索引只能有1个类型,7.x以后已经移除了这个概念。 |
文档 | 文档是存入索引库最小数据单元,一个文档可以是一条客户数据,一条商品数据,一条订单数据,通常用Json数据结构表示。文档存在索引库下的类型中。 |
字段 | Field是Elasticsearch的最小单位。一个document里面有多个field,每个field就是一个数据字段。 |
映射 | 类型对文档结构的约束叫做映射(mapping),用来定义document的每个字段的约束。如:字段的数据类型、是否分词、是否索引、是否存储等特性。类型是模拟mysql中的table概念。表是有结构的,也就是表中每个字段都有约束信息。 |
最新版ES中的 index、document、filed、mapping这样的概念,对比参考MySQL如下:
ElasticSearch | MySQL |
---|---|
index | 表 |
document | 行 |
field | 列 |
mapping | 表结构 |
1.3 原理举例
要实现全文搜索的效果,不可能使用数据库中like操作去进行比对,这种效率太低了。ES设计了一种全新的思想,来实现全文搜索。具体操作过程如下:
- 将被查询的字段的数据全部文本信息进行拆分,分成若干个词
- 例如“北京科技公司”就会被拆分成三个词,分别是“北京”、“科技”、“公司”,此过程有专业术语叫做分词。分词的策略不同,分出的效果不一样,不同的分词策略称为分词器。
- 将分词得到的结果存储起来,对应每条数据的id
- 例如id为1的数据中名称这一项的值是“北京科技公司”,那么分词结束后,就会出现“北京”对应id为1,“科技”对应id为1,“公司”对应id为1。
- 例如id为2的数据中名称这一项的值是“北京朝阳群众“,那么分词结束后,就会出现“北京”对应id为2,“朝阳”对应id为2,“群众”对应id为2。
- 此时就会出现如下对应结果,按照上述形式可以对所有文档进行分词。需要注意分词的过程不是仅对一个字段进行,而是对每一个参与查询的字段都执行,最终结果汇总到一个表格中。
分词结果关键字 | 对应id |
---|---|
北京 | 1,2 |
科技 | 1 |
公司 | 1 |
朝阳 | 2 |
群众 | 2 |
- 当进行查询时,如果输入“北京”作为查询条件,可以通过上述表格数据进行比对,得到id值1,2,然后根据id值就可以得到查询的结果数据了。
二、Java操作示例
2.1 示例源码
- SpringBoot2
- SpringBoot3
PS:本文只是一篇极其简单的整合教程,不涉及复杂搜索示例,建议认真阅读官方文档
2.2 版本对应关系
Spring Boot | Spring Data Elasticsearch | Elasticsearch |
---|---|---|
2.4.x | 4.1.x | 7.9.x |
2.5.x | 4.2.x | 7.12.x |
2.6.x | 4.3.x | 7.15.x |
2.7.x | 4.4.x | 7.17.x |
- 因为
Spring Boot 2.7.18
对应的Spring Data Elasticsearch 4.4.18
对应的Elasticsearch
的版本为7.17.18
,所以本文以此版本为例- Spring Boot 2.7.18
- Spring Data Elasticsearch 4.4.18
- Elasticsearch 7.17.18
- 本文不介绍
Reactive
模式的相关代码
2.3 引入Maven依赖包
pom.xml
<!--++++++++++++++++++++++++++++++++++++++++++++++++++++--> <!-- Elasticsearch搜索引擎 --> <!--++++++++++++++++++++++++++++++++++++++++++++++++++++--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
2.4 客户端配置
注意:官网未介绍yml方式配置,但是也是可以使用的,不做复杂的设置的话,yml即可
方式一:application.yml
# yml配置Elasticsearch客户端地址(可配置项有限) spring: elasticsearch: uris: http://127.0.0.1:9200 # Elasticsearch 连接地址 #username: elastic # 用户名 #password: 123456 # 密码 connection-timeout: 10s # 连接超时时间(默认1s) socket-timeout: 30s # 数据读取超时时间(默认30s)
方式二:Bean
import java.time.Duration; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.RestClients; import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; /** * Java高级别REST客户端是Elasticsearch的默认客户端 * * Bean方式配置 */ @Configuration public class RestClientConfig extends AbstractElasticsearchConfiguration { @Bean @Override public RestHighLevelClient elasticsearchClient() { // 使用构建器来提供集群地址,设置默认值HttpHeaders或启用SSL。 ClientConfiguration clientConfiguration = ClientConfiguration.builder() // 设置连接地址 .connectedTo("127.0.0.1:9200") // 可以设置多个地址 // .connectedTo("127.0.0.1:9200", "127.0.0.1:9201") // 是否启用ssl // .usingSsl() // 设置连接超时时间 .withConnectTimeout(Duration.ofSeconds(10)) // 设置 .withSocketTimeout(Duration.ofSeconds(30)) // 设置用户名密码 // .withBasicAuth("elastic", "123456") // 创建连接信息 .build(); // 创建RestHighLevelClient。 return RestClients.create(clientConfiguration).rest(); } }
2.5 日志输出
方便查询发送的http请求
详细内容
在application.yml
内添加如下内容:
logging: file: name: ./log/log.log level: root: info org.springframework.data.elasticsearch.client.WIRE: trace
2.6 实体映射
官方文档:Elasticsearch Object Mapping
package com.wanma.apps.model.bo; 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.LocalDateTime; @Document(indexName = "user", shards = 3, replicas = 0) public class User { @Id private Integer id; @Field(type = FieldType.Keyword) private String name; @Field(type = FieldType.Integer) private Integer age; @Field(type = FieldType.Text, analyzer = "ik_max_word") private String address; @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSX") private LocalDateTime createTime; }
注解说明:
在MappingElasticsearchConverter使用元数据驱动的对象的映射文件。元数据取自可以注释的实体属性。 提供以下注释: @Document:在类级别应用,以指示该类是映射到数据库的候选对象。最重要的属性是: indexName:用于存储此实体的索引的名称。它可以包含SpEL模板表达式,例如 "log-#{T(java.time.LocalDate).now().toString()}" createIndex:标记是否在存储库引导中创建索引。默认值为true。请参见使用相应的映射自动创建索引 versionType:版本管理的配置。默认值为EXTERNAL。 @Id:在字段级别应用,以标记用于标识目的的字段。 @Transient:默认情况下,存储或检索文档时,所有字段都映射到文档,此注释不包括该字段。 @PersistenceConstructor:标记从数据库实例化对象时要使用的给定构造函数,甚至是受保护的程序包。构造函数参数按名称映射到检索到的Document中的键值。 @Field:在字段级别应用并定义字段的属性,大多数属性映射到各自的Elasticsearch映射定义(以下列表不完整,请查看注释Javadoc以获得完整参考): name:字段名称,它将在Elasticsearch文档中表示,如果未设置,则使用Java字段名称。 type:字段类型,可以是Text, Keyword, Long, Integer, Short, Byte, Double, Float, Half_Float, Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator, Flattened, Search_As_You_Type。请参阅Elasticsearch映射类型 format和日期类型的pattern定义。必须为日期类型定义format store:标记是否将原始字段值存储在Elasticsearch中,默认值为false。 analyzer,searchAnalyzer,normalizer用于指定自定义分析和正规化。 @GeoPoint:将字段标记为geo_point数据类型。如果字段是GeoPoint类的实例,则可以省略。 @ValueConverter:定义用于转换给定属性的类。与注册的 Spring 不同,Converter这仅转换带注释的属性,而不是给定类型的每个属性。 @Setting:注释定义不同的索引设置。以下参数可用: useServerConfiguration 不发送任何设置参数,因此 Elasticsearch 服务器配置确定它们。 settingPath 是指定义必须在类路径中解析的设置的 JSON 文件 shards要使用的分片数,默认为1 replicas副本数,默认为1 refreshIntervall, 默认为“1s” indexStoreType, 默认为"fs"
2.7 增、删、改、查
Elasticsearch Operations
主要有以下4个类
IndexOperations
在索引级别定义操作,例如创建或删除索引。DocumentOperations
定义根据其ID存储,更新和检索实体的操作。SearchOperations
定义使用查询搜索多个实体的动作ElasticsearchOperations
结合DocumentOperations
和SearchOperations
的接口ElasticsearchRestTemplate
是ElasticsearchOperations
的实现类
日常使用时,使用
ElasticsearchRestTemplate
即可,代码如下
@Autowired private ElasticsearchRestTemplate template;
IndexOperations
需要从ElasticsearchRestTemplate
中获取,例如:template.indexOps(User.class)
(1)操作索引
// 获取IndexOperations对象 IndexOperations indexOperations = template.indexOps(User.class); // 查 boolean exists = indexOperations.exists(); // 删 boolean delete = indexOperations.delete(); // 增 boolean flag = indexOperations.create(); // 设置Mapping boolean mapping = indexOperations.putMapping();
完整示例:com.maxqiu.demo.IndexApi
(2)操作文档
// 增/改 User user = new User(); user.set... template.save(user); // 批量 增/改 List<User> userList = new ArrayList<>(); userList.add(new User); ... Iterable<User> users = template.save(userList); // 查 User user = template.get("1", User.class); // 删 String delete = template.delete("1", User.class);
完整示例:com.maxqiu.demo.DocumentApi
(3)搜索
// 新建一个QueryBuilder(该对象是Elasticsearch的,QueryBuilders可以构建各种各样的条件查询) QueryBuilder queryBuilder = QueryBuilders.matchAllQuery(); // 新建一个Query(该对象是Spring Data Elasticsearch的) Query query = new NativeSearchQueryBuilder().withQuery(queryBuilder).build(); // 使用template执行查询,SearchHits<User>就是执行后的返回结果的映射实体 SearchHits<User> search = template.search(query, User.class);
完整示例:com.maxqiu.demo.SearchApi
ElasticsearchRepository
官方文档:Elasticsearch Repositories
一个类似于Spring Data JPA
的文档操作方法
// 新建一个User的数据持久层 @Repository public interface UserRepository extends ElasticsearchRepository<User, Integer> {}
建立此持久层后,如果User
实体@Document
注解内的createIndex
为true
时(默认为true
),则服务启动时会先检查索引是否存在,若不存在则会自动创建索引。
(1)默认的CRUD示例
// 增/改 User user = new User(1, "张三", 18, "上海市闵行区", LocalDateTime.now()); User save = repository.save(user); // 查 Optional<User> optionalUser = repository.findById(1); // 删 repository.deleteById(1);
完整示例:com.maxqiu.demo.repository.UserRepositoryTest
(2)自定义的条件查询
在UserRepository
内新建抽象方法
List<User> findUsersByNameAndAddress(String name, String address);
调用
@Test void test() { List<User> userList = repository.findUsersByNameAndAddress("张三", "上海"); for (User user : userList) { System.out.println(user); } }
参考: