高大上网站设计,科讯网站首页公告模板,做网站公司哪个比较好,东莞哪些地方是风险区ES 文章目录 ES一、初识elasticsearch1、什么是elasticsearch#xff0c;elastic static#xff0c;Lucene2、倒排索引2.1、正向索引和倒排序索引 3、es与mysql的概念对比3.1、文档3.2、索引3.3、es与数据库中的关系 二、索引库操作1、mapping属性2、创建索引库和映射基本语法…ES 文章目录 ES一、初识elasticsearch1、什么是elasticsearchelastic staticLucene2、倒排索引2.1、正向索引和倒排序索引 3、es与mysql的概念对比3.1、文档3.2、索引3.3、es与数据库中的关系 二、索引库操作1、mapping属性2、创建索引库和映射基本语法格式示例 3、查找、删除、修改索引3.1、查询索引库基本语法格式 3.2、修改索引库语法说明示例 3.3、删除索引库语法格式 三、文档操作1、查询、新增、删除文档1.1、新增文档语法 1.2、查询文档语法 1.3、删除文档语法 2、修改文档2.1、全量修改语法 2.2、局部修改语法示例 3、批处理 四、RestAPI4.1、初始化RestClient4.2、创建索引库4.2.1、Mapping映射4.2.2、创建索引 4.3、删除索引库4.4、判断索引库是否存在4.5、总结 五、RestClient操作文档5.1、新增文档5.1.1、实体类5.1.2、API语法5.1.3、完整代码 5.2、查询文档5.2.1、语法说明5.2.2、完整代码 5.3、删除文档5.4、修改文档5.4.1、语法说明5.4.2、完整代码 5.5、批量导入文档5.5.1、语法说明5.5.2、完整代码 5.6、小结 一、初识elasticsearch
1、什么是elasticsearchelastic staticLucene
elasticsearch是一个开源的分布式搜索引擎可以用来实现搜索日志统计、分析系统监控等功能
elasticsearch结合kibanalogstachbeats也就是elastic static被广泛应用在日志数据分析实时监控等领域。
elasticsearch是elastic static的核心底层实现是通过Lucene负责存储、搜索、分析数据。beatslogstach负责数据抓取。Kibana负责数据可视化。 elastic static是以elastic search为核心的技术栈包括beatslogstachkibanaelasticsearch。
Lucene是Apache的来源引擎类库提供了搜索 引擎的核心api。优势易扩展高性能基于倒排序索引。缺点只限java语言开发学习路线陡峭不支持水平扩展
2、倒排索引
2.1、正向索引和倒排序索引
正向索引基于文档id创建索引。查询词条时必须先找到文档再判断文档是否包含词条。mysql会基于id创建一个索引生成一棵B树根据id查询数据速度非常快。
缺点假如搜索的是title字段因为title比较长所以一般不会给title加索引。即便加了索引如果不是精确查找而是如下图的搜索此时索引会失效数据库会采用逐条扫描的方式 来判断是否包含手机如果包含就放到结果集中 否则丢弃。如果数据量庞大 那么查找的性能就非常低下。
**倒排索引**先对文档内容进行分词对词条创建索引。并记录词条所在的文档信息。查询时先根据词条查找到文档id而后获取文档。
文档document每条数据就是一个文档词条term按照语义分成的词语。对文档中的内容进行分词得到的词语就是词条 3、es与mysql的概念对比
3.1、文档
elasticsearch面向的是文档的存储可以是数据库中的一条商品数据一个订单信息。文档中的数据会被序列化为json格式后存储在elasticsearch中。
文档一条数据就是一个文档es中是json格式字段json文档中的字段 3.2、索引
索引index相同类型的文档的集合映射mapping索引中文档的字段约束信息类似表的结构约束。比如字段名称类型。 3.3、es与数据库中的关系
Mysql擅长事务类型操作可以确保数据的安全和一致性Elasticsearch擅长海量数据的搜索、分析、计算
二、索引库操作
1、mapping属性
Mapping是索引库中文档的约束常见的Mapping属性包括
type字段数据类型常见的简单类型有 字符串text可分词的文本、keyword精确值例如品牌、国家、ip地址数值long、integer、short、byte、double、float布尔boolean日期date对象object index是否创建索引默认为trueanalyzer使用哪种分词器properties该字段的子字段
{age: 21,weight: 52.1,isMarried: false,info: 黑马程序员Java讲师,email: zyitcast.cn,score: [99.1, 99.5, 98.9],name: {firstName: 云,lastName: 赵}
}字段名字段类型类型说明是否参与搜索是否参与分词分词器ageinteger整数是否——weightfloat浮点数是否——isMarriedboolean布尔是否——infotext字符串但需要分词是是IKemailkeyword字符串但是不分词否否——scorefloat只看数组中元素类型是否——firstNamekeyword字符串但是不分词是否——lastNamekeyword字符串但是不分词是否——
2、创建索引库和映射
基本语法
请求方式PUT请求路径/索引库名可以自定义mapping映射
格式
PUT /索引库名称
{mappings: {properties: {字段名:{type: text,analyzer: ik_smart},字段名2:{type: keyword,index: false},字段名3:{properties: {子字段: {type: keyword}}},// ...略}}
}示例
# PUT /heima
{mappings: {properties: {info:{type: text,analyzer: ik_smart},email:{type: keyword,index: false},name:{properties: {firstName: {type: keyword}}}}}
}3、查找、删除、修改索引
3.1、查询索引库
基本语法
请求方式GET请求路径/索引库名请求参数无
格式
GET/索引库名3.2、修改索引库
倒排索引结构虽然不复杂但是一旦数据结改变比如改变哩分词器就需要重新创建倒排索引。因此索引库一旦创建无法修改mapping。
虽然无法修改mapping中已有的字段但是却允许添加新的字段到mapping中因为不会对倒排索引产生影响。因此修改索引库能做的就是向索引库中添加新字段或者更新索引库的基础属性。
语法说明
PUT /索引库名/_mapping
{properties: {新字段名:{type: integer}}
}示例
PUT /heima/_mapping
{properties: {age:{type: integer}}
}3.3、删除索引库
语法
请求方式DELETE请求路径/索引库名请求参数无
格式
DELETE/索引库名三、文档操作
1、查询、新增、删除文档
1.1、新增文档
语法
POST /索引库名/_doc/文档id
{字段1: 值1,字段2: 值2,字段3: {子属性1: 值3,子属性2: 值4},
}1.2、查询文档
语法
GET /{索引库名称}/_doc/{id}1.3、删除文档
语法
DELETE /{索引库名}/_doc/id值2、修改文档
2.1、全量修改
全量修改是覆盖原来的文档其本质是两步操作
根据指定的id删除文档新增一个相同id的文档
**注意**如果根据id删除时id不存在第二步的新增也会执行也就从修改变成了新增操作了。
语法
PUT /{索引库名}/_doc/文档id
{字段1: 值1,字段2: 值2,// ... 略
}示例
PUT /heima/_doc/1
{info: 黑马程序员高级Java讲师,email: zyitcast.cn,name: {firstName: 云,lastName: 赵}
} 如果id为1的文档已经被删除那么第一次执行时得到的反馈是created。如果执行第2次时得到的反馈则是update。
2.2、局部修改
局部修改是指只修改指定id匹配的文档中的部分字段。
语法
POST /{索引库名}/_update/文档id
{doc: {字段名: 新的值,}
}示例
POST /heima/_update/1
{doc: {email: ZhaoYunitcast.cn}
}3、批处理
批处理采用POST请求基本语法如下
POST _bulk
{ index : { _index : test, _id : 1 } }
{ field1 : value1 }
{ delete : { _index : test, _id : 2 } }
{ create : { _index : test, _id : 3 } }
{ field1 : value3 }
{ update : {_id : 1, _index : test} }
{ doc : {field2 : value2} }其中
index代表新增操作 _index指定索引库名_id指定要操作的文档id{field1:value1}:要新增的文档内容 delete代表删除操作 _index指定索引库名_id指定要操作的文档id update代表更新操作 _index指定索引库名_id指定要操作的文档id{ doc : {field2 : value2} }要更新的文档字段
示例批量新增
POST /_bulk
{index: {_index:heima, _id: 3}}
{info: 黑马程序员C讲师, email: wwitcast.cn, name:{firstName: 五, lastName:王}}
{index: {_index:heima, _id: 4}}
{info: 黑马程序员前端讲师, email: zhangsanitcast.cn, name:{firstName: 三, lastName:张}}批量删除
POST /_bulk
{delete:{_index:heima, _id: 3}}
{delete:{_index:heima, _id: 4}}四、RestAPI
4.1、初始化RestClient
在elasticsearch提供的API中与elasticsearch一切交互都封装在一个名为RestHighLevelClient的类中必须先完成这个对象的初始化建立与elasticsearch的连接。
分三步
在item-service模块中引入es的RestHighLevel Client依赖
dependencygroupIdorg.elasticsearch.client/groupIdartifactIdelasticsearch-rest-high-level-client/artifactId
/dependency覆盖掉SpringBoot默认的ES版本7.17.0 propertiesmaven.compiler.source11/maven.compiler.sourcemaven.compiler.target11/maven.compiler.targetelasticsearch.version7.12.1/elasticsearch.version/properties初始化RestHighLevelClient
RestHighLevelClient client new RestHighLevelClient(RestClient.builder(HttpHost.create(http://192.168.150.101:9200)
)); 创建一个测试类IndexTest然后将初始化的代码编写在BeforeEach方法中
package com.hmall.item.es;import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;import java.io.IOException;public class IndexTest {private RestHighLevelClient client;BeforeEachvoid setUp() {this.client new RestHighLevelClient(RestClient.builder(HttpHost.create(http://192.168.150.101:9200)));}Testvoid testConnect() {System.out.println(client);}AfterEachvoid tearDown() throws IOException {this.client.close();}
}4.2、创建索引库
由于要实现对商品搜索所以我们需要将商品添加到Elasticsearch中不过需要根据搜索业务的需求来设定索引库结构而不是一股脑的把MySQL数据写入Elasticsearch.
4.2.1、Mapping映射
搜索页面的效果如图所示 实现搜索功能需要的字段包括三大部分
搜索过滤字段 分类品牌价格 排序字段 默认按照更新时间降序排序销量价格 展示字段 商品id用于点击后跳转图片地址是否是广告推广商品名称价格评价数量销量
对应的商品表结构如下索引库无关字段已经划掉 结合数据库表结构以上字段对应的mapping映射属性如下
字段名字段类型类型说明是否参与搜索是否参与分词分词器idlong长整数是否——nametext字符串参与分词搜索是是IKpriceinteger以分为单位所以是整数是否——stockinteger字符串但需要分词是否——imagekeyword字符串但是不分词否否——categorykeyword字符串但是不分词是否——brandkeyword字符串但是不分词是否——soldinteger销量整数是否——commentCountinteger评价整数否否——isADboolean布尔类型是否——updateTimeDate更新时间
因此最终我们的索引库文档结构应该是
PUT /items
{mappings: {properties: {id: {type: keyword},name:{type: text,analyzer: ik_max_word},price:{type: integer},stock:{type: integer},image:{type: keyword,index: false},category:{type: keyword},brand:{type: keyword},sold:{type: integer},commentCount:{type: integer,index: false},isAD:{type: boolean},updateTime:{type: date}}}
}4.2.2、创建索引
创建索引库的API如下 代码分为三步 创建Request对象 因为是创建索引库的操作因此Request是CreateIndexRequest。 添加请求参数 其实就是Json格式的Mappiing映射参数。因为json字符串很长这里定义了静态字符串常量MAPPING_TEMPLATE让代码看起来更优雅。 发送请求 client.indices()方法的返回值是IndicesClient类型封装了所有与索引库操作有关的方法。例如创建索引、删除索引、判断索引是否存在等。 在item-service中的IndexTest测试类中具体代码如下
Test
void testCreateIndex() throws IOException {// 1.创建Request对象CreateIndexRequest request new CreateIndexRequest(items);// 2.准备请求参数request.source(MAPPING_TEMPLATE, XContentType.JSON);// 3.发送请求client.indices().create(request, RequestOptions.DEFAULT);
}static final String MAPPING_TEMPLATE {\n \mappings\: {\n \properties\: {\n \id\: {\n \type\: \keyword\\n },\n \name\:{\n \type\: \text\,\n \analyzer\: \ik_max_word\\n },\n \price\:{\n \type\: \integer\\n },\n \stock\:{\n \type\: \integer\\n },\n \image\:{\n \type\: \keyword\,\n \index\: false\n },\n \category\:{\n \type\: \keyword\\n },\n \brand\:{\n \type\: \keyword\\n },\n \sold\:{\n \type\: \integer\\n },\n \commentCount\:{\n \type\: \integer\\n },\n \isAD\:{\n \type\: \boolean\\n },\n \updateTime\:{\n \type\: \date\\n }\n }\n }\n };4.3、删除索引库
删除索引库的请求非常简单
DELETE/hotel与创建索引库相比
请求方式从PUT变为DELETE请求路径不变无请求参数
所以代码的差异集中体现在Request对象上。流程如下
创建Request对象。这次是DeleteIndexRequest对象准备参数。这里是无参因此省略发送请求。改用delete方法
在item-service中的IndexTest测试类中编写单元测试实现删除索引
Test
void testDeleteIndex() throws IOException {// 1.创建Request对象DeleteIndexRequest request new DeleteIndexRequest(items);// 2.发送请求client.indices().delete(request, RequestOptions.DEFAULT);
}4.4、判断索引库是否存在
判断索引库是否存在本质就是查询对应的请求语句
GET/hotel因此与删除的Java代码流程是类似的流程如下
创建Request对象。这次是GetIndexRequest对象准备参数。这里是无参直接省略发送请求。改用exists方法
Test
void testExistsIndex() throws IOException {// 1.创建Request对象GetIndexRequest request new GetIndexRequest(items);// 2.发送请求boolean exists client.indices().exists(request, RequestOptions.DEFAULT);// 3.输出System.err.println(exists ? 索引库已经存在 : 索引库不存在);
}4.5、总结
JavaRestClient操作elasticsearch的流程基本类似。核心是client.indices()方法来获取索引库的操作对象。
索引库操作的基本步骤
初始化RestHighLevelClient创建xxxIndexRequest。xxx是Create、Get、Delete准备请求参数Create时需要其他是无参可以省略发送请求。调用RestHighLevelClient#indices().xxx()方法xxx是create、exists、delete
五、RestClient操作文档
索引库准备好以后就可以操作文档了。为了与索引库操作分离我们再创建一个测试类做两件事情
初始化RestHighLevelClient我们的商品数据在数据库需要利用IHOtelService去查询所以注入这个接口
package com.hmall.item.es;import com.hmall.item.service.IItemService;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.io.IOException;SpringBootTest(properties spring.profiles.activelocal)
public class DocumentTest {private RestHighLevelClient client;Autowiredprivate IItemService itemService;BeforeEachvoid setUp() {this.client new RestHighLevelClient(RestClient.builder(HttpHost.create(http://192.168.150.101:9200)));}AfterEachvoid tearDown() throws IOException {this.client.close();}
}5.1、新增文档
我们需要将数据库中的商品信息导入elasticsearch中而不是造假数据。
5.1.1、实体类
索引结构与数据库结构还存在一些差异因此我们定义一个索引库结构对应的实体。在hm-service模块的com.hmall.item.domain.dto包中定义一个新的DTO
package com.hmall.item.domain.po;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.time.LocalDateTime;Data
ApiModel(description 索引库实体)
public class ItemDoc{ApiModelProperty(商品id)private String id;ApiModelProperty(商品名称)private String name;ApiModelProperty(价格分)private Integer price;ApiModelProperty(商品图片)private String image;ApiModelProperty(类目名称)private String category;ApiModelProperty(品牌名称)private String brand;ApiModelProperty(销量)private Integer sold;ApiModelProperty(评论数)private Integer commentCount;ApiModelProperty(是否是推广广告true/false)private Boolean isAD;ApiModelProperty(更新时间)private LocalDateTime updateTime;
}5.1.2、API语法
新增文档的请求语法如下
POST /{索引库名}/_doc/1
{name: Jack,age: 21
}对应的JavaAPI如下 创建Request对象这里是IndexRequest因为添加文档就是创建倒排序索引的过程准备请求参数本例中就是Json文档发送请求
变化的地方在于这里直接使用client.xxx()的API不再需要client.indices()了。
5.1.3、完整代码
导入商品数据出了参考API模板“三步走”以外还要做以下准备工作
商品数据来自于数据库我们需要先查询出来得到Item对象Item对象需要转为ItemDocItemDTO需要序列化为json格式
因此代码整体步骤如下
根据id查询商品数据Item将Item封装为ItemDoc将ItemDoc序列化为JSON创建IndexRequest指定索引库名和id准备请求参数也就是JSON文档发送请求
在item-service的DocumentTest测试类中编写单元测试
Test
void testAddDocument() throws IOException {// 1.根据id查询商品数据Item item itemService.getById(100002644680L);// 2.转换为文档类型ItemDoc itemDoc BeanUtil.copyProperties(item, ItemDoc.class);// 3.将ItemDTO转jsonString doc JSONUtil.toJsonStr(itemDoc);// 1.准备Request对象IndexRequest request new IndexRequest(items).id(itemDoc.getId());// 2.准备Json文档request.source(doc, XContentType.JSON);// 3.发送请求client.index(request, RequestOptions.DEFAULT);
}5.2、查询文档
5.2.1、语法说明
查询的请求语句如下
GET/{索引库名}/_doc/{id}步骤如下
创建Request对象发送请求
不过查询的目的是得到结果解析为ItemDTO还要再加一步对结果的解析。示例代码如下 响应结果是一个JSON其中文档放在一个_source属性中因此解析就是拿到_source反序列化为Java对象即可。
流程如下
准备Request对象。这次是查询所以是GetRequest发送请求得到结果。因为是查询这里调用client.get()方法解析结果就是对JSON做反序列化
5.2.2、完整代码
在测试类中编写单元测试
Test
void testGetDocumentById() throws IOException {// 1.准备Request对象GetRequest request new GetRequest(items).id(100002644680);// 2.发送请求GetResponse response client.get(request, RequestOptions.DEFAULT);// 3.获取响应结果中的sourceString json response.getSourceAsString();ItemDoc itemDoc JSONUtil.toBean(json, ItemDoc.class);System.out.println(itemDoc ItemDoc);
}5.3、删除文档
请求语句如下
DELETE/hotel/_doc/{id}与查询相比仅仅是请求方式从GET变成DELETE,java代码依然是两步走
准备Request对象因为是删除所以通过DeleteRequest对象指定索引库名和id发送请求。因为是删除所以是client.delete()方法
在测试类中编写单元测试
Test
void testDeleteDocument() throws IOException {// 1.准备Request两个参数第一个是索引库名第二个是文档idDeleteRequest request new DeleteRequest(item, 100002644680);// 2.发送请求client.delete(request, RequestOptions.DEFAULT);
}5.4、修改文档
全量修改本质是先根据ID删除再新增局部修改修改文档中的指定字段值
在RestClient的API中全量修改与新增的API完全一致判断依据是id
如果新增时ID已经存在则修改如果新增时ID不存在则新增
5.4.1、语法说明
局部修改的请求语法如下
POST /{索引库名}/_update/{id}
{doc: {字段名: 字段值,字段名: 字段值}
}代码示例图 步骤如下
准备Request对象。修改的对象是UpdateRequest准备参数。也就是JSON文档里面包含要修改的字段发送请求。调用client.update()方法
5.4.2、完整代码
在测试类中编写单元测试
Test
void testUpdateDocument() throws IOException {// 1.准备Request第一个参数时索引库名第二个参数是idUpdateRequest request new UpdateRequest(items, 100002644680);// 2.准备请求参数request.doc(price, 58800,commentCount, 1);// 3.发送请求client.update(request, RequestOptions.DEFAULT);
}5.5、批量导入文档
在之前的案例中我们都是操作单个文档。而数据库中的商品数据实际会达到数十万条某些项目中可能达到数百万条。
将这些数据导入索引库肯定不能逐条导入而是采用批处理方案。常见的方案有
利用Logstash批量导入 需要安装Logstash对数据的再加工能力弱无需编码但要学习编写Logstash导入配置 利用JavaAPI批量导入 需要编码但基于JavaAPI学习成本低更加灵活可以任意对数据做再加工处理后写入索引库
5.5.1、语法说明
批处理与前面讲的CRUD步骤基本一致
创建Request批量使用的是BulkRequest准备参数发送请求调用的方法是client.bulk()
BulkRequest本身其实并没有请求参数其本质就是将多个普通的CRUD请求组合在一起发送。例如
批量新增文档就是给每个文档创建一个IndexRequest请求然后封装到BulkRequest中一起发出。批量删除就是创建N个DeleteRequest请求然后封装到BulkRequest一起发出
因此BulkRequest中提供了add方法用以添加其他CRUD的请求 能添加的请求有
IndexRequest,也就是新增UpdateRequest也就是修改DeleteRequest也就是删除
因此Bulk中添加了多个IndexRequest就是批量新增功能。
Test
void testBulk() throws IOException {// 1.创建RequestBulkRequest request new BulkRequest();// 2.准备请求参数request.add(new IndexRequest(items).id(1).source(json doc1, XContentType.JSON));request.add(new IndexRequest(items).id(2).source(json doc2, XContentType.JSON));// 3.发送请求client.bulk(request, RequestOptions.DEFAULT);
}5.5.2、完整代码
当我们要导入商品数据时由于商品数量达到数十万因此不可能一次性全部导入。建议采用循环遍历方式每次导入1000条左右的数据。
在测试类中编写单元测试
Test
void testLoadItemDocs() throws IOException {// 分页查询商品数据int pageNo 1;int size 1000;while (true) {PageItem page itemService.lambdaQuery().eq(Item::getStatus, 1).page(new PageItem(pageNo, size));// 非空校验ListItem items page.getRecords();if (CollUtils.isEmpty(items)) {return;}log.info(加载第{}页数据共{}条, pageNo, items.size());// 1.创建RequestBulkRequest request new BulkRequest(items);// 2.准备参数添加多个新增的Requestfor (Item item : items) {// 2.1.转换为文档类型ItemDTOItemDoc itemDoc BeanUtil.copyProperties(item, ItemDoc.class);// 2.2.创建新增文档的Request对象request.add(new IndexRequest().id(itemDoc.getId()).source(JSONUtil.toJsonStr(itemDoc), XContentType.JSON));}// 3.发送请求client.bulk(request, RequestOptions.DEFAULT);// 翻页pageNo;}
}5.6、小结
基本步骤
初始化RestHighLevelClient创建xxxRequest xxx是Index、Get、Update、Delete、Bulk 准备参数Index、Update、Bulk时需要发送请求 调用RestHighLevelClient#.xxx()方法xxx是index、get、update、delete、bulk 解析结果Get时需要