文章目录
谷粒商城_01_环境搭建
谷粒商城_02_Nacos、网关
谷粒商城_03_前端基础
谷粒商城_04_商品CRUD
谷粒商城_05_阿里云OSS和前端校验
JSR303校验
问题引入:填写form时应该有前端校验,后端也应该有校验
前端的校验是element-ui表单验证:https://element.eleme.cn/#/zh-CN/component/form
-
Form 组件提供了表单验证的功能,只需要通过
rules
属性传入约定的验证规则,并将 Form-Item 的prop
属性设置为需校验的字段名即可。校验规则参见 async-validator -
使用自定义校验规则可以解决字母限制、密码不一致、非整数限制等等的问题
-
基本使用
// dataForm:表单的数据、dataRule:所有检验的规则 <el-form :model="dataForm" :rules="dataRule"/> // 表单的某一项用prop指定校验的目标即可使用 <el-form-item label="密码" prop="checkPass"> // data() { return { dataRule: { // 密码校验的字段checkPass,validator: validatePass指向校验的具体方法,既可以将其替换成方法使用 checkPass: [ { validator: validatePass, trigger: 'blur' } ], } } } var validatePass = (rule, value, callback) => { if (value === '') { callback(new Error('请再次输入密码')); // dataForm表单的数据 } else if (value !== this.dataForm.pass) { callback(new Error('两次输入密码不一致!')); } else { callback(); } };
-
后端:@NotNull、@NotBank、@URL等等
普通校验
1、使用校验注解
在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints、org.hibernate.validator.constraints
”包中,提供了如@Email、@NotNull、@URL等注解。
- 主要看注解源码文档来了解其中使用
<!--jsr3参数校验器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.6.1</version>
</dependency>
2、@Valid内置异常
这里内置异常的意思是发生异常时返回的json不是我们的R对象,而是mvc的内置类
在controller类上加:@Validated,方法参数中加校验注解:@Valid,开启校验,
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
测试: http://localhost:88/api/product/brand/save,在postman种发送上面的请求,发现返回的是json对象,但不是我们的R对象
// 其中
"defaultMessage": "不能为空",
“defaultMessage”: “不能为空”,这些错误消息定义在“hibernate-validator
”的“\org\hibernate\validator\ValidationMessages_zh_CN.properties”文件中。在该文件中定义了很多的错误规则:
javax.validation.constraints.AssertFalse.message = 只能为false
javax.validation.constraints.AssertTrue.message = 只能为true
javax.validation.constraints.DecimalMax.message = 必须小于或等于{value}
javax.validation.constraints.DecimalMin.message = 必须大于或等于{value}
javax.validation.constraints.Digits.message = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
javax.validation.constraints.Email.message = 不是一个合法的电子邮件地址
javax.validation.constraints.Future.message = 需要是一个将来的时间
javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
javax.validation.constraints.Max.message = 最大不能超过{value}
javax.validation.constraints.Min.message = 最小不能小于{value}
javax.validation.constraints.Negative.message = 必须是负数
javax.validation.constraints.NegativeOrZero.message = 必须是负数或零
javax.validation.constraints.NotBlank.message = 不能为空
javax.validation.constraints.NotEmpty.message = 不能为空
javax.validation.constraints.NotNull.message = 不能为null
javax.validation.constraints.Null.message = 必须为null
javax.validation.constraints.Past.message = 需要是一个过去的时间
javax.validation.constraints.PastOrPresent.message = 需要是一个过去或现在的时间
javax.validation.constraints.Pattern.message = 需要匹配正则表达式"{regexp}"
javax.validation.constraints.Positive.message = 必须是正数
javax.validation.constraints.PositiveOrZero.message = 必须是正数或零
javax.validation.constraints.Size.message = 个数必须在{min}和{max}之间
org.hibernate.validator.constraints.CreditCardNumber.message = 不合法的信用卡号码
org.hibernate.validator.constraints.Currency.message = 不合法的货币 (必须是{value}其中之一)
org.hibernate.validator.constraints.EAN.message = 不合法的{type}条形码
org.hibernate.validator.constraints.Email.message = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.CodePointLength.message = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}的校验码不合法, ${modType}校验和不匹配
org.hibernate.validator.constraints.NotBlank.message = 不能为空
org.hibernate.validator.constraints.NotEmpty.message = 不能为空
org.hibernate.validator.constraints.ParametersScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.Range.message = 需要在{min}和{max}之间
org.hibernate.validator.constraints.SafeHtml.message = 可能有不安全的HTML内容
org.hibernate.validator.constraints.ScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.URL.message = 需要是一个合法的URL
org.hibernate.validator.constraints.time.DurationMax.message = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
org.hibernate.validator.constraints.time.DurationMin.message = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
想要自定义错误消息,可以覆盖默认的错误提示信息,如@NotBlank的默认message是
public @interface NotBlank {
String message() default "{javax.validation.constraints.NotBlank.message}";
}
可以在添加注解的时候,修改message:
@NotBlank(message = "品牌名必须非空")
private String name;
当再次发送请求时,得到的错误提示信息:
"defaultMessage": "品牌名必须非空",
但是返回的错误不是R对象,影响接收端的接收,我们可以通过局部异常处理或者统一一次处理解决
局部异常处理BindResult
- 给校验的Bean后,紧跟一个BindResult,就可以获取到校验的结果。拿到校验的结果,就可以自定义的封装。
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand,
BindingResult result){ // 手动处理异常
if( result.hasErrors()){
Map<String,String> map=new HashMap<>();
//1.获取错误的校验结果
result.getFieldErrors().forEach((item)->{
//获取发生错误时的message,提示信息
String message = item.getDefaultMessage();
//获取发生错误的属性的名字
String field = item.getField();
map.put(field,message);
});
return R.error(400,"提交的数据不合法").put("data",map);
}else {
}
brandService.save(brand);
return R.ok();
}
这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理。
统一异常处理
@ExceptionHandler
- @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。
1、抽取一个异常处理类
@ControllerAdvice
标注在类上,通过“basePackages”能够说明处理哪些路径下的异常。@ExceptionHandler(value = 异常类型.class)
标注在方法上- 这里的ConstraintViolationException异常我和项目中不一样,但是可以通过源码定位到里面的属性,然后获取异常信息放入map
/**
* @author ljy
* @version 1.0.0
* @Description 集中处理所有异常
* @createTime 2021年12月14日 19:22:00
*/
@Slf4j
// @RestControllerAdvice和@ControllerAdvice的关系类似于@RestController
@RestControllerAdvice(basePackages = "com.liu.gulimall.product.controller")// 管理的controller
public class GulimallExceptionControllerAdvice {
@ExceptionHandler(value = ConstraintViolationException.class) // 感知精确异常
// 也可以返回ModelAndView
// exception是controller中的异常,精确匹配ConstraintViolationException这个异常
// MethodArgumentNotValidException
public R handleValidException(ConstraintViolationException exception){
Map<String,String> map=new HashMap<>();
// 获取数据校验的错误结果,和之前一样BindingResult
exception.getConstraintViolations().forEach(item->{
String message = item.getMessage();
Path propertyPath = item.getPropertyPath();
map.put(propertyPath.toString(),message);
});
log.error("数据校验出现问题{},异常类型{}",exception.getMessage(),exception.getClass());
return R.error(400,"数据校验出现问题").put("data",map);
}
@ExceptionHandler(value = Throwable.class)//异常的范围更大
public R handleException(Throwable throwable){
log.error("未知异常{},异常类型{}",
throwable.getMessage(),
throwable.getClass());
return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),
BizCodeEnum.UNKNOW_EXEPTION.getMsg());
}
}
(2)测试: http://localhost:9999/product/brand/save
(3)默认异常处理
@ExceptionHandler(value = Throwable.class)// 异常的范围更大
public R handleException(Throwable throwable){
log.error("未知异常{},异常类型{}",
throwable.getMessage(),
throwable.getClass());
return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),
BizCodeEnum.UNKNOW_EXEPTION.getMsg());
}
(4)错误状态码
正规开发过程中,错误状态码有着严格的定义规则,在项目中我们的错误状态码定义
上面的用法主要是通过@Controller+@ExceptionHandler
来进行异常拦截处理,单独定义一个枚举类,用来存储这些错误状态码
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:10001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*/
/**
* @author ljy
* @version 1.0.0
* @Description TODO
* @createTime 2021年12月14日 19:23:00
*/
public enum BizCodeEnum {
UNKNOW_EXEPTION(10000,"系统未知异常"),
VALID_EXCEPTION( 10001,"参数格式校验失败");
private int code;
private String msg;
BizCodeEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
分组校验功能(多场景校验)
前面解决了统一异常处理,但是同一实体比如id,修改的时候不能为空,添加的时候为空,那怎么来校验呢?
我们采取分组的方式
1、定义分组接口,A(添加分组)、B(删除分组),接口只是标识
2、在controller方法中添加的方法中使用 @Validated(A.class)
// 表示添加的时候采用A分组策略
public R save(@Validated({A.class}) @RequestBody BrandEntity brand){
3、在实体属性上,比如id,@NotNull(message = “修改id不为空”,groups = {B.class})、@Null(message = “新增id必须为空”,groups = {A.class})
@NotNull(message = "修改必须定制品牌id", groups = {UpdateGroup.class})
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@TableId
private Long brandId;
在这种情况下,没有指定分组的校验注解,默认是不起作用的。想要起作用就必须要加groups。
- controller接收到之后,根据@Validated表明的分组信息,然后确定品牌对应的校验注解。
总结:
package com.liu.common.valid;
/**
* @author ljy
* @version 1.0.0
* @Description TODO
* @createTime 2021年12月14日 20:49:00
*/
public interface addGroup {
}
========================
/**
* 保存
*/
@RequestMapping("/save")
public R save(@Validated({addGroup.class}) @RequestBody BrandEntity brand, BindingResult result){
brandService.save(brand);
return R.ok();
}
========================
/**
* 品牌名
*/
@NotBlank(message = "品牌名不能为空", groups = {addGroup.class})
private String name;
========================
{
"msg": "提交的数据不合法",
"code": 400,
"data": {
"name": "品牌名不能为空"
}
}
自定义校验注解
Hibernate Validator提供了一系列内置的校验注解,可以满足大部分的校验需求。但是,仍然有一部分校验需要特殊定制
比如:要校验showStatus
的0/1状态,只能是0,1两个值
1、添加依赖
<!--校验-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!--高版本需要javax.el-->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b08</version>
</dependency>
2、自定义注解
/**
* @author ljy
* @version 1.0.0
* @Description 自定义注解,模仿其他的检验注解即可
* @createTime 2021年12月14日 20:57:00
*/
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class}) // 校验器,指定校验器,需要我们自己定义
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) // 在哪可以标注此注解
@Retention(RUNTIME)
public @interface ListValue {
// 必须有3个属性
// 使用该属性去Validation.properties中取
String message() default "{com.liu.common.valid.ListValue.message}"; // 错误信息
Class<?>[] groups() default { }; // 分组校验
Class<? extends Payload>[] payload() default { }; // 自定义负载信息
// 数组,需要用户自己指定
int[] value() default {};
}
因为上面的message值对应的最终字符串需要去ValidationMessages.properties中获得,所以我们在common中新建文件ValidationMessages.properties
3、ValidationMessages.properties
com.liu.common.valid.ListValue.message=必须提交指定的值 [0,1]
4、自定义校验器
- 通过源码:@Constraint注解的validatedBy是一个ConstraintValidator类型,并且里面包含了注解和校验类型,所以我们自定义实现ConstraintValidator,放入我们的注解和类型
/**
* @author ljy
* @version 1.0.0
* @Description TODO
* @createTime 2021年12月14日 21:06:00
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
// 存储所有可能的值
private Set<Integer> set=new HashSet<Integer>();
// 初始化,你可以获取注解上的内容并进行处理
public void initialize(ListValue constraintAnnotation) {
// 获取后端写好的限制 // 这个value就是ListValue里的value,我们写的注解是@ListValue(value={0,1})
int[] value = constraintAnnotation.value();
for (int i : value) {
set.add(i);
}
}
/**
* 覆写验证逻辑
* @param value 需要校验的值
* @param context
* @return
*/
public boolean isValid(Integer value, ConstraintValidatorContext context) {
// 看是否在限制的值里,是返回true
return set.contains(value);
}
}
5、使用
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(value = {0,1},groups ={addGroup.class})
private Integer showStatus;
6、测试
{
"msg": "提交的数据不合法",
"code": 400,
"data": {
"showStatus": "必须提交指定的值 [0,1]"
}
}
Elasticsearch
初步检索
1、检索es信息
(1)GET /_cat/nodes
:查看所有节点 :http://localhost:9200/_cat/nodes
(2)GET /_cat/health
:查看es健康状况:green表示健康值正常
(3)GET /_cat/master
:查看主节点
(4)GET/_cat/indicies
:查看所有索引 ,等价于mysql数据库的show databases;
2、新增文档
保存一个数据,保存在哪个索引的哪个类型下(哪张数据库哪张表下),保存时用唯一标识指定
# 在customer索引下的external类型下保存1号数据
PUT customer/external/1
{
"name":"John Doe"
}
PUT和POST区别
- POST新增。如果不指定id,会自动生成id,指定id就会修改这个数据,并新增版本号;
- 可以不指定id,不指定id时永远为创建
- 指定不存在的id为创建
- 指定存在的id为更新,而版本号会根据内容变没变而觉得版本号递增与否
- PUT可以新增也可以修改。PUT必须指定id;由于PUT需要指定id,我们一般用来做修改操作,不指定id会报错。
- 必须指定id
- 版本号总会增加
seq_no和version的区别:
- 每个文档的版本号"
_version
" 起始值都为1 每次对当前文档成功操作后都加1- 而序列号"
_seq_no
"则可以看做是索引的信息 在第一次为索引插入数据时为0,每对索引内数据操作成功一次sqlNO
加1, 并且文档会记录是第几次操作使它成为现在的情况的
下面是在postman中选用POST方式的测试数据:
创建数据成功后,显示201 created表示插入记录成功。
返回数据:
带有下划线开头的,称为元数据,反映了当前的基本信息。
{
"_index": "customer", 表明该数据在哪个数据库下;
"_type": "external", 表明该数据在哪个类型下;
"_id": "1", 表明被保存数据的id;
"_version": 1, 被保存数据的版本
"result": "created", 这里是创建了一条数据,如果重新put一条数据,则该状态会变为updated,并且版本号也会发生变化。
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
12345678910111213141516
下面选用POST方式:
添加数据的时候,不指定ID,会自动的生成id,并且类型是新增:
{
"_index": "customer",
"_type": "external",
"_id": "5MIjvncBKdY1wAQm-wNZ",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 11,
"_primary_term": 6
}
1234567891011121314
再次使用POST插入数据,不指定ID,仍然是新增的:
{
"_index": "customer",
"_type": "external",
"_id": "5cIkvncBKdY1wAQmcQNk",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 12,
"_primary_term": 6
}
添加数据的时候,指定ID,会使用该id,并且类型是新增:
{
"_index": "customer",
"_type": "external",
"_id": "2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 13,
"_primary_term": 6
}
再次使用POST插入数据,指定同样的ID,类型为updated
{
"_index": "customer",
"_type": "external",
"_id": "2",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 14,
"_primary_term": 6
}
3、查看文档
GET /customer/external/1
{
"_index": "customer",
"_type": "external",
"_id": "1",
"_version": 10,
"_seq_no": 18,//并发控制字段,每次更新都会+1,用来做乐观锁
"_primary_term": 6,//同上,主分片重新分配,如重启,就会变化
"found": true,
"_source": {
"name": "John Doe"
}
}
123456789101112
乐观锁用法:通过“
if_seq_no=1&if_primary_term=1
”,当序列号匹配的时候,才进行修改,否则不修改。
实例:将id=1的数据更新为name=1,然后再次更新为name=2,起始1_seq_no=18,_primary_term=6
(1)将name更新为1
PUT http://localhost:9200/customer/external/1?if_seq_no=18&if_primary_term=6
(2)将name更新为2,更新过程中使用seq_no=18
PUT http://localhost:9200/customer/external/1?if_seq_no=18&if_primary_term=6
结果为:出现更新错误。
(3)查询新的数据
GET http://localhost:9200/customer/external/1
{
"_index": "customer",
"_type": "external",
"_id": "1",
"_version": 11,
"_seq_no": 19,
"_primary_term": 6,
"found": true,
"_source": {
"name": "1"
}
}
能够看到_seq_no变为19
(4)再次更新,更新成功
4、更新文档_update
POST customer/externel/1/_update
{
"doc":{
"name":"111"
}
}
或者
POST customer/externel/1
{
"doc":{
"name":"222"
}
}
或者
PUT customer/externel/1
{
"doc":{
"name":"222"
}
}
不同:带有update情况下
- POST操作会对比源文档数据,如果相同不会有什么操作,文档version不增加。
- PUT操作总会重新保存并增加version版本
POST时带_update
对比元数据如果一样就不进行任何操作。
看场景:
- 对于大并发更新,不带update
- 对于大并发查询偶尔更新,带update;对比更新,重新计算分配规则
(1)POST更新文档,带有_update
如果再次执行更新,则不执行任何操作,序列号也不发生变化
返回
{
"_index": "customer",
"_type": "external",
"_id": "1",
"_version": 12,
"result": "noop", // 无操作
"_shards": {
"total": 0,
"successful": 0,
"failed": 0
},
"_seq_no": 20,
"_primary_term": 6
}
POST更新方式,会对比原来的数据,和原来的相同,则不执行任何操作(version和_seq_no)都不变。
(2)POST更新文档,不带_update,和put一样
在更新过程中,重复执行更新操作,数据也能够更新成功,不会和原来的数据进行对比。
{
"_index": "customer",
"_type": "external",
"_id": "1",
"_version": 13,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 21,
"_primary_term": 6
}
5、删除文档或索引
DELETE customer/external/1
DELETE customer // 删除索引
注:elasticsearch并没有提供删除类型的操作,只提供了删除索引和文档的操作。
实例:删除id=1的数据,删除后继续查询
DELETE http://localhost:9200/customer/external/1
{
"_index": "customer",
"_type": "external",
"_id": "1",
"_version": 14,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 22,
"_primary_term": 6
}
再次执行DELETE http://localhost:9200/customer/external/1
{
"_index": "customer",
"_type": "external",
"_id": "1",
"_version": 15,
"result": "not_found",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 23,
"_primary_term": 6
}
GET http://localhost:9200/customer/external/1
{
"_index": "customer",
"_type": "external",
"_id": "1",
"found": false
}
123456
删除索引
实例:删除整个costomer索引数据
删除前,所有的索引http://localhost:9200/_cat/indices
green open .kibana_task_manager_1 DhtDmKrsRDOUHPJm1EFVqQ 1 0 2 0 31.3kb 31.3kb
green open .apm-agent-configuration vxzRbo9sQ1SvMtGkx6aAHQ 1 0 0 0 283b 283b
green open .kibana_1 rdJ5pejQSKWjKxRtx-EIkQ 1 0 8 3 28.8kb 28.8kb
yellow open customer mG9XiCQISPmfBAmL1BPqIw 1 1 9 1 8.6kb 8.6kb
删除“ customer ”索引
DELTE http://localhost:9200/customer
响应
{
"acknowledged": true
}
删除后,所有的索引 http://localhost:9200/_cat/indices
green open .kibana_task_manager_1 DhtDmKrsRDOUHPJm1EFVqQ 1 0 2 0 31.3kb 31.3kb
green open .apm-agent-configuration vxzRbo9sQ1SvMtGkx6aAHQ 1 0 0 0 283b 283b
green open .kibana_1 rdJ5pejQSKWjKxRtx-EIkQ 1 0 8 3 28.8kb 28.8kb
6、ES的批量操作——bulk
匹配导入数据
POST http://localhost:9200/customer/external/_bulk
两行为一个整体
{"index":{"_id":"1"}}
{"name":"a"}
{"index":{"_id":"2"}}
{"name":"b"}
注意格式json和text均不可,要去kibana里Dev Tools
语法格式:
{action:{metadata}}\n
{request body }\n
{action:{metadata}}\n
{request body }\n
这里的批量操作,当发生某一条执行发生失败时,其他的数据仍然能够接着执行,也就是说彼此之间是独立的。
bulk api以此按顺序执行所有的action(动作)。如果一个单个的动作因任何原因失败,它将继续处理它后面剩余的动作。当bulk api返回时,它将提供每个动作的状态(与发送的顺序相同),所以您可以检查是否一个指定的动作是否失败了。
实例1: 执行多条数据
POST /customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"John Doe"}
{"index":{"_id":"2"}}
{"name":"John Doe"}
12345
执行结果
#! Deprecation: [types removal] Specifying types in bulk requests is deprecated.
{
"took" : 318, 花费了多少ms
"errors" : false, 没有发生任何错误
"items" : [ 每个数据的结果
{
"index" : { 保存
"_index" : "customer", 索引
"_type" : "external", 类型
"_id" : "1", 文档
"_version" : 1, 版本
"result" : "created", 创建
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1,
"status" : 201 新建完成
}
},
{
"index" : { 第二条记录
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1,
"status" : 201
}
}
]
}
实例2:对于整个索引执行批量操作
POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":"123"}}
{"create":{"_index":"website","_type":"blog","_id":"123"}}
{"title":"my first blog post"}
{"index":{"_index":"website","_type":"blog"}}
{"title":"my second blog post"}
{"update":{"_index":"website","_type":"blog","_id":"123"}}
{"doc":{"title":"my updated blog post"}}
7、样本测试数据
准备了一份顾客银行账户信息的虚构的JSON文档样本。每个文档都有下列的schema(模式)。
{
"account_number": 1,
"balance": 39225,
"firstname": "Amber",
"lastname": "Duke",
"age": 32,
"gender": "M",
"address": "880 Holmes Lane",
"employer": "Pyrami",
"email": "amberduke@pyrami.com",
"city": "Brogan",
"state": "IL"
}
POST bank/account/_bulk
上面的数据
http://localhost:9200/_cat/indices
刚导入了1000条
yellow open bank 99m64ElxRuiH46wV7RjXZA 1 1 1000 0 427.8kb 427.8kb