六、三级分类
1.按照父子类的结构获取所有分类
所有的分类都是一样的表,依靠表的级别和父标签来区分子父表。
要想一次性按父子结构打印出所有的分类,那么分类这个类中必须还得有子类,这个类要存在于entity中,但不能存在于数据库。代码如下:
@TableField(exist = false)
private List<CategoryEntity> children;
@Override
public List<CategoryEntity> listWithTree(){
//1、查出所有分类 null表名没有查找的条件 即全部查出
List<CategoryEntity> entities = baseMapper.selectList(null);
//2、组装成父子的树形结构
//2.1 找到所有的一级分类
List<CategoryEntity> level1Menus = entities.stream().filter(categoryEntity ->
categoryEntity.getParentCid()==0
).map((menu)->{
menu.setChildren(getChildrens(menu,entities));
return menu;
}).collect(Collectors.toList());
return level1Menus;
}
private List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){
List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
return categoryEntity.getParentCid == root.getCatId();
}).map(categoryEntity -> {
categoryEntity.setChildren(getChildrens(categoryEntity,all));
return categoryEntity;
}).sorted((menu1,menu2)->{
return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort())
}).collect(Collectors.toList());
return children;
}
2.跨域问题的解决
跨域:当前网址与请求的网址 协议、域名、端口三者不完全相同
解决思路:
1、使用nginx部署为同一域
2、让服务器告诉预检请求能跨域
在网关中定义配置类
package com.atguigu.gulimall.gateway.config;
@Configuration // gateway
public class GulimallCorsConfiguration {
@Bean // 添加过滤器
public CorsWebFilter corsWebFilter(){
// 基于url跨域,选择reactive包下的
UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
// 跨域配置信息
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 允许跨域的头
corsConfiguration.addAllowedHeader("*");
// 允许跨域的请求方式
corsConfiguration.addAllowedMethod("*");
// 允许跨域的请求来源
corsConfiguration.addAllowedOrigin("*");
// 是否允许携带cookie跨域
corsConfiguration.setAllowCredentials(true);
// 任意url都要进行跨域配置
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
这里配置跨域后前端访问还是会出错,因为脚手架工程renren-fast里面也对跨域做了一些配置,需要把那里面的配置注释掉,才能成功。
3.过滤器优先级问题
- id: ware_route
uri: lb://gulimall-ware
predicates:
- Path=/api/ware/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{
segment}
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters: # 这段过滤器和验证码有关,api内容缓存了/renren-fast,还得注意/renren-fast也注册到nacos中
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{
segment}
两个过滤器有明显的层次关系。 /api/** 包含了/api/ware/** 所以,要把比较精确的那个路由放到上面。
也就是把 /api/ware/** 放到 /api/** 的上面、
4.删除
@RequestBody:获取请求体,必须发送POST请求
SpringMVC自动将请求体的数据(json),转为对应的对象
多数时候我们并不希望真的把数据库中的数据删除,而只是采用逻辑删除。
也就是给所有数据加一个标记位,标记位为1则代表数据存在,为0则代表数据已被删除(举例)。
本质上就是把delete语句变成了update、当然这个可以去mybatis-plus中配置一下直接实现
application.yml:
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto
logic-delete-value: 1
logic-not-delete-value: 0
CategoryEntity :
/**
* 是否显示[0-不显示,1显示]
*/
@TableLogic(value = "1",delval = "0")
private Integer showStatus;
@TableLogic这个注解是要加的,让mybatis-plus识别这个是逻辑删除的标记位。置于什么标志代表是删还是不删,是可以通过在@TableLogic中加参数来声明,默认是1代表删,0代表没删。
前端的删除代码 就是发送post请求,把要逻辑删除的数据的ids发来
下面是vue前端代码
remove(node, data) {
var ids = [data.catId];
// 弹窗 确认
this.$confirm(`是否删除【${
data.name}】菜单?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
// 点击确定
this.$http({
// 给delete发送
url: this.$http.adornUrl("/product/category/delete"),
method: "post",
data: this.$http.adornData(ids, false)
}).then(({
data }) => {
// 删除成功$message
this.$message({
message: "菜单删除成功",
type: "success"
});
//刷新出新的菜单
this.getMenus();
//设置需要默认展开的菜单
this.expandedKey = [node.parent.data.catId];
});
})
.catch(// 取消
() => {
});
}
},
//生命周期 - 创建完成(可以访问当前this实例)
created() {
this.getMenus();
},
//生命周期 - 挂载完成(可以访问DOM元素)
mounted() {
},
beforeCreate() {
}, //生命周期 - 创建之前
beforeMount() {
}, //生命周期 - 挂载之前
beforeUpdate() {
}, //生命周期 - 更新之前
updated() {
}, //生命周期 - 更新之后
beforeDestroy() {
}, //生命周期 - 销毁之前
destroyed() {
}, //生命周期 - 销毁完成
activated() {
} //如果页面有keep-alive缓存功能,这个函数会触发
};
5.增加修改拖拽
都属于前端vue知识,在官网有具体的操作步骤,这里就偷懒了。
https://blog.csdn.net/hancoder/article/details/107612619
七、品牌管理
还是基本一样,数据库拉数据显示到前端页面。和前面三级分类一样的部分就不记了。
然后品牌的话,涉及到图片,所以这里用阿里云云仓库。
1.阿里云上传
和传统的单体应用不同,这里我们选择将数据上传到分布式文件服务器上。
这里我们选择将图片放置到阿里云上,使用对象存储。
和图床类似,可以直接手动在网页上上传,这里的图片存储空间叫bucket
下面主要介绍代码上传
2.代码上传品牌图片
代码上传中,使用的 “AccessKey ID”和“AccessKeySecret” 是阿里云用户的子用户,这也是为了保证安全。该子用户拥有root用户的所有读写对象权限。
新建一个微服务用于专门上传图片。
依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
具体按照官网操作即可
access-key secret-key endpoint 写入配置中心 然后再读取
@SpringBootTestclass GulimallThirdPartyApplicationTests {
@Autowired OSSClient ossClient; @Test public void testUpload() throws FileNotFoundException {
// Endpoint以杭州为例,其它Region请按实际情况填写。 String endpoint = "oss-cn-qingdao.aliyuncs.com"; // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。 String accessKeyId = "LTAI4G4W1RA4JXz2QhoDwHhi"; String accessKeySecret = "R99lmDOJumF2x43ZBKT259Qpe70Oxw"; // 创建OSSClient实例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); //上传文件流。 InputStream inputStream = new FileInputStream("C:\\Users\\HAN\\Downloads\\123.jpg"); ossClient.putObject("gulimall-fermhan", "333.jpg", inputStream); // 关闭OSSClient。 ossClient.shutdown(); System.out.println("上传成功."); }}
通过ossClient 操作、
上面的逻辑中,我们的想法是先把字节流给服务器,服务器给阿里云,还是传到了服务器。我们需要一些前端代码完成这个功能,字节流就别来服务器了
改进后:
@RestControllerpublic class OssController {
@Autowired OSS ossClient; @Value ("${spring.cloud.alicloud.oss.endpoint}") String endpoint ; @Value("${spring.cloud.alicloud.oss.bucket}") String bucket ; @Value("${spring.cloud.alicloud.access-key}") String accessId ; @Value("${spring.cloud.alicloud.secret-key}") String accessKey ; @RequestMapping("/oss/policy") public Map<String, String> policy(){
String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); String dir = format; // 用户上传文件时指定的前缀。 Map<String, String> respMap=null; try { // 签名有效事件 long expireTime = 30; long expireEndTime = System.currentTimeMillis() + expireTime * 1000; Date expiration = new Date(expireEndTime); PolicyConditions policyConds = new PolicyConditions(); policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); byte[] binaryData = postPolicy.getBytes("utf-8"); String encodedPolicy = BinaryUtil.toBase64String(binaryData); // 签名 String postSignature = ossClient.calculatePostSignature(postPolicy); respMap= new LinkedHashMap<String, String>(); respMap.put("accessid", accessId); respMap.put("policy", encodedPolicy); respMap.put("signature", postSignature); respMap.put("dir", dir); respMap.put("host", host); respMap.put("expire", String.valueOf(expireEndTime / 1000)); } catch (Exception e) { // Assert.fail(e.getMessage()); System.out.println(e.getMessage()); } finally { ossClient.shutdown(); } return respMap; }}
核心思想就是自己组装一个policy,发送给阿里云。这样的话,前端的请求只需要获取到这个policy,发送给阿里云就ok了。而不需要经过ossClient,也就是不需要经过服务器。
3.JSR303校验
前端提供一些校验,比如两次密码输入是否一致,然后定义一些输入规则。
详见原链接,或vue官网的组件。
后端可以通过一些注解来简单实现参数校验,主要在 javax.validation.constraints 包下,提供了如@Email,@NotNull等注解。
<!--jsr3参数校验器--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId></dependency>里面依赖了hibernate-validator
@Valid内置异常
这里内置异常的意思是发生异常时返回的json不是我们的R对象,而是mvc的内置类
@RequestMapping("/save")public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand); return R.ok();}
比如这样开启后,如果参数出错,返回的就不是R对象了,而是一些错误信息。
错误信息也可以自定义,比如:
@NotBlank(message = "品牌名必须非空") private String name;
这里有很多处理参数异常的注解,不细看了,因为实际开发中还不涉及,如果需要可以看:
https://blog.csdn.net/hancoder/article/details/107612619
统一异常处理
@Slf4j@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")//管理的controllerpublic class GulimallExceptionControllerAdvice { @ExceptionHandler(value = Exception.class) // 也可以返回ModelAndView public R handleValidException(MethodArgumentNotValidException exception){ Map<String,String> map=new HashMap<>(); // 获取数据校验的错误结果 BindingResult bindingResult = exception.getBindingResult(); // 处理错误 bindingResult.getFieldErrors().forEach(fieldError -> { String message = fieldError.getDefaultMessage(); String field = fieldError.getField(); map.put(field,message); }); log.error("数据校验出现问题{},异常类型{}",exception.getMessage(),exception.getClass()); return R.error(400,"数据校验出现问题").put("data",map); }}
basePackages 包下所有controller的异常都会由这个类 GulimallExceptionControllerAdvice 处理
八、商品SPU和SKU管理
1.概念
SPU:standard product unit(标准化产品单元):是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
SKU:stock keeping unit(库存量单位):库存进出计量的基本单元,可以是件/盒/托盘等单位。SKU是对于大型连锁超市DC配送中心物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品对应有唯一的SKU号。
也就是说SPU是product(产品)级别的、比如iphoneX iphone9 小米8 什么的
而比如,手机颜色,内存,显卡这些,就是SKU,是keeping(库存)级别的。根据SKU才可以确定到一个具体的商品
简单说 三级分类 -> SPU -> SKU
手机/运营商/数码 -> 手机通讯 -> 手机 -> Apple iPhone 11 (A2223) 128GB 白色 移动联通电信4G手机 双卡双待 -> 128G 公开版 …
2.三级分类表和属性表
三级分类表包含了所有分类,属性表里有所有三级分类所拥有的属性。但这些属性和三级分类并非一一对应,比如属性表有1,2,3,三级分类表有A,B,C,那么A可能有1,2属性,B有1,2,3属性,C有2,3属性,三级分类和属性多了甚至会更复杂。
所以牵扯到三级分类表和属性表,这里一共需要四张表,三级分类表,属性表,属性分组表,三级分类和属性分组对应表。
属性分组表,就比如,一号属性分组内有1,2,3属性,二号属性分组表内有2,3属性。之所以要多一个属性分组表,是因为三级分类里面有很多这样的情况:A分类属性有1,2,3, B分类属性也有1,2,3 所以有了分组,会更便于管理。
3.分页插件
https://mp.baomidou.com/
mybatis-plus一个配置可以搞定
@EnableTransactionManagement@MapperScan("com.atguigu.gulimall.product.dao")@Configurationpublic class MybatisConfig {
@Bean public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false paginationInterceptor.setOverflow(true); // 设置最大单页限制数量,默认 500 条,-1 不受限制 paginationInterceptor.setLimit(1000); return paginationInterceptor; }}
@MapperScan下面是dao接口的包
@EnableTransactionManagement是开启事务
4.模糊查询
@Override // BrandServiceImplpublic PageUtils queryPage(Map<String, Object> params) { QueryWrapper<BrandEntity> wrapper = new QueryWrapper<>(); String key = (String) params.get("key"); if(!StringUtils.isEmpty(key)){ // 字段等于 or 模糊查询 wrapper.eq("brand_id", key).or().like("name", key); } // 按照分页信息和查询条件 进行查询 IPage<BrandEntity> page = this.page( // 传入一个IPage对象,他是接口,实现类是Page new Query<BrandEntity>().getPage(params), wrapper ); return new PageUtils(page);}
此处的 .eq 意思是key必须和brand_id完全相同
而or的意思是两边条件是或的关系
like是模糊匹配 是左右模糊的 执行的sql类似于
wrapper就是存放匹配规则的model
SELECT * FROM users WHERE (firstname = '海' OR lastname LIKE '%海%')
5.冗余字段保持一致
也就是说,后台管理系统,修改一些字段的时候,涉及到的关联关系也要同步修改。此处略
https://blog.csdn.net/hancoder/article/details/107612619
6.PO VO DTO POJO
PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合。PO中应该不包含任何对数据的操作。
DTO这个概念来源于J2EE的设汁模式,原来的目的是为了EJB的分布式应用握供粗粒度的数据实体,以减少分布式调用的次数,从而握分布式调用的性能和降低网络负载,但在这里,泛指用于示层与服务层之间的数据传输对象。
VO通常用于业务层之间的数据传递,和PO一样也是仅仅包含数据而已。但应是抽象出的业务对象,可以和表对应,也可以不,这根据业务的需要。用new关韃字创建,由GC回收的。
POJO简单无规则java对象。
九、规格参数新增与VO
1.新建VO
现在的情况是,它在保存的时候,只是保存了attr,并没有保存attrgroup,为了解决这个问题,我们新建了一个vo/AttrVo.java,在原Attr基础上增加了attrGroupId字段,使得保存新增数据的时候,也保存了它们之间的关系。
通过" BeanUtils.copyProperties(attr,attrEntity);"能够实现在两个Bean之间属性对拷
简单来说就是,现在我这个请求需要的参数,并不是一个完整的数据库表的数据,也就是前端传来的数据并不是一个PO,此时就需要VO的新建。然后把VO的数据提取到各个PO中,再去业务层CRUD。
@Transactional@Overridepublic void saveAttr(AttrVo attrVo) {
AttrEntity attrEntity = new AttrEntity(); // 重要的工具 BeanUtils.copyProperties(attrVo, attrEntity); //1、保存基本数据 this.save(attrEntity); //2、保存关联关系 if (attrVo.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() && attrVo.getAttrGroupId() != null) { AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity(); relationEntity.setAttrGroupId(attrVo.getAttrGroupId()); relationEntity.setAttrId(attrEntity.getAttrId()); relationEntity.setAttrSort(0); relationDao.insert(relationEntity); }}
2.查询涉及到多个表
要分步骤查询,根据一个参数从一个表中查出一个结果,这个结果作为参数再去查另一个表。
不要多表联查。因为多表联查太耗费性能。
3.采购
采购需求的生成方式可能有两种:
- 人工新增
- 系统检测到库存量低自动创建
新建采购需求后还要可以提供合并采购单,比如一个仓库的东西可以合并到一起,让采购人员一趟采购完
领取采购单时要查询该采购单的状态是否是未领取,防止重复领取。
采购完成后更新库存。
十、ES
1.基本概念
index type document
分别相当于mysql的数据库 表 记录
ES通过倒排索引来提高查询效率、也就是关键词匹配
2.安装
docker中安装elastic search(也就是在linux虚拟机中docker pull)
下载ealastic search(存储和检索)和kibana(可视化检索)
3.配置
docker目录中的es目录挂载到linux 、 方便修改数据
设置es可以被任何机器访问
设置开机自启动 设置访问权限 设置占用内存的大小
kibana也一样 设置开机自启动 设置与es的交互端口 设置自己的主页端口
kibana只是一个es的可视化界面 不安装也可以 因为我们是通过发请求的方式操作es的
4.基本命令
这些命令是通过发送请求的方式 形如:http://192.168.56.10:9200/_cat/nodes
GET /_cat/nodes:查看所有节点
GET /_cat/health:查看es健康状况
GET /_cat/master:查看主节点
GET/_cat/indices:查看所有索引 ,等价于mysql数据库的show databases(之前说过 es的索引相当于mysql的数据库)
新增/修改
# # 在customer索引下的external类型下保存1号数据PUT customer/external/1# POSTMAN输入http://192.168.56.10:9200/customer/external/1{
"name":"John Doe"}
此处put也可以换成post 两者区别:
put必须有标号 而post可以没有 没有标号的时候会默认随机一个不重复的标号
当标号相同时,会从新增改为修改操作
结果:
返回数据:带有下划线开头的,称为元数据,反映了当前的基本信息。 {
"_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}
每次更新 _version 变化
_id可以指定 不指定会随机一个不重复的 _id
seq_no和version的区别:
每个文档的版本号" _ version" 起始值都为1 每次对当前文档成功操作后都加1
而序列号"_seq_no"则可以看做是索引的信息 在第一次为索引插入数据时为0,每对索引内数据操作成功一次 _seq_no 加1, 并且文档会记录是第几次操作使它成为现在的情况的
更新命令:(发送请求的方式)
POST customer/externel/1/_update{
"doc":{
"name":"111" }}或者POST customer/externel/1{
"doc":{
"name":"222" }}或者PUT customer/externel/1{
"doc":{
"name":"222" }}PUT http://192.168.56.10:9200/customer/external/1?if_seq_no=18&if_primary_term=6当 _seq_no 和 _primary_term 匹配时,才修改
可以看到更新和新增请求是有重复部分的
区别可以看原网页
主要就是两种
一种会对比原来的数据,一样就不动
一种是不对比,数据不变,版本号依然会增加
https://blog.csdn.net/hancoder/article/details/113922398
查询
GET /customer/external/1
http://192.168.56.10:9200/customer/external/1
查询结果示例:
{
"_index": "customer", "_type": "external", "_id": "1", "_version": 10, "_seq_no": 18,//并发控制字段,每次更新都会+1,用来做乐观锁 "_primary_term": 6,//同上,主分片重新分配,如重启,就会变化 "found": true, "_source": { "name": "John Doe" }}
删除
DELETE customer/external/1
DELETE customer
此处只能删除文档和索引(相当于数据库的一条数据和数据库)
es是不支持删除类型的(相当于数据库的一张表)
5.批量操作
执行多条数据(指定索引/类型)
POST /customer/external/_bulk{
"index":{
"_id":"1"}}{
"name":"John Doe"}{
"index":{
"_id":"2"}}{
"name":"John Doe"}
对于整个索引执行批量操作
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"}}
可以看到是在具体的条目上加了操作类型(delete、create、update),索引、类型、文档id(创建可以不加)
批量导入样本测试数据(此处仅导入一条,其余与此类似)
POST bank/account/_bulk{"index":{"_id":"1"}}{ "account_number": 1, "balance": 39225, "firstname": "Amber", "lastname": "Duke", "age": 32, "gender": "M", "address": "880 Holmes Lane", "employer": "Pyrami", "email": "[email protected]", "city": "Brogan", "state": "IL"}
6.进阶–检索
首先是检查一下虚拟机状态
sudo docker ps检查当前在运行的容器sudo docker ps -a检查容器中的所有应用(启动未启动都罗列)sudu docker start 84c 847 启动容器中84c 847唯一标识的应用(两个)sudo docker update 84c --restart=always让84c标识的应用每次都自动重启
请求参数方式检索GET bank/_search?q=*&sort=account_number:ascq就是检索条件 *代表查所有 sort是排序信息 account_number升序排列
uri+请求体进行检索GET /bank/_search{
"query": {
"match_all": {
} }, "sort": [ {
"account_number": "asc" }, {
"balance":"desc"} ]}
DSL领域特定语言
Elasticsearch提供了一个可以执行查询的Json风格的DSL(domain-specific language领域特定语言)。这个被称为Query DSL,该查询语言非常全面。
示例 使用时不要加#注释内容GET bank/_search{
"query": {
# 查询的字段 "match_all": {
} }, "from": 0, # 从第几条文档开始查 "size": 5, "_source":["balance","firstname"], # 返回哪些字段 不写就全部返回 "sort": [ {
"account_number": {
# 返回结果按哪个列排序 "order": "desc" # 降序 } } ]}_source为要返回的字段from+size限定,完成分页功能;sort排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准;
query/match匹配查询
如果是非字符串,会进行精确匹配。如果是字符串,会进行全文检索,然后按照匹配度降序返回。
GET bank/_search{ "query": { "match": { "account_number": "20" } }}GET bank/_search{ "query": { "match": { "address": "mill road" # 匹配到的包括:包含mill的 包含road的 包含mill road的 } }}
query/match_phrase [不拆分匹配]
前面的是包含mill或road就查出来,我们现在要都包含才查出
GET bank/_search{ "query": { "match_phrase": { "address": "mill road" # 就是说不要匹配只有mill或只有road的,要匹配mill road一整个子串 } }}
字段.keyword:必须全匹配上才检索成功
GET bank/_search{ "query": { "match": { "address.keyword": "990 Mill" # 字段后面加上 .keyword } }}
address必须为"990 Mill" 有一点差别都不能匹配
query/multi_math【多字段匹配】
state或者address中包含mill,并且在查询过程中,会对于查询条件进行分词。
GET bank/_search{ "query": { "multi_match": { # 前面的match仅指定了一个字段。 "query": "mill", "fields": [ # state和address有mill子串 不要求都有 "state", "address" ] } }}
query/bool/must复合查询
也就是bool查询 内部可以写多个match语句
GET bank/_search{
"query": {
"bool": {
"must": [ {
"match": {
"gender": "M" #gender必须包含M } }, {
"match": {
"address": "mill" #address必须包含mill } } ], "must_not": [ {
"match": {
"age": "18" #年龄必须不包含18 } } ], "should": [ {
"match": {
"lastname": "Wallace" #lastname最好是wallace 不是也可以 只影响max_score(匹配度) } } ] } }}
query/filter【结果过滤】
filter对查询的最终结果进行过滤 不贡献得分(max_score)
GET bank/_search{
"query": {
"bool": {
"must": [ {
"match": {
"address": "mill" } } ], "filter": {
# query.bool.filter "range": {
"balance": {
# 哪个字段 过滤剩下balance在10000~20000之间的 且该条件不会影响匹配得分 "gte": "10000", "lte": "20000" } } } } }}
query/term
和match一样。匹配某个属性的值。
- 全文检索字段用match,
- 其他非text字段匹配用term。
不要使用term来进行文本字段查询
GET bank/_search{
"query": {
"term": {
"age": "30" } }}