PHP笔记网

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2024-03-10 00:39:53  修改时间:2024-12-20 11:23:33  分类:04.大数据/Elasticsearch  编辑

一、Elasticsearch 介绍

Elaticsearch简称为ES,是一个开源的可扩展的分布式全文检索引擎服务器,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。ES使用Java开发并使用Lucene作为其核心来实现索引和搜索的功能,它通过简单的RestfulAPIJavaAPI来隐藏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设计了一种全新的思想,来实现全文搜索。具体操作过程如下:

  1. 将被查询的字段的数据全部文本信息进行拆分,分成若干个词
    1. 例如“北京科技公司”就会被拆分成三个词,分别是“北京”、“科技”、“公司”,此过程有专业术语叫做分词。分词的策略不同,分出的效果不一样,不同的分词策略称为分词器。
  2. 将分词得到的结果存储起来,对应每条数据的id
    1. 例如id为1的数据中名称这一项的值是“北京科技公司”,那么分词结束后,就会出现“北京”对应id为1,“科技”对应id为1,“公司”对应id为1。
    2. 例如id为2的数据中名称这一项的值是“北京朝阳群众“,那么分词结束后,就会出现“北京”对应id为2,“朝阳”对应id为2,“群众”对应id为2。
    3. 此时就会出现如下对应结果,按照上述形式可以对所有文档进行分词。需要注意分词的过程不是仅对一个字段进行,而是对每一个参与查询的字段都执行,最终结果汇总到一个表格中。
分词结果关键字 对应id
北京 1,2
科技 1
公司 1
朝阳 2
群众 2
  1. 当进行查询时,如果输入“北京”作为查询条件,可以通过上述表格数据进行比对,得到id值1,2,然后根据id值就可以得到查询的结果数据了。

二、Java操作示例

2.1 示例源码


官方教程:Spring Data Elasticsearch - Reference Documentation

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 客户端配置

官方文档:Elasticsearch Clients

注意:官网未介绍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

官方文档:Elasticsearch Operations

主要有以下4个类

  • IndexOperations 在索引级别定义操作,例如创建或删除索引。
  • DocumentOperations 定义根据其ID存储,更新和检索实体的操作。
  • SearchOperations 定义使用查询搜索多个实体的动作
  • ElasticsearchOperations 结合DocumentOperationsSearchOperations的接口
  • 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注解内的createIndextrue时(默认为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);
    }
}

 

 

参考:

  1. SpringBoot2.7.6 整合 Elasticsearch7.17.7
  2. 最详细SpringBoot与Elasticsearch整合