Elasticsearch的使用教程

Elasticsearch简介

Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。作为 Elastic Stack 的核心,Elasticsearch 会集中存储您的数据,让您飞快完成搜索,微调相关性,进行强大的分析,并轻松缩放规模。

Elasticsearch的安装

下载安装包

下载Elasticsearch7.17.3版本的zip包,并解压到指定目录,下载地址:https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-17-3

安装中文分词器

安装中文分词器,注意下载与Elasticsearch对应的版本,下载地址
https://github.com/medcl/elasticsearch-analysis-ik/releases

下载完成后解压

下载完成后解压到Elasticsearch的plugins目录下

运行bin目录下的elasticsearch.bat启动Elasticsearch服务。

访问 http://localhost:9200/ 出现这个界面,表示启动成功

安装Kibana

作为Elasticsearch 的客户端访问

下载Kibana,作为访问Elasticsearch的客户端,请下载7.17.3版本的zip包,并解压到指定目录,下载地址:https://www.elastic.co/cn/downloads/past-releases/kibana-7-17-3

运行bin目录下的kibana.bat,启动Kibana服务;

打开Kibana的用户界面,访问地址:http://localhost:5601

相关概念

● 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;

索引操作

创建索引并查看

bash 复制代码
PUT /customer
GET /_cat/indices?v

删除索引并查看

cpp 复制代码
DELETE /customer
GET /_cat/indices?v

类型操作

首先要导入一批数据

cpp 复制代码
 https://github.com/macrozheng/mall-learning/blob/teach/document/json/accounts.json 
cpp 复制代码
POST /bank/account/_bulk

查看文档类型的操作

cpp 复制代码
GET /bank/_mapping

在索引中添加文档

cpp 复制代码
PUT /customer/doc/1
{
  "name": "John Doe"
}

查看索引中的文档

GET /customer/doc/1

修改索引中的文档

cpp 复制代码
POST /customer/doc/1/_update
{
  "doc": { "name": "Jane Doe" }
}

删除索引中的文档

cpp 复制代码
DELETE /customer/doc/1

数据搜索

查询表达式(Query DSL)是一种非常灵活又富有表现力的查询语言,Elasticsearch使用它可以以简单的JSON接口来实现丰富的搜索功能,下面的搜索操作都将使用它。

数据导入

首先要导入一批数据

cpp 复制代码
 https://github.com/macrozheng/mall-learning/blob/teach/document/json/accounts.json 
cpp 复制代码
POST /bank/account/_bulk

搜索入门

最简单的搜索,使用match_all来表示,例如搜索全部

cpp 复制代码
GET /bank/_search
{
  "query": { "match_all": {} }
}

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

cpp 复制代码
GET /bank/_search
{
  "query": { "match_all": {} },
  "from": 0,
  "size": 10
}

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

cpp 复制代码
GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": { "balance": { "order": "desc" } }
}

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

cpp 复制代码
GET /bank/_search
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}

条件搜索

条件搜索,使用match表示匹配条件,例如搜索出account_number为20的文档

cpp 复制代码
GET /bank/_search
{
  "query": {
    "match": {
      "account_number": 20
    }
  }
}

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

cpp 复制代码
GET /bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill lane"
    }
  }
}

组合搜索

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

cpp 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

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

cpp 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

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

cpp 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

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

cpp 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}

过滤搜索

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

cpp 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": {
        "range": {
          "balance": {
            "gte": 20000,
            "lte": 30000
          }
        }
      }
    }
  }
}

参考资料https://www.elastic.co/guide/en/elasticsearch/reference/7.17/getting-started.html

Spring Data Elasticsearch

Spring Data Elasticsearch是Spring提供的一种以Spring Data风格来操作数据存储的方式,它可以避免编写大量的样板代码。

常用注解

其中常用的FieldType类型有如下几种:

cpp 复制代码
public enum FieldType {
	Auto("auto"), //自动判断字段类型
	Text("text"), //会进行分词并建了索引的字符类型
	Keyword("keyword"), //不会进行分词建立索引的类型
	Long("long"), //
	Integer("integer"), //
	Short("short"), //
	Byte("byte"), //
	Double("double"), //
	Float("float"), //
	Date("date"), //
	Boolean("boolean"), //
	Object("object"), //
	Nested("nested"), //嵌套对象类型
	Ip("ip"), //
}

