mall

  1. SpringBoot
    1. 父工程依赖
    2. 常用起步依赖
    3. 第三方依赖
  2. ES的使用
    1. 基本使用
    2. 数据导入
    3. 基本搜索
    4. 条件搜索
    5. 组合搜索
    6. 过滤搜索
    7. 搜索聚合
    8. 整合到SpringBoot
  3. 项目结构

SpringBoot

父工程依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.5</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <skipTests>true</skipTests>
    <docker.host>http://192.168.3.101:2375</docker.host>
    <docker.maven.plugin.version>0.40.2</docker.maven.plugin.version>
    <pagehelper-starter.version>1.4.5</pagehelper-starter.version>
    <pagehelper.version>5.3.2</pagehelper.version>
    <druid.version>1.2.14</druid.version>
    <hutool.version>5.8.9</hutool.version>
    <springfox-swagger.version>3.0.0</springfox-swagger.version>
    <swagger-models.version>1.6.0</swagger-models.version>
    <swagger-annotations.version>1.6.0</swagger-annotations.version>
    <mybatis-generator.version>1.4.1</mybatis-generator.version>
    <mybatis.version>3.5.10</mybatis.version>
    <mybatis-starter.version>2.2.2</mybatis-starter.version>
    <mysql-connector.version>8.0.29</mysql-connector.version>
    <spring-data-commons.version>2.7.5</spring-data-commons.version>
    <jjwt.version>0.9.1</jjwt.version>
    <aliyun-oss.version>2.5.0</aliyun-oss.version>
    <logstash-logback.version>7.2</logstash-logback.version>
    <minio.version>8.4.5</minio.version>
    <jaxb-api.version>2.3.1</jaxb-api.version>
</properties>

<dependencies>
    <!--解决JDK 11 兼容性问题-->
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>${jaxb-api.version}</version>
    </dependency>
</dependencies>

<!--使用aliyun的Maven镜像源提升下载速度-->
<repositories>
    <repository>
        <id>aliyunmaven</id>
        <name>aliyun</name>
        <url>https://maven.aliyun.com/repository/public</url>
    </repository>
    <repository>
        <id>central2</id>
        <name>central2</name>
        <url>https://repo1.maven.org/maven2/</url>
    </repository>
</repositories>

常用起步依赖

<dependencies>
    <!--SpringBoot整合Web功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--SpringBoot整合Actuator功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--SpringBoot整合AOP功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!--SpringBoot整合测试功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!--SpringBoot整合注解处理功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <!--SpringBoot整合Spring Security安全功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!--SpringBoot整合Redis数据存储功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--SpringBoot整合Elasticsearch数据存储功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    <!--SpringBoot整合MongoDB数据存储功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <!--SpringBoot整合AMQP消息队列功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <!--SpringBoot整合Quartz定时任务功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    <!--SpringBoot整合JPA数据存储功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!--SpringBoot整合邮件发送功能依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
</dependencies>

第三方依赖

<dependencies>
    <!--SpringBoot整合MyBatis数据存储功能依赖-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis-version.version}</version>
    </dependency>
    <!--SpringBoot整合PageHelper分页功能依赖-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>${pagehelper-starter.version}</version>
    </dependency>
    <!--SpringBoot整合Druid数据库连接池功能依赖-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>${druid.version}</version>
    </dependency>  
    <!--SpringBoot整合Springfox的Swagger API文档功能依赖-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>${springfox-version}</version>
    </dependency>
    <!--SpringBoot整合MyBatis-Plus数据存储功能依赖-->  
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>${mybatis-plus-version}</version>
    </dependency>
    <!--SpringBoot整合Knife4j API文档功能依赖--> 
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>${knife4j-version}</version>
    </dependency>        
</dependencies>

ES的使用

基本使用

索引-类型-文档-字段

  • Near Realtime(近实时):Elasticsearch是一个近乎实时的搜索平台,这意味着从索引文档到可搜索文档之间只有一个轻微的延迟(通常是一秒钟)。

  • Cluster(集群):群集是一个或多个节点的集合,它们一起保存整个数据,并提供跨所有节点的联合索引和搜索功能。每个集群都有自己的唯一集群名称,节点通过名称加入集群。

  • Node(节点):节点是指属于集群的单个Elasticsearch实例,存储数据并参与集群的索引和搜索功能。可以将节点配置为按集群名称加入特定集群,默认情况下,每个节点都设置为加入一个名为elasticsearch的群集。

  • Index(索引):索引是一些具有相似特征的文档集合,类似于MySql中数据库的概念。

  • Type(类型):类型是索引的逻辑类别分区,通常,为具有一组公共字段的文档类型,类似MySql中表的概念。注意:在Elasticsearch 6.0.0及更高的版本中,一个索引只能包含一个类型。

  • Document(文档):文档是可被索引的基本信息单位,以JSON形式表示,类似于MySql中行记录的概念。

  • Shards(分片):当索引存储大量数据时,可能会超出单个节点的硬件限制,为了解决这个问题,Elasticsearch提供了将索引细分为分片的概念。分片机制赋予了索引水平扩容的能力、并允许跨分片分发和并行化操作,从而提高性能和吞吐量。

  • Replicas(副本):在可能出现故障的网络环境中,需要有一个故障切换机制,Elasticsearch提供了将索引的分片复制为一个或多个副本的功能,副本在某些节点失效的情况下提供高可用性。

  • 通过Kibana的Dev Tools功能,我们可以操作Elasticsearch;

    # 查看集群健康状态
    GET /_cat/health?v
    
    # 查看节点状态
    GET /_cat/nodes?v
    
    # 索引操作
    # 查看所有索引信息
    GET /_cat/indices?v
    # 创建索引并查看
    PUT /customer
    GET /_cat/indices?v
    # 删除索引并查看
    DELETE /customer
    GET /_cat/indices?v
    
    # 类型操作
    # 查看文档的类型,需要完成数据搜索部分的导入才可以查看
    GET /bank/_mapping
    
    # 文档操作
    # 在索引中添加文档;
    PUT /customer/doc/1
    {
      "name": "John Doe"
    }
    # 查看索引中的文档
    GET /customer/doc/1
    #
    {
      "_index" : "customer",
      "_type" : "doc",
      "_id" : "1",
      "_version" : 1,
      "_seq_no" : 0,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "name" : "John Doe"
      }
    }
    # 修改索引中的文档
    POST /customer/doc/1/_update
    {
      "doc": { "name": "Jane Doe" }
    }
    #
    {
      "_index": "customer",
      "_type": "doc",
      "_id": "1",
      "_version": 2,
      "result": "updated",
      "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
      },
      "_seq_no": 4,
      "_primary_term": 1
    }
    
    # 删除索引中的文档
    DELETE /customer/doc/1
    #
    {
      "_index" : "customer",
      "_type" : "doc",
      "_id" : "1",
      "_version" : 2,
      "result" : "deleted",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 1,
      "_primary_term" : 1
    }
    
    # 对索引中的文档执行批量操作
    POST /customer/doc/_bulk
    {"index":{"_id":"1"}}
    {"name": "John Doe" }
    {"index":{"_id":"2"}}
    {"name": "Jane Doe" }
    #
    {
      "took" : 9,
      "errors" : false,
      "items" : [
        {
          "index" : {
            "_index" : "customer",
            "_type" : "doc",
            "_id" : "1",
            "_version" : 3,
            "result" : "created",
            "_shards" : {
              "total" : 2,
              "successful" : 1,
              "failed" : 0
            },
            "_seq_no" : 2,
            "_primary_term" : 1,
            "status" : 201
          }
        },
        {
          "index" : {
            "_index" : "customer",
            "_type" : "doc",
            "_id" : "2",
            "_version" : 1,
            "result" : "created",
            "_shards" : {
              "total" : 2,
              "successful" : 1,
              "failed" : 0
            },
            "_seq_no" : 3,
            "_primary_term" : 1,
            "status" : 201
          }
        }
      ]
    }
    

数据导入

首先我们需要导入一定量的数据用于搜索,使用的是银行账户表的例子,数据结构如下:

{
    "account_number": 0,
    "balance": 16623,
    "firstname": "Bradshaw",
    "lastname": "Mckenzie",
    "age": 29,
    "gender": "F",
    "address": "244 Columbus Place",
    "employer": "Euron",
    "email": "bradshawmckenzie@euron.com",
    "city": "Hobucken",
    "state": "CO"
}

我们先复制下需要导入的数据,数据地址: https://github.com/macrozheng/mall-learning/blob/teach/document/json/accounts.json

然后直接使用批量操作来导入数据,注意本文所有操作都在Kibana的Dev Tools中进行。

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": "amberduke@pyrami.com",
  "city": "Brogan",
  "state": "IL"
}
......省略若干条数据

导入完成后查看索引信息,可以发现bank索引中已经创建了1000条文档。

GET /_cat/indices?v

health status index                           uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   bank                            ycOSgiWjQomwzdygGwqOrQ   1   1       1000            0    374.5kb        374.5kb

基本搜索

match_all

GET /bank/_search
{
  "query": { "match_all": {} }
}
image-20251112094602193

分页搜索,from表示偏移量,从0开始,size表示每页显示的数量

GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 0,
  "size": 10
}
image-20251112094640335

搜索排序,使用sort表示,例如按balance字段降序排列

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}
image-20251112094727364

搜索并返回指定字段内容,使用_source表示,例如只返回account_number和balance两个字段内容:

GET /bank/_search
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}
image-20251112094818571

条件搜索

条件搜索,使用match表示匹配条件,例如搜索出account_number20的文档:

GET /bank/_search
{
  "query": {
    "match": {
      "account_number": 20
    }
  }
}
image-20251112094904397

文本类型字段的条件搜索,例如搜索address字段中包含mill的文档,对比上一条搜索可以发现,对于数值类型match操作使用的是精确匹配,对于文本类型使用的是模糊匹配;

GET /bank/_search
{
  "query": {
    "match": {
      "address": "mill"
    }
  },
  "_source": [
    "address",
    "account_number"
  ]
}
image-20251112094953836

短语匹配搜索,使用match_phrase表示,例如搜索address字段中同时包含milllane的文档:

GET /bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill lane"
    }
  }
}
image-20251112095056795

组合搜索

组合搜索,使用bool来进行组合,must表示同时满足,例如搜索address字段中同时包含milllane的文档;

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
image-20251112095124881

组合搜索,should表示满足其中任意一个,搜索address字段中包含mill或者lane的文档;

GET /bank/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
image-20251112095357271

组合搜索,must_not表示同时不满足,例如搜索address字段中不包含mill且不包含lane的文档;

GET /bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}
image-20251112095428733

组合搜索,组合mustmust_not,例如搜索age字段等于40state字段不包含ID的文档;

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}
image-20251112095505152

过滤搜索

搜索过滤,使用filter来表示,例如过滤出balance字段在20000~30000的文档;

GET /bank/_search
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}
image-20251112095630742

搜索聚合

对搜索结果进行聚合,使用aggs来表示,类似于MySql中的group by,例如对state字段进行聚合,统计出相同state的文档数量;

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      }
    }
  }
}
image-20251112095707015

嵌套聚合,例如对state字段进行聚合,统计出相同state的文档数量,再统计出balance的平均值

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
image-20251112095955269

对聚合搜索的结果进行排序,例如按balance的平均值降序排列;

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
image-20251112100211805

按字段值的范围进行分段聚合,例如分段范围为age字段的[20,30] [30,40] [40,50],之后按gender统计文档个数和balance的平均值;

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },
          {
            "from": 30,
            "to": 40
          },
          {
            "from": 40,
            "to": 50
          }
        ]
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "average_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  }
}
image-20251112100308567

整合到SpringBoot

Getting started with the Elasticsearch Java client | Java

<!--Elasticsearch相关依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

修改application.yml配置文件,在spring节点下添加Elasticsearch相关配置

spring:
  data:
    elasticsearch:
      repositories:
        enabled: true # 开启ES仓库配置,自动为仓库接口生成实现类
  elasticsearch:
    uris: http://localhost:9200 # ES的连接地址及端口号

项目结构

mall
├── document
|       └── sql -- 项目的数据库脚本
├── mall-common -- 工具类及通用代码
├── mall-mbg -- MyBatis Generator生成的数据库操作代码
├── mall-security -- SpringSecurity封装公用模块
├── mall-admin -- 后台管理系统接口
├── mall-search -- 基于Elasticsearch的商品搜索系统
├── mall-portal -- 前台商城系统接口
├── mall-demo -- 框架搭建时的测试代码
└── pom.xml -- Maven项目依赖配置

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jungle8884@163.com

×

喜欢就点赞,疼爱就打赏