Elastic search入门到集群实战操作详解(原生API操作、springboot整合操作)-step2

本文主要是介绍Elastic search入门到集群实战操作详解(原生API操作、springboot整合操作)-step2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一篇详细讲解了使用kibana RestFul风格操作es,接下来就是ES集群、操作原生API 以及SpringDataElasticsearch

https://blog.csdn.net/qq_45441466/article/details/120110968

1.Elasticsearch集群

单点的elasticsearch存在哪些可能出现的问题呢?

  • 单台机器存储容量有限,无法实现高存储
  • 单服务器容易出现单点故障,无法实现高可用
  • 单服务的并发处理能力有限,无法实现高并发

所以,为了应对这些问题,我们需要对elasticsearch搭建集群

1.2.集群的结构

1.2.1.数据分片

首先,我们面临的第一个问题就是数据量太大,单点存储量有限的问题。

大家觉得应该如何解决?

没错,我们可以把数据拆分成多份,每一份存储到不同机器节点(node),从而实现减少每个节点数 据量的目的。这就是数据的分布式存储,也叫做: 数据分片(Shard) 。

 

 

1.2.2.数据备份

数据分片解决了海量数据存储的问题,但是如果出现单点故障,那么分片数据就不再完整,这又该如何 解决呢?

没错,就像大家为了备份手机数据,会额外存储一份到移动硬盘一样。我们可以给每个分片数据进行备 份,存储到其它节点,防止数据丢失,这就是数据备份,也叫 数据副本(replica)

数据备份可以保证高可用,但是每个分片备份一份,所需要的节点数量就会翻一倍,成本实在是太高 了!

为了在高可用和成本间寻求平衡,我们可以这样做:

  • 首先对数据分片,存储到不同节点
  • 然后对每个分片进行备份,放到对方节点,完成互相备份

这样可以大大减少所需要的服务节点数量,如图,我们以3分片,每个分片备份一份为例:

 在这个集群中,如果出现单节点故障,并不会导致数据缺失,所以保证了集群的高可用,同时也减少了 节点中数据存储量。并且因为是多个节点存储数据,因此用户请求也会分发到不同服务器,并发能力也 得到了一定的提升。

1.3.搭建集群

集群需要多台机器,我们这里用一台机器来模拟,因此我们需要在一台虚拟机中部署多个elasticsearch 节点,每个elasticsearch的端口都必须不一样。

一台机器进行模拟:将我们的ES的安装包复制三份,修改端口号,data和log存放位置的不同。

实际开发中:将每个ES节点放在不同的服务器上。

我们计划集群名称为:lagou-elastic,部署3个elasticsearch节点,分别是:

http:表示使用http协议进行访问时使用 端口,elasticsearch-head、kibana、postman,默认端口号 是9200。

tcp:集群间的各个节点进行通讯的端口,默认9300

第一步:复制es软件粘贴3次,分别改名

第二步:修改每一个节点的配置文件 config下的elasticsearch.yml,下面已第一份配置文件为例

 node-01:

#必须为本机的ip地址
network.host: 127.0.0.1

#允许跨域名访问
http.cors.enabled: true
#当设置允许跨域,默认为*,表示支持所有域名
http.cors.allow-origin: "*"
#允许所有节点访问
#network.host: 0.0.0.0
# 集群的名称,同一个集群下所有节点的集群名称应该一致
cluster.name: lagou-elastic
#当前节点名称 每个节点不一样
node.name: node-01
#数据的存放路径 每个节点不一样,不同es服务器对应的data和log存储的路径不能一样
path.data: D:\class\es-9201\data
#日志的存放路径 每个节点不一样
path.logs: D:\class\es-9201\logs
# http协议的对外端口 每个节点不一样,默认:9200
http.port: 9201
# TCP协议对外端口 每个节点不一样,默认:9300
transport.tcp.port: 9301
#三个节点相互发现,包含自己,使用tcp协议的端口号
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
#声明大于几个的投票主节点有效,请设置为(nodes / 2) + 1
#discovery.zen.minimum_master_nodes: 2
# 是否为主节点
#node.master: true

第三步:启动集群 把三个节点分别启动,启动时不要着急,要一个一个地启动

使用head插件查看:

 

1.4.测试集群中创建索引库

配置kibana,再重启

 

搭建集群以后就要创建索引库了,那么问题来了,当我们创建一个索引库后,数据会保存到哪个服务节 点上呢?如果我们对索引库分片,那么每个片会在哪个节点呢?

这个要亲自尝试才知道。

 

这里给搭建看看集群中分片和备份的设置方式,示例:

PUT /lagou { "settings": { "number_of_shards": 3, "number_of_replicas": 1 } }

这里有两个配置:

  •  number_of_shards:分片数量,这里设置为3
  • number_of_replicas:副本数量,这里设置为1,每个分片一个备份,一个原始数据,共2份。

通过chrome浏览器的head查看,我们可以查看到分片的存储结构:

可以看到,test这个索引库,有三个分片,分别是0、1、2,每个分片有1个副本,共6份。

node-01上保存了1号分片和2号分片的副本

node-02上保存了0号分片和2号分片的副本

node-03上保存了0号分片和1号分片的副本

1.5.集群工作原理

1.5.1.shad与replica机制

(1)一个index包含多个shard,也就是一个index存在多个服务器上

(2)每个shard都是一个最小工作单元,承载部分数据,比如有三台服务器,现在有三条数据,这三条数 据在三台服务器上各方一条. (

3)增减节点时,shard会自动在nodes中负载均衡

(4)primary shard(主分片)和replica shard(副本分片),每个document肯定只存在于某一个 primary shard以及其对应的replica shard中,不可能存在于多个primary shard

(5)replica shard是primary shard的副本,负责容错,以及承担读请求负载

(6)primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改

(7)primary shard的默认数量是5,replica默认是1(每个主分片一个副本分片),默认有10个 shard,5个primary shard,5个replica shard

(8)primary shard不能和自己的replica shard放在同一个节点上(否则节点宕机,primary shard和 副本都丢失,起不到容错的作用),但是可以和其他primary shard的replica shard放在同一个节点上

1.5.2.集群写入数据

1. 客户端选择一个node发送请求过去,这个node就是coordinating node (协调节点)

2. coordinating node,对document进行路由,将请求转发给对应的node。(根据一定的算法选择 对应的节点进行存储)

3. 实际上的node上的primary shard处理请求,将数据保存在本地,然后将数据同步到replica node

4. coordinating node,如果发现primary node和所有的replica node都搞定之后,就会返回请求到 客户端

这个路由简单的说就是取模算法,比如说现在有3太服务器,这个时候传过来的id是5,那么5%3=2,就 放在第2台服务器

1.5.3.ES查询数据

倒排序算法

查询有个算法叫倒排序:简单的说就是:通过分词把词语出现的id进行记录下来,再查询的时候先去查到哪 些id包含这个数据,然后再根据id把数据查出来

查询过程

1. 客户端发送一个请求给coordinate node

2. 协调节点将搜索的请求转发给所有的shard对应的primary shard 或replica shard

3. query phase(查询阶段):每一个shard 将自己搜索的结果(其实也就是一些唯一标识),返回 给协调节点,有协调节点进行数据的合并,排序,分页等操作,产出最后的结果

4. fetch phase(获取阶段) ,接着由协调节点,根据唯一标识去各个节点进行拉取数据,最终返回 给客户端

2.Elasticsearch客户端

2.1.客户端介绍

在elasticsearch官网中提供了各种语言的客户端:https://www.elastic.co/guide/en/elasticsearch/clie nt/index.html

注意点击进入后,选择版本到 6.2.4 ,因为我们之前按照的都是 6.2.4 版本:

2.2.创建Demo工程

2.2.1.初始化项目

创建springboot项目。

2.2.2.pom文件

注意,这里我们直接导入了SpringBoot的启动器,方便后续讲解。不过还需要手动引入elasticsearch的 High-level-Rest-Client的依赖:

<properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.5</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency><!--Apache开源组织提供的用于操作JAVA BEAN的工具包--><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.1</version></dependency><!--ES高级Rest Client--><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>6.4.3</version></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>6.4.3</version></dependency><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>6.4.3</version></dependency><!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>-->
<!--        </dependency>--></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>

2.2.3.配置文件

我们在resource下创建application.yml

2.3.索引库及映射

 创建索引库的同时,我们也会创建type及其映射关系,但是这些操作不建议使用java客户端完成,原因 如下:

  • 索引库和映射往往是初始化时完成,不需要频繁操作,不如提前配置好
  • 官方提供的创建索引库及映射API非常繁琐,需要通过字符串拼接json结构:

 

因此,这些操作建议还是使用我们昨天学习的Rest风格API去实现。

我们接下来以这样一个商品数据为例来创建索引库:

public class Product {
private Long id;
private String title; //标题
private String category;// 分类
private String brand; // 品牌
private Double price; // 价格
private String images; // 图片地址
}

分析一下数据结构:

  • id:可以认为是主键,将来判断数据是否重复的标示,不分词,可以使用keyword类型
  • title:搜索字段,需要分词,可以用text类型
  • category:商品分类,这个是整体,不分词,可以使用keyword类型 brand:品牌,与分类类似,不分词,可以使用keyword类型
  • price:价格,这个是double类型
  • images:图片,用来展示的字段,不搜索,index为false,不分词,可以使用keyword类型

我们可以编写这样的映射配置:

PUT /lgt
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"item": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text","analyzer": "ik_max_word"
},
"category": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"images": {
"type": "keyword",
"index": false
},
"price": {
"type": "double"
}
}
}
}
}

2.4.索引数据操作

有了索引库,我们接下来看看如何新增索引数据

操作MYSQL数据库:

1.获取数据库连接

2.完成数据的增删改查

3.释放资源

2.4.1.初始化客户端

完成任何操作都需要通过HighLevelRestClient客户端,看下如何创建。

private static RestHighLevelClient restHighLevelClient;private Gson gson = new Gson();/*** 初始化客户端*/@BeforeAllstatic void init() {System.out.println("执行了");RestClientBuilder clientBuilder = RestClient.builder(new HttpHost("127.0.0.1", 9201, "http"),new HttpHost("127.0.0.1", 9202, "http"),new HttpHost("127.0.0.1", 9203, "http"));restHighLevelClient = new RestHighLevelClient(clientBuilder);}/*** 关闭客户端*/@AfterAllstatic void close() throws IOException {restHighLevelClient.close();}

2.4.2.新增文档

/*** 新增文档* @throws IOException*/@Testvoid addDoc() throws IOException {//1.创建文档Product product = new Product();product.setBrand("华为");product.setCategory("手机");product.setId(1L);product.setImages("http://image.huawei.com/1.jpg");product.setTitle("华为P50就是棒");product.setPrice(88.88d);//2.将文档数据转换为json格式String source = gson.toJson(product);//3.创建索引请求对象//    public IndexRequest(String index, String type, String id)IndexRequest request = new IndexRequest("lagou","item",product.getId().toString());//4.发出请求request.source(source, XContentType.JSON);IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);System.out.println(response);}

2.4.3.查看文档

 /*** 查看文档* @throws IOException*/@Testvoid queryDoc() throws IOException {//创建请求对象GetRequest,并指定idGetRequest request = new GetRequest("lagou","item","1");//执行查询GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);System.out.println(response);String source = response.getSourceAsString();Product product = gson.fromJson(source, Product.class);System.out.println(product);}

2.4.4.修改文档

/*** 修改文档* @throws IOException*/@Testvoid updateDoc() throws IOException {Product product = new Product();product.setBrand("华为");product.setCategory("手机");product.setId(1L);product.setImages("http://image.huawei.com/1.jpg");product.setTitle("华为P50就是棒");product.setPrice(88.99d);//创建请求对象GetRequest,并指定idIndexRequest request = new IndexRequest("lagou","item",product.getId().toString());String source = gson.toJson(product);request.source(source,XContentType.JSON);//执行查询IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);System.out.println(response);}

2.4.5.删除文档

/*** 删除文档* @throws IOException*/@Testvoid deleteDoc() throws IOException {//创建请求对象GetRequest,并指定idDeleteRequest request = new DeleteRequest("lagou","item","1");//执行查询DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);System.out.println(response);}

2.5.搜索数据

2.5.1.查询所有match_all

/*** 匹配所有* @throws IOException*/@Testvoid matchAll() throws IOException {//创建搜索对象SearchRequest request = new SearchRequest();//查询构建工具SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//添加查询条件,通过QueryBuilders获取各种查询sourceBuilder.query(QueryBuilders.matchAllQuery());request.source(sourceBuilder);//执行查询SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);//获取查询结果SearchHits hits = search.getHits();//获取文件数组SearchHit[] searchHits = hits.getHits();List<Product> productList = new ArrayList<>();for (SearchHit searchHit : searchHits) {String source = searchHit.getSourceAsString();Product product = gson.fromJson(source, Product.class);productList.add(product);}System.out.println(productList);}

注意,上面的代码中,搜索条件是通过 sourceBuilder.query(QueryBuilders.matchAllQuery()) 来添加的。这个 query() 方法接受的参数是: QueryBuilder 接口类型。

这个接口提供了很多实现类,分别对应我们在之前中学习的不同类型的查询,例如:term查询、match 查询、range查询、boolean查询等,如图:

 因此,我们如果要使用各种不同查询,其实仅仅是传递给 sourceBuilder.query() 方法的参数不同而 已。而这些实现类不需要我们去 new ,官方提供了 QueryBuilders 工厂帮我们构建各种实现类:

2.5.2.关键字搜索match

封装基础查询方法:

 /*** 基础查询方法*         //查询构建工具*         SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();*         //添加查询条件,通过QueryBuilders获取各种查询*         sourceBuilder.query(QueryBuilders.matchAllQuery());*/void basicQuery(SearchSourceBuilder sourceBuilder) throws IOException {//创建搜索对象SearchRequest request = new SearchRequest();request.source(sourceBuilder);//执行查询SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);//获取查询结果SearchHits hits = search.getHits();//获取文件数组SearchHit[] searchHits = hits.getHits();List<Product> productList = new ArrayList<>();for (SearchHit searchHit : searchHits) {String source = searchHit.getSourceAsString();Product product = gson.fromJson(source, Product.class);productList.add(product);}System.out.println(productList);}
    /*** 关键字查询* @throws IOException*/@Testvoid match() throws IOException {SearchSourceBuilder builder = new SearchSourceBuilder();//设置查询类型的查询条件builder.query(QueryBuilders.matchQuery("title","P50"));//调用基础查询方法basicQuery(builder);}

2.5.3.范围查询range

RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");

方法说明
gt(Object from)大于
gte(Object from)大于等于
lt(Object from)小于
lte(Object from)小于等于

示例:

 /*** 范围查询 30-100* @throws IOException*/@Testvoid range() throws IOException {SearchSourceBuilder builder = new SearchSourceBuilder();//设置查询类型的查询条件builder.query(QueryBuilders.matchQuery("title","P50"));builder.query(QueryBuilders.rangeQuery("price").gt(30).lt(100));//调用基础查询方法basicQuery(builder);}

2.5.4.source过滤

_source:存储原始文档

默认情况下,索引库中所有数据都会返回,如果我们想只返回部分字段,可以通过source filter来控 制。

    /*** _source过滤* @throws IOException*/@Testvoid sourceFilter() throws IOException {SearchSourceBuilder builder = new SearchSourceBuilder();//设置查询类型的查询条件builder.query(QueryBuilders.matchQuery("title","P50"));builder.query(QueryBuilders.rangeQuery("price").gt(30).lt(100));builder.fetchSource(new String[]{"id","price","title"},new String[0]);//调用基础查询方法basicQuery(builder);}

2.6.排序

依然是通过sourceBuilder来配置:

 

 /*** 排序* @throws IOException*/@Testvoid sort() throws IOException {SearchSourceBuilder builder = new SearchSourceBuilder();//设置查询类型的查询条件builder.query(QueryBuilders.matchQuery("title","P50"));builder.query(QueryBuilders.rangeQuery("price").gt(30).lt(100));builder.fetchSource(new String[]{"id","price","title"},new String[0]);builder.sort("price", SortOrder.DESC);//调用基础查询方法basicQuery(builder);}

2.7.分页

 

  /*** 分页* @throws IOException*/@Testvoid page() throws IOException {SearchSourceBuilder builder = new SearchSourceBuilder();//设置查询类型的查询条件builder.query(QueryBuilders.matchAllQuery());//添加分页int page = 1;int size =3;int start = (page-1)*size;//配置分页builder.from(start);builder.size(size);//调用基础查询方法basicQuery(builder);}

3.Spring Data Elasticsearch

接下来我们学习Spring提供的elasticsearch组件:Spring Data Elasticsearch

3.1.什么是SpringDataElasticsearch

Spring Data Elasticsearch(以后简称SDE)是Spring Data项目下的一个子模块。

Spring Data 的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是 非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提 高开发效率。

Spring Data Elasticsearch的页面:https://projects.spring.io/spring-data-elasticsearch/

特征:

  • 支持Spring的基于 @Configuration 的java配置方式,或者XML配置方式
  • 提供了用于操作ES的便捷工具类 ElasticsearchTemplate 。包括实现文档到POJO之间的自动智 能映射。
  • 利用Spring的数据转换服务实现的功能丰富的对象映射
  • 基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式,可以定义JavaBean:类 名、属性
  • 根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自 动得到实现)。当然,也支持人工定制查询

3.2.配置SpringDataElasticsearch

我们在pom文件中,引入SpringDataElasticsearch的启动器:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

然后,只需要在resources下新建application.yml文件,引入elasticsearch的host和port即可:

spring:data:elasticsearch:cluster-name: lagou-elasticcluster-nodes: 127.0.0.1:9301,127.0.0.1:9302,127.0.0.1:9303

需要注意的是,SpringDataElasticsearch底层使用的不是Elasticsearch提供的RestHighLevelClient, 而是TransportClient,并不采用Http协议通信,而是访问elasticsearch对外开放的tcp端口,我们之前 集群配置中,设置的分别是:9301,9302,9303

3.3.索引库操作

准备一个pojo对象 然后准备一个新的实体类,作为下面与索引库对应的文档:

@Data
@Document(indexName = "lat",type = "product",shards = 3,replicas = 1)
@AllArgsConstructor
@NoArgsConstructor
public class Product {@Idprivate Long id;@Field(value = "title",type = FieldType.Text,analyzer = "ik_max_word")private String title; //标题@Field(value = "category",type = FieldType.Keyword)private String category;// 分类@Field(value = "brand",type = FieldType.Keyword)private String brand; // 品牌@Field(value = "price",type = FieldType.Double)private Double price; // 价格@Field(value = "images",type = FieldType.Keyword,index = false)private String images; // 图片地址
}
  • @Document:声明索引库配置
    • indexName:索引库名称
    • type:类型名称,默认是“docs”
    • shards:分片数量,默认5
    • replicas:副本数量,默认1
  • @Id:声明实体类的id
  • @Field:声明字段属性
    • type:字段的数据类型
    • analyzer:指定分词器类型
    • index:是否创建索引

我们先创建一个测试类,然后注入ElasticsearchTemplate:

    @Autowiredprivate ElasticsearchTemplate template;

下面是创建索引库的API示例:

    @Testpublic void createIndex() {//创建索引的方法template.createIndex(Product.class);}

 创建索引库需要指定的信息,比如:索引库名、类型名、分片、副本数量、还有映射信息都已经填写

3.3.2.创建映射

 @Testpublic void putMapping() {//创建类型映射template.putMapping(Product.class);}

3.4.索引数据CRUD

SDE的索引数据CRUD并没有封装在ElasticsearchTemplate中,而是有一个叫做 ElasticsearchRepository的接口:

 我们需要自定义接口,继承ElasticsearchRespository:

/*** @Author panghl* @Date 2021/9/5 0:17* @Description* 当SDE访问索引库时,需要定义一个持久层的接口去继承ElasticsearchRepository 即可,无需实现**/
public interface ProductRepository extends ElasticsearchRepository<Product, Long> {/*** 根据价格区间查询* @param from 开始价格* @param to 结束价格* @return 符合条件的goods*/List<Product> findByPriceBetween(Double from, Double to);
}

3.4.1.创建索引数据

    @Autowiredprivate ProductRepository productRepository;@Testpublic void addDoc() {Product product1 = new Product(1L, "锤子手机", "手机", "锤子", 3288.88d, "http://image.huawei.com/1.jpg");Product product2 = new Product(2L, "华为手机", "手机", "华为", 3288.88d, "http://image.huawei.com/1.jpg");Product product3 = new Product(3L, "小米手机", "手机", "小米", 3288.88d, "http://image.huawei.com/1.jpg");Product product4 = new Product(4L, "苹果手机", "手机", "苹果", 3288.88d, "http://image.huawei.com/1.jpg");Product product5 = new Product(5L, "OPPO手机", "手机", "OPPO", 3288.88d, "http://image.huawei.com/1.jpg");List<Product> productList = new ArrayList<>();productList.add(product1);productList.add(product2);productList.add(product3);productList.add(product4);productList.add(product5);productRepository.saveAll(productList);System.out.println("save success");}

3.4.2.查询索引数据

默认提供了根据id查询,查询所有两个功能:

 @Testpublic void queryIndexData() {Product product = productRepository.findById(1L).orElse(new Product());//取出数据//orElse 方法的作用:如果optional中封装的实体对象为空也就是没有从索引库中查询出匹配的文档,返回orElse的参数System.out.println("product=>" + product);}

3.4.3.自定义方法查询

ProductRepository提供的查询方法有限,但是它却提供了非常强大的自定义查询功能:

只要遵循SpringData提供的语法,我们可以任意定义方法声明:

    /*** 根据价格区间查询* @param from 开始价格* @param to 结束价格* @return 符合条件的goods*/List<Product> findByPriceBetween(Double from, Double to);

无需写实现,SDE会自动帮我们实现该方法,我们只需要用即可:

    @Testpublic void querySelfIndexData() {List<Product> byPriceBetween = productRepository.findByPriceBetween(1000d, 4000d);System.out.println(byPriceBetween);}

支持的一些语法示例:

 

 

3.5.原生查询

 如果觉得上述接口依然不符合你的需求,SDE也支持原生查询,这个时候还是使用 ElasticsearchTemplate

而查询条件的构建是通过一个名为 NativeSearchQueryBuilder 的类来完成的,不过这个类的底层还 是使用的原生API中的 QueryBuilders 、 AggregationBuilders 、 HighlightBuilders 等工具。

需求: 查询title中包含小米手机的商品,以价格升序排序,分页查询:每页展示2条,查询第1页。 对查询结果进行聚合分析:获取品牌及个数

示例

/*** 查询title中包含小米手机的商品,以价格升序排序,分页查询:每页展示2条,查询第1页。* 对查询结果进行聚合分析:获取品牌及个数*/@Testpublic void nativeQuery() {//1.构建一个原生查询器NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();//2.source过来//2.1参数: public FetchSourceFilter(String[] includes, String[] excludes)queryBuilder.withSourceFilter(new FetchSourceFilter(new String[0], new String[0]));//3.查询条件queryBuilder.withQuery(QueryBuilders.matchQuery("title","小米手机"));//4.设置分页,并排序queryBuilder.withPageable(PageRequest.of(0,10, Sort.by(Sort.Direction.ASC,"price")));//高亮
//        HighlightBuilder.Field field = new HighlightBuilder.Field("title");HighlightBuilder builder = new HighlightBuilder();builder.field("title");builder.preTags("<font style='color:red'>");builder.postTags("</font>");//设置为0即可返回完整内容 而非片段builder.numOfFragments(0);queryBuilder.withHighlightBuilder(builder);//5.对查询结果进行聚合分析:获取品牌及个数queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand").missing("title"));//6.查询AggregatedPage<Product> result = template.queryForPage(queryBuilder.build(), Product.class,new ESSearchResultMapper());System.out.println(result);//7.获取结果//总数long total = result.getTotalElements();//页码int totalPage = result.getTotalPages();//获取本业的数据集合List<Product> content = result.getContent();content.stream().forEach(System.out::print);//获取聚合的结果Aggregations aggregations = result.getAggregations();Terms terms = aggregations.get("brandAgg");//获取桶并且遍历桶中的内容terms.getBuckets().forEach(b->{System.out.println("品牌->"+b.getKey());System.out.println("文档数->"+b.getDocCount());});}

注:上述查询不支持高亮结果。

高亮展示:

1、自定义搜索结果映射

/*** @Author panghl* @Date 2021/9/5 1:09* @Description 自定义结果映射,处理高亮**/
public class ESSearchResultMapper implements SearchResultMapper {/*** 完成结果映射* 操作的重点应该是将原有的结果: _source 取出来,放入高亮的数据** @param searchResponse* @param aClass* @param pageable* @param <T>* @return AggregatedPage 需要三个参数进行构建:pageable,List<Product>,总记录数*/@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {//获取总记录数SearchHits hits = searchResponse.getHits();long totalHits = hits.getTotalHits();System.out.println("总记录数->" + totalHits);Gson gson = new Gson();//记录列表List<T> productList = new ArrayList<>();for (SearchHit hit : hits) {if (hits.getHits().length <= 0) {return null;}//获取_source属性中的所有数据Map<String, Object> map = hit.getSourceAsMap();//获取高亮的字段Map<String, HighlightField> highlightFields = hit.getHighlightFields();//每个高亮字段都需要进行设置for (Map.Entry<String, HighlightField> highlightField : highlightFields.entrySet()) {//获取高亮的key : 高亮的字段String key = highlightField.getKey();//获得value : 高亮之后的效果HighlightField value = highlightField.getValue();//将高亮字段和文本效果放入map中map.put(key, value.getFragments()[0].toString());}//将map转为对象T T = gson.fromJson(gson.toJson(map), aClass);productList.add(T);}//第四个参数response.getAggregations()  添加聚合结果return new AggregatedPageImpl<>(productList,pageable,totalHits,searchResponse.getAggregations(),searchResponse.getScrollId());}@Overridepublic <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {return null;}}

2、高亮实现:

这篇关于Elastic search入门到集群实战操作详解(原生API操作、springboot整合操作)-step2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/324244

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2