SpringData 方式的数据操作

  • 继承ElasticsearchRepository接口可以获得常用的数据操作方法;

可以使用衍生查询,在接口中直接指定查询方法名称便可查询,无需进行实现,如商品表中有商品名称、标题和关键字,直接定义以下查询,就可以对这三个字段进行全文搜索。

cpp 复制代码
/**
 * @description 商品ES操作类
 */
public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {
    /**
     * 搜索查询
     *
     * @param name              商品名称
     * @param subTitle          商品标题
     * @param keywords          商品关键字
     * @param page              分页信息
     * @return
     */
    Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);
}

通过@Query注解可以使用Elasticsearch的原生DSL语句进行查询;

cpp 复制代码
/**
 * @description 商品ES操作类
 */
public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {
    @Query("{"bool" : {"must" : {"field" : {"name" : " ? 0"}}}}")
    Page<EsProduct> findByName(String name, Pageable pageable);
}

整合Elasticsearch商品搜索

整理依赖配置

  • 在pom.xml中添加相关依赖;
xml 复制代码
<!--Elasticsearch相关依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
  • 修改application.yml配置文件,在spring节点下添加Elasticsearch相关配置;
yml 复制代码
spring:
  data:
    elasticsearch:
      repositories:
        enabled: true # 开启ES仓库配置,自动为仓库接口生成实现类
  elasticsearch:
    uris: http://localhost:9200 # ES的连接地址及端口号

实现商品搜索功能

添加商品文档对象EsProduct

不需要中文分词的字段设置成Keyword类型,需要中文分词的设置成Text类型,并设置分词器为ik_max_word;

cpp 复制代码
/**
 * @description 搜索商品的信息
 */
@Data
@EqualsAndHashCode
@Document(indexName = "pms")
@Setting(shards = 1,replicas = 0)
public class EsProduct implements Serializable {
    private static final long serialVersionUID = -1L;
    @Id
    private Long id;
    @Field(type = FieldType.Keyword)
    private String productSn;
    private Long brandId;
    @Field(type = FieldType.Keyword)
    private String brandName;
    private Long productCategoryId;
    @Field(type = FieldType.Keyword)
    private String productCategoryName;
    private String pic;
    @Field(analyzer = "ik_max_word",type = FieldType.Text)
    private String name;
    @Field(analyzer = "ik_max_word",type = FieldType.Text)
    private String subTitle;
    @Field(analyzer = "ik_max_word",type = FieldType.Text)
    private String keywords;
    private BigDecimal price;
    private Integer sale;
    private Integer newStatus;
    private Integer recommandStatus;
    private Integer stock;
    private Integer promotionType;
    private Integer sort;
    @Field(type =FieldType.Nested)
    private List<EsProductAttributeValue> attrValueList;
}

继承ElasticsearchRepository接口

这样就拥有了一些基本的Elasticsearch数据操作方法,同时定义了一个衍生查询方法;

cpp 复制代码
/**

 * @description 商品ES操作类

 */
public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {
    /**
     * 搜索查询
     *
     * @param name              商品名称
     * @param subTitle          商品标题
     * @param keywords          商品关键字
     * @param page              分页信息
     * @return
     */
    Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);

}

添加EsProductService,定义好ES的操作方法

cpp 复制代码
/**
 * @description 商品搜索管理Service
 */
public interface EsProductService {
    /**
     * 从数据库中导入所有商品到ES
     */
    int importAll();

    /**
     * 根据id删除商品
     */
    void delete(Long id);

    /**
     * 根据id创建商品
     */
    EsProduct create(Long id);

    /**
     * 批量删除商品
     */
    void delete(List<Long> ids);

    /**
     * 根据关键字搜索名称或者副标题
     */
    Page<EsProduct> search(String keyword, Integer pageNum, Integer pageSize);

}

添加EsProductService接口的实现类EsProductServiceImpl;

cpp 复制代码
/**
 * @description 搜索商品管理Service实现类
 */
@Service
public class EsProductServiceImpl implements EsProductService {
    private static final Logger LOGGER = LoggerFactory.getLogger(EsProductServiceImpl.class);
    @Autowired
    private EsProductDao productDao;
    @Autowired
    private EsProductRepository productRepository;
    @Override
    public int importAll() {
        List<EsProduct> esProductList = productDao.getAllEsProductList(null);
        Iterable<EsProduct> esProductIterable = productRepository.saveAll(esProductList);
        Iterator<EsProduct> iterator = esProductIterable.iterator();
        int result = 0;
        while (iterator.hasNext()) {
            result++;
            iterator.next();
        }
        return result;
    }

