分布式电商项目 谷粒商城 学习笔记<2>

六、三级分类

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

猜你喜欢

转载自blog.csdn.net/GBS20200720/article/details/121188943