首页 >> 大全

Elastic search入门到集群实战操作详解(原生API操作、spring

2023-11-01 大全 19 作者:考证青年

1.4.测试集群中创建索引

配置,再重启

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

这个要亲自尝试才知道。

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

PUT /lagou { "": { "": 3, "": 1 } }

这里有两个配置:

通过浏览器的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与机制

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

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

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

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

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

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

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

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

1.5.2.集群写入数据

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

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

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

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

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

1.5.3.ES查询数据

倒排序算法

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

查询过程

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

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

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

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

2.客户端 2.1.客户端介绍

在官网中提供了各种语言的客户端: nt/index.html

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

2.2.创建Demo工程 2.2.1.初始化项目

创建项目。

2.2.2.pom文件

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

1.8org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-devtoolsruntimetrueorg.springframework.bootspring-boot-configuration-processortrueorg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtestorg.springframework.bootspring-boot-starter-loggingcom.google.code.gsongson2.8.5org.apache.commonscommons-lang33.8.1commons-beanutilscommons-beanutils1.9.1org.elasticsearch.clientelasticsearch-rest-high-level-client6.4.3org.elasticsearch.clientelasticsearch-rest-client6.4.3org.elasticsearchelasticsearch6.4.3
org.springframework.bootspring-boot-maven-pluginorg.projectlomboklombok

2.2.3.配置文件

我们在下创建.yml

2.3.索引库及映射

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

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

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

_spring从入门到精通_操作集中操作的分类

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

分析一下数据结构:

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

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.初始化客户端

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

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.查询所有

/*** 匹配所有* @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 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);}

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

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

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

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 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

= .("price");

方法

说明

gt( from)

大于

gte( from)

大于等于

lt( from)

小于

lte( 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过滤* @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.排序

依然是通过来配置:

 /*** 排序* @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);}

操作集中操作的分类_spring从入门到精通_

3. Data

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

3.1.什么是rch

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

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

Data 的页面:

特征:

3.2.配置rch

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


org.springframework.boot
spring-boot-starter-data-elasticsearch

然后,只需要在下新建.yml文件,引入的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

需要注意的是,rch底层使用的不是提供的, 而是,并不采用Http协议通信,而是访问对外开放的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; // 图片地址
}

@Id:声明实体类的id@Field:声明字段属性

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

    @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并没有封装在e中,而是有一个叫做 ory的接口:

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

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

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

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

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

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

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

支持的一些语法示例:

3.5.原生查询

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

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

需求: 查询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("");builder.postTags("");//设置为0即可返回完整内容 而非片段builder.numOfFragments(0);queryBuilder.withHighlightBuilder(builder);//5.对查询结果进行聚合分析:获取品牌及个数queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand").missing("title"));//6.查询AggregatedPage result = template.queryForPage(queryBuilder.build(), Product.class,new ESSearchResultMapper());System.out.println(result);//7.获取结果//总数long total = result.getTotalElements();//页码int totalPage = result.getTotalPages();//获取本业的数据集合List 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 * @return AggregatedPage 需要三个参数进行构建:pageable,List,总记录数*/@Overridepublic  AggregatedPage mapResults(SearchResponse searchResponse, Class aClass, Pageable pageable) {//获取总记录数SearchHits hits = searchResponse.getHits();long totalHits = hits.getTotalHits();System.out.println("总记录数->" + totalHits);Gson gson = new Gson();//记录列表List productList = new ArrayList<>();for (SearchHit hit : hits) {if (hits.getHits().length <= 0) {return null;}//获取_source属性中的所有数据Map map = hit.getSourceAsMap();//获取高亮的字段Map highlightFields = hit.getHighlightFields();//每个高亮字段都需要进行设置for (Map.Entry 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 mapSearchHit(SearchHit searchHit, Class aClass) {return null;}}

2、高亮实现:

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了