    @Override
    public void delete(Long id) {
        productRepository.deleteById(id);
    }

    @Override
    public EsProduct create(Long id) {
        EsProduct result = null;
        List<EsProduct> esProductList = productDao.getAllEsProductList(id);
        if (esProductList.size() > 0) {
            EsProduct esProduct = esProductList.get(0);
            result = productRepository.save(esProduct);
        }
        return result;
    }

    @Override
    public void delete(List<Long> ids) {
        if (!CollectionUtils.isEmpty(ids)) {
            List<EsProduct> esProductList = new ArrayList<>();
            for (Long id : ids) {
                EsProduct esProduct = new EsProduct();
                esProduct.setId(id);
                esProductList.add(esProduct);
            }
            productRepository.deleteAll(esProductList);
        }
    }

    @Override
    public Page<EsProduct> search(String keyword, Integer pageNum, Integer pageSize) {
        Pageable pageable = PageRequest.of(pageNum, pageSize);
        return productRepository.findByNameOrSubTitleOrKeywords(keyword, keyword, keyword, pageable);
    }

}

添加EsProductController定义接口。

cpp 复制代码
/**
 * @description 搜索商品管理Controller

 */
@Controller
@Api(tags = "EsProductController")
@Tag(name = "EsProductController", description = "搜索商品管理")
@RequestMapping("/esProduct")
public class EsProductController {
    @Autowired
    private EsProductService esProductService;

    @ApiOperation(value = "导入所有数据库中商品到ES")
    @RequestMapping(value = "/importAll", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<Integer> importAllList() {
        int count = esProductService.importAll();
        return CommonResult.success(count);
    }

    @ApiOperation(value = "根据id删除商品")
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<Object> delete(@PathVariable Long id) {
        esProductService.delete(id);
        return CommonResult.success(null);
    }

    @ApiOperation(value = "根据id批量删除商品")
    @RequestMapping(value = "/delete/batch", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<Object> delete(@RequestParam("ids") List<Long> ids) {
        esProductService.delete(ids);
        return CommonResult.success(null);
    }

    @ApiOperation(value = "根据id创建商品")
    @RequestMapping(value = "/create/{id}", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<EsProduct> create(@PathVariable Long id) {
        EsProduct esProduct = esProductService.create(id);
        if (esProduct != null) {
            return CommonResult.success(esProduct);
        } else {
            return CommonResult.failed();
        }
    }

    @ApiOperation(value = "简单搜索")
    @RequestMapping(value = "/search/simple", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<CommonPage<EsProduct>> search(@RequestParam(required = false) String keyword,
                                                      @RequestParam(required = false, defaultValue = "0") Integer pageNum,
                                                      @RequestParam(required = false, defaultValue = "5") Integer pageSize) {
        Page<EsProduct> esProductPage = esProductService.search(keyword, pageNum, pageSize);
        return CommonResult.success(CommonPage.restPage(esProductPage));
    }
}
相关推荐
青木沐7 分钟前
Jenkins介绍
运维·jenkins
WTT001142 分钟前
2024楚慧杯WP
大数据·运维·网络·安全·web安全·ctf
只会copy的搬运工4 小时前
Jenkins 持续集成部署——Jenkins实战与运维(1)
运维·ci/cd·jenkins
云云3215 小时前
怎么通过亚矩阵云手机实现营销?
大数据·服务器·安全·智能手机·矩阵
新加坡内哥谈技术5 小时前
苏黎世联邦理工学院与加州大学伯克利分校推出MaxInfoRL:平衡内在与外在探索的全新强化学习框架
大数据·人工智能·语言模型
Data-Miner6 小时前
经典案例PPT | 大型水果连锁集团新零售数字化建设方案
大数据·big data
lovelin+v175030409666 小时前
安全性升级:API接口在零信任架构下的安全防护策略
大数据·数据库·人工智能·爬虫·数据分析
测试杂货铺6 小时前
如何用postman做接口自动化测试及完美的可视化报告?
自动化测试·软件测试·测试工具·职场和发展·jenkins·压力测试·postman
道一云黑板报6 小时前
Flink集群批作业实践:七析BI批作业执行
大数据·分布式·数据分析·flink·kubernetes