当前位置:   article > 正文

Elasticsearch 入门及使用_elasticsearch怎么使用

elasticsearch怎么使用

目录

 

一、前言

二、ES的概念及使用场景

三、基本概念

3.1、文档(Document)

3.2、类型(Type)

3.3、索引(Index)

3.4、节点(node)

3.5、分片(shard)

3.6、副本分片(replica shard)

四、极速安装配置

五、基础使用

5.1、索引基础操作

创建一个空索引

修改副本

删除索引

5.2、数据增删改查

插入数据

修改数据

查询数据

删除数据

六、SpringBoot集成ES

1.pom.xml

2.application.properties

3.创建员工对象实体类 和 分页类

4.创建config类

5.创建ES操作工具类

6.创建controller

七、总结


​​​​​​​

一、前言

本文版本说明:

  1. ElasticSearch版本:7.7 (目前最新版)

  2. Kibana版本:7.7(目前最新版)

ElasticSearch在实际生产里通常和LogStash,Kibana,FileBeat一起构成Elastic Stack来使用,它是这些组件里面最核心的一个。因此学好ElasticSearch的必要性不言而喻,但是由于ElasticSearch官方更新太过频繁且文档陈旧,同时在Linux下安装配置的过程较繁杂,不利于入门使用。

为了帮助大家快速入门ElasticSearch,并掌握ElasticSearch和Kibana的使用。本文会把最新版的ElasticSearch的知识点用通俗易懂的语言来展现,并会在核心概念上和MySql对比,同时给大家介绍百分百成功的极速安装配置方法,让大家可以把时间更多的用在技术研究上。

注意:下文咱们把ElasticSearch简称为ES,对可能出现的疑问进行标红并解释。 logo

二、ES的概念及使用场景

ElasticSearch是一个分布式,高性能、高可用、可伸缩、RESTful 风格的搜索和数据分析引擎。通常作为Elastic Stack的核心来使用,Elastic Stack大致是如下这样组成的: 在这里插入图片描述

ES是一个近实时(NRT)的搜索引擎,一般从添加数据到能被搜索到只有很少的延迟(大约是1s),而查询数据是实时的。一般我们可以把ES配合logstash,kibana来做日志分析系统,或者是搜索方面的系统功能,比如在网上商城系统里实现搜索商品的功能也会用到ES。

疑问一:搜索商品的时候为啥要用ES呢?用sql的like进行模糊查询,它不香吗?

我们假设一个场景:我们要买苹果吃,咱们想买天水特产的花牛苹果,然后在搜索框输入天水花牛苹果,这时候咱们希望搜索到所有的售卖天水花牛苹果的商家,但是如果咱们技术上根据这个天水花牛苹果使用sql的like模糊查询,是不能匹配到诸如天水特产花牛苹果天水正宗,果园直送精品花牛苹果这类的不连续的店铺的。所以sql的like进行模糊查询来搜索商品还真不香

 

三、基本概念

很多人第一次学习ES,看到基本概念后瞬间懵逼了,这是啥玩意呀,乱七八糟!别急,我整理了一下ES和mysql相关的基本概念的对比表格,先看一下:

ESMySql
字段
文档一行数据
类型(已废弃)
索引数据库

看完这个表格后,建议像背单词那样盖住右半部分的MySql,通过左边的概念来联想在MySql里的概念,加深记忆! 然后我们组合起来,所谓ES里的数据其实就是指索引下的类型里面的JSON格式的数据

下面我们对这些概念分别进行详细的解释:

 

3.1、文档(Document)

  • 我们知道Java是面向对象的,而Elasticsearch是面向文档的,也就是说文档是所有可搜索数据的最小单元。ES的文档就像MySql中的一条记录,只是ES的文档会被序列化成json格式,保存在Elasticsearch中;

  • 这个json对象是由字段组成,字段就相当于Mysql的列,每个字段都有自己的类型(字符串、数值、布尔、二进制、日期范围类型);

  • 当我们创建文档时,如果不指定字段的类型,Elasticsearch会帮我们自动匹配类型;

  • 每个文档都有一个ID,类似MySql的主键,咱们可以自己指定,也可以让Elasticsearch自动生成;

  • 文档的json格式支持数组/嵌套,在一个索引(数据库)或类型(表)里面,你可以存储任意多的文档。

注意:虽然在实际存储上,文档存在于某个索引里,但是文档必须被赋予一个索引下的类型才可以。

 

3.2、类型(Type)

类型就相当于MySql里的表,我们知道MySql里一个库下可以有很多表,最原始的时候ES也是这样,一个索引下可以有很多类型,但是从6.0版本开始,type已经被逐渐废弃,但是这时候一个索引仍然可以设置多个类型,一直到7.0版本开始,一个索引就只能创建一个类型了(_doc)。这一点,大家要注意,网上很多资料都是旧版本的,没有对这点进行说明。

 

3.3、索引(Index)

  • 索引就相当于MySql里的数据库,它是具有某种相似特性的文档集合。反过来说不同特性的文档一般都放在不同的索引里;

  • 索引的名称必须全部是小写;

  • 在单个集群中,可以定义任意多个索引;

  • 索引具有mapping和setting的概念,mapping用来定义文档字段的类型,setting用来定义不同数据的分布

除了这些常用的概念,我们还需要知道节点概念的作用,因此咱们接着往下看!

 

3.4、节点(node)

  • 一个节点就是一个ES实例,其实本质上就是一个java进程;

  • 节点的名称可以通过配置文件配置,或者在启动的时候使用-E node.name=ropledata指定,默认是随机分配的。建议咱们自己指定,因为节点名称对于管理目的很重要,咱们可以通过节点名称确定网络中的哪些服务器对应于ES集群中的哪些节点;

  • ES的节点类型主要分为如下几种:

    1. Master Eligible节点:每个节点启动后,默认就是Master Eligible节点,可以通过设置node.master: false 来禁止。Master Eligible可以参加选主流程,并成为Master节点(当第一个节点启动后,它会将自己选为Master节点);注意:每个节点都保存了集群的状态,只有Master节点才能修改集群的状态信息。

    2. Data节点:可以保存数据的节点。主要负责保存分片数据,利于数据扩展。

    3. Coordinating 节点:负责接收客户端请求,将请求发送到合适的节点,最终把结果汇集到一起

  • 注意:每个节点默认都起到了Coordinating node的职责。一般在开发环境中一个节点可以承担多个角色,但是在生产环境中,还是设置单一的角色比较好,因为有助于提高性能。

     

3.5、分片(shard)

了解分布式或者学过mysql分库分表的应该对分片的概念比较熟悉,ES里面的索引可能存储大量数据,这些数据可能会超出单个节点的硬件限制。

为了解决这个问题,ES提供了将索引细分为多个碎片的功能,这就是分片。这里咱们可以简单去理解,在创建索引时,只需要咱们定义所需的碎片数量就可以了,其实每个分片都可以看作是一个完全功能性和独立的索引,可以托管在集群中的任何节点上。

疑问二:分片有什么好处和注意事项呢?

  1. 通过分片技术,咱们可以水平拆分数据量,同时它还支持跨碎片(可能在多个节点上)分布和并行操作,从而提高性能/吞吐量;

  2. ES可以完全自动管理分片的分配和文档的聚合来完成搜索请求,并且对用户完全透明;

  3. 主分片数在索引创建时指定,后续只能通过Reindex修改,但是较麻烦,一般不进行修改。

 

3.6、副本分片(replica shard)

熟悉分布式的朋友应该对副本对概念不陌生,为了实现高可用、遇到问题时实现分片的故障转移机制,ElasticSearch允许将索引分片的一个或多个复制成所谓的副本分片。

疑问三:副本分片有什么作用和注意事项呢?

  1. 当分片或者节点发生故障时提供高可用性。因此,需要注意的是,副本分片永远不会分配到复制它的原始或主分片所在的节点上;

  2. 可以提高扩展搜索量和吞吐量,因为ES允许在所有副本上并行执行搜索;

  3. 默认情况下,ES中的每个索引都分配5个主分片,并为每个主分片分配1个副本分片。主分片在创建索引时指定,不能修改,副本分片可以修改。

看到这里,各位一定对ES有所了解了,那么接下来就是安装配置并使用了!有不少朋友初学时查阅资料,选择安装win版本,这里我不推荐,因为实际工作中,ES不可能安装在win下。但是根据官方文档安装Linux版本时,又会遇到各种奇葩问题,咋办呢?别急,我这里有一本极速安装方法,百分百不出错,咱们接着往下看! 在这里插入图片描述

 

四、极速安装配置

咱们如果想很爽的使用ES,需要安装3个东西:ES、Kibana、ElasticSearch Head。通过Kibana可以对ES进行便捷的可视化操作,通过ElasticSearch Head可以查看ES的状态及数据,可以理解为ES的图形化界面。

那如何进行极速且不出错的安装配置呢?答案很简单,站在巨人的肩膀上!用docker启动前辈们已经配置好的ES环境不就可以了吗?!咱们做开发的应该把时间花在刀刃上,而不是花费大量时间去安装配置。

首先开始安装ES、Kibana,同时安装这两个加启动,一共需要3步,3行代码搞定:

  1. 搜索docker镜像库里可用的ES镜像:

    docker search elasticsearch
    1

    在这里插入图片描述

    可以看到,stars排名第一的是官方的ES镜像,第二是大牛已经融合了ES7.7和Kibana7.7的镜像,那咱们就用第二个了。

  2. 把这个镜像从镜像库拉下来:

    docker pull nshou/elasticsearch-kibana
    1

    在这里插入图片描述

  3. 最后咱们把镜像启动为容器就可以了,端口映射保持不变,咱们给这个容器命名为eskibana,到这里ES和Kibana就安装配置完成了!容器启动后,它们也就启动了,一般不会出错,是不是非常方便?节省大把时间放到开发上来,这也是我一直推荐docker的原因。

    docker run -d -p 9200:9200 -p 9300:9300 -p 5601:5601 --name eskibana  nshou/elasticsearch-kibana
    1

    在这里插入图片描述

咱们还需要安装ElasticSearch Head,它相当于是ES的图形化界面,这个更简单,它是一个浏览器的扩展程序,直接在chrome浏览器扩展程序里下载安装即可:

  1. 打开chrome浏览器,在扩展程序chrome应用商店那里,搜索elasticsearch: 在这里插入图片描述

  2. 选择ElasticSearch Head,点击添加至Chrome,进行扩展程序的安装即可: 在这里插入图片描述

到这里咱们的ES、Kibana、ElasticSearch Head都已经安装完成了,下面咱们验证一下,看是否安装成功!

  1. 验证ES:

    打开浏览器,输入IP:端口,比如我的:http://127.0.0.1:9200/,然后就看到了那句经典的:You Know, for Search在这里插入图片描述

  2. 验证Kibana:

    打开浏览器,输入Kibana的IP:端口,比如我的:http://127.0.0.1:5601/,然后会看到如下界面: 在这里插入图片描述

    这里面可以提供很多模拟数据,感兴趣的可以自己玩玩,咱们学习期间只要使用左下角那个扳手形状的Dev Tools就可以了,点击后,会出现如下界面: 在这里插入图片描述

  3. 验证ES Head:

    这个更简单,只需要点击之前咱们安装的那个扩展程序图标就可以了: 在这里插入图片描述 点击信息,还可以看到集群或者索引的信息,很方便,大家没事可以玩一玩,熟悉一下: 在这里插入图片描述

通过验证,我们已经全部安装配置成功了,那么接下来,就让我们一起练习一下基础的增删改查,加深对ES的理解吧!

 

五、基础使用

前面我们已经介绍过了ES 是RESTful 风格的系统,所以我们需要先掌握RESTful 的四个关键词:PUT(修改),POST(添加),DELETE(删除),GET(查询)。其中在ES里面PUT和POST的界限并不是很分明,有时候PUT也作为添加。 好了,下面就开始愉快的code吧~

5.1、索引基础操作

  1. 创建一个空索引

    如下代码,咱们创建了一个0副本2分片的ropledata索引,然后咱们可以在Elasticsearch Head里刷新一下,并查看索引的信息:

    PUT /ropledata
    {
      "settings": { 
        "number_of_shards": "2", 
        "number_of_replicas": "0"
      } 
    }
    1234567

    在这里插入图片描述在这里插入图片描述

  2. 修改副本

    咱们如果对刚才创建的索引副本数量不满意,可以进行修改,注意:分片不允许修改。

    PUT ropledata/_settings 
    { 
      "number_of_replicas" : "2" 
    }
    1234

    在这里插入图片描述

  3. 删除索引

    当这个索引不想用了,可以进行删除,执行如下命令即可,执行成功后,刷新ElasticSearch Head可以看到刚才创建的ropledata索引消失了:

    DELETE /ropledata
    1

    在这里插入图片描述在这里插入图片描述

5.2、数据增删改查

  1. 插入数据

    插入数据的时候可以指定id,如果不指定的话,ES会自动帮我们生成。我们以指定id为例,如下代码是我们创建了一个101的文档,创建成功后,可以在Elasticsearch Head的数据浏览模块里看到这些数据,代码及演示如下:

    //指定id 
    POST /ropledata/_doc/101 
    {
      "id":1,
      "name":"且听_风吟",
      "page":"https://ropledata.blog.csdn.net",
      "say":"欢迎点赞,收藏,关注,一起学习" 
    }
    12345678

    在这里插入图片描述在这里插入图片描述

  2. 修改数据

    这里大家要特别注意,ES里的文档是不可以修改的,但是可以覆盖,所以ES修改数据本质上是对文档的覆盖。ES对数据的修改分为全局更新局部更新,咱们分别进行code并对比:

    • 全局更新

      PUT /ropledata/_doc/101
      { 
        "id":1,
        "name":"且听_风吟",
        "page":"https://ropledata.blog.csdn.net",
        "say":"再次欢迎点赞,收藏,关注,一起学习" 
      }
      1234567

      大家可以多全局更新几次,会发现每次全局更新之后这个文档的_version都会发生改变! 在这里插入图片描述

    • 局部更新

      POST /ropledata/_update/101 
      {
        "doc":
        {
          "say":"奥力给"
        } 
      }
      1234567

      这时候我们可以多次去执行上面的局部更新代码,会发现除了第一次执行,后续不管又执行了多少次,_version都不再变化! 在这里插入图片描述

    疑问四:局部更新的时候ES底层的流程是怎样的?和全局更新相比性能怎么样?

    局部更新的底层流程:

    1. 内部先获取到对应的文档;

    2. 将传递过来的字段更新到文档的json中(这一步实质上也是一样的);

    3. 将老的文档标记为deleted(到一定时候才会物理删除);

    4. 将修改后的新的文档创建出来。

    性能对比:

    1. 全局更新本质上是替换操作,即使内容一样也会去替换;

    2. 局部更新本质上是更新操作,只有遇到新的东西才更新,没有新的修改就不更新;

    3. 局部更新比全局更新的性能好,因此推荐使用局部更新。

  3. 查询数据

    ES的数据查询知识点非常多,也非常复杂,后面我打算单独讲解演示,本文只展示最基本的根据id搜索数据的code:

    GET /ropledata/_doc/101
    1

    在这里插入图片描述

  4. 删除数据

    比如我们想把ropledata索引下的id为101的文档删除,可以使用如下命令:

    DELETE /ropledata/_doc/101
    1

    在这里插入图片描述

疑问五:查询或者删除的时候指定的ID是文档里面得字段id吗?

不是的,这点容易混淆,查询或者删除时候用到的ID是创建文档时候指定或者ES自动生成的那个id,而不是文档里面的那个叫id 字段!文档里面的文档字段是可以没有id 的。

六、SpringBoot集成ES

1.pom.xml

 

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-test</artifactId>
  8. <scope>test</scope>
  9. </dependency>
  10. <!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
  11. <dependency>
  12. <groupId>org.elasticsearch</groupId>
  13. <artifactId>elasticsearch</artifactId>
  14. <version>6.4.2</version>
  15. </dependency>
  16. <!-- https://mvnrepository.com/artifact/org.elasticsearch.client/transport -->
  17. <dependency>
  18. <groupId>org.elasticsearch.client</groupId>
  19. <artifactId>transport</artifactId>
  20. <version>6.4.2</version>
  21. <exclusions>
  22. <exclusion>
  23. <groupId>org.elasticsearch</groupId>
  24. <artifactId>elasticsearch</artifactId>
  25. </exclusion>
  26. </exclusions>
  27. </dependency>
  28. <!-- fastjson -->
  29. <dependency>
  30. <groupId>com.alibaba</groupId>
  31. <artifactId>fastjson</artifactId>
  32. <version>1.2.39</version>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.apache.commons</groupId>
  36. <artifactId>commons-lang3</artifactId>
  37. <version>3.4</version>
  38. </dependency>
  39. <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
  40. <dependency>
  41. <groupId>org.projectlombok</groupId>
  42. <artifactId>lombok</artifactId>
  43. <version>1.16.20</version>
  44. </dependency>
  45. <dependency>
  46. <groupId>commons-httpclient</groupId>
  47. <artifactId>commons-httpclient</artifactId>
  48. <version>3.1</version>
  49. </dependency>

2.application.properties

 

  1. # Elasticsearch
  2. # 9200端口是用来让HTTP REST API来访问ElasticSearch,而9300端口是传输层监听的默认端口
  3. elasticsearch.ip=127.0.0.1
  4. elasticsearch.port=9300
  5. elasticsearch.pool=5
  6. #注意cluster.name需要与config/elasticsearch.yml中的cluster.name一致
  7. elasticsearch.cluster.name=elasticsearch_cici
  8. server.port=8181

3.创建员工对象实体类 和 分页类

员工对象实体类

 

  1. /**
  2. * @Description:Book实体 加上了@Document注解之后,默认情况下这个实体中所有的属性都会被建立索引、并且分词
  3. */
  4. @Data
  5. @ToString
  6. @NoArgsConstructor
  7. public class Employee {
  8. private String id;
  9. private Long version;
  10. String firstName;
  11. String lastName;
  12. String age;
  13. String[] interests;
  14. }

分页类

 

  1. @Data
  2. @ToString
  3. public class EsPage {
  4. /**
  5. * 当前页
  6. */
  7. private int currentPage;
  8. /**
  9. * 每页显示多少条
  10. */
  11. private int pageSize;
  12. /**
  13. * 总记录数
  14. */
  15. private int recordCount;
  16. /**
  17. * 本页的数据列表
  18. */
  19. private List<Map<String, Object>> recordList;
  20. /**
  21. * 总页数
  22. */
  23. private int pageCount;
  24. /**
  25. * 页码列表的开始索引(包含)
  26. */
  27. private int beginPageIndex;
  28. /**
  29. * 页码列表的结束索引(包含)
  30. */
  31. private int endPageIndex;
  32. /**
  33. * 只接受前4个必要的属性,会自动的计算出其他3个属性的值
  34. *
  35. * @param currentPage
  36. * @param pageSize
  37. * @param recordCount
  38. * @param recordList
  39. */
  40. public EsPage(int currentPage, int pageSize, int recordCount, List<Map<String, Object>> recordList) {
  41. this.currentPage = currentPage;
  42. this.pageSize = pageSize;
  43. this.recordCount = recordCount;
  44. this.recordList = recordList;
  45. // 计算总页码
  46. pageCount = (recordCount + pageSize - 1) / pageSize;
  47. // 计算 beginPageIndex 和 endPageIndex
  48. // >> 总页数不多于10页,则全部显示
  49. if (pageCount <= 10) {
  50. beginPageIndex = 1;
  51. endPageIndex = pageCount;
  52. }
  53. // 总页数多于10页,则显示当前页附近的共10个页码
  54. else {
  55. // 当前页附近的共10个页码(前4+ 当前页 +5个)
  56. beginPageIndex = currentPage - 4;
  57. endPageIndex = currentPage + 5;
  58. // 当前面的页码不足4个时,则显示前10个页码
  59. if (beginPageIndex < 1) {
  60. beginPageIndex = 1;
  61. endPageIndex = 10;
  62. }
  63. // 当后面的页码不足5个时,则显示后10个页码
  64. if (endPageIndex > pageCount) {
  65. endPageIndex = pageCount;
  66. beginPageIndex = pageCount - 10 + 1;
  67. }
  68. }
  69. }
  70. }

4.创建config类

 

  1. @Configuration
  2. public class ElasticSearchConfig {
  3. private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchConfig.class);
  4. /**
  5. * elk集群地址
  6. */
  7. @Value("${elasticsearch.ip}")
  8. private String hostName;
  9. /**
  10. * 端口
  11. */
  12. @Value("${elasticsearch.port}")
  13. private String port;
  14. /**
  15. * 集群名称
  16. */
  17. @Value("${elasticsearch.cluster.name}")
  18. private String clusterName;
  19. /**
  20. * 连接池
  21. */
  22. @Value("${elasticsearch.pool}")
  23. private String poolSize;
  24. /**
  25. * Bean name default 函数名字
  26. *
  27. * @return
  28. */
  29. @Bean(name = "transportClient")
  30. public TransportClient transportClient() {
  31. LOGGER.info("Elasticsearch初始化开始。。。。。");
  32. TransportClient transportClient = null;
  33. try {
  34. // 配置信息
  35. Settings esSetting = Settings.builder()
  36. .put("cluster.name", clusterName) //集群名字
  37. .put("client.transport.sniff", true)//增加嗅探机制,找到ES集群
  38. .put("thread_pool.search.size", Integer.parseInt(poolSize))//增加线程池个数,暂时设为5
  39. .build();
  40. //配置信息Settings自定义
  41. transportClient = new PreBuiltTransportClient(esSetting);
  42. TransportAddress transportAddress = new TransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port));
  43. transportClient.addTransportAddresses(transportAddress);
  44. } catch (Exception e) {
  45. LOGGER.error("elasticsearch TransportClient create error!!", e);
  46. }
  47. return transportClient;
  48. }
  49. }

5.创建ES操作工具类

Elasticsearch JAVA操作有三种客户端:
1、TransportClient
2、JestClient
3、RestClient
还有种是2.3中有的NodeClient,在5.5.1中好像没有了。
还有种是spring-data-elasticsearch,这里先以TransportClient为例子。

 

  1. @Component
  2. public class ElasticsearchUtil {
  3. private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchUtil.class);
  4. @Autowired
  5. private TransportClient transportClient;
  6. private static TransportClient client;
  7. /**
  8. * @PostContruct是spring框架的注解 spring容器初始化的时候执行该方法
  9. */
  10. @PostConstruct
  11. public void init() {
  12. client = this.transportClient;
  13. }
  14. /**
  15. * 创建索引
  16. *
  17. * @param index
  18. * @return
  19. */
  20. public static boolean createIndex(String index) {
  21. if (!isIndexExist(index)) {
  22. LOGGER.info("Index is not exits!");
  23. }
  24. CreateIndexResponse indexresponse = client.admin().indices().prepareCreate(index).execute().actionGet();
  25. LOGGER.info("执行建立成功?" + indexresponse.isAcknowledged());
  26. return indexresponse.isAcknowledged();
  27. }
  28. /**
  29. * 删除索引
  30. *
  31. * @param index
  32. * @return
  33. */
  34. public static boolean deleteIndex(String index) {
  35. if (!isIndexExist(index)) {
  36. LOGGER.info("Index is not exits!");
  37. }
  38. DeleteIndexResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet();
  39. if (dResponse.isAcknowledged()) {
  40. LOGGER.info("delete index " + index + " successfully!");
  41. } else {
  42. LOGGER.info("Fail to delete index " + index);
  43. }
  44. return dResponse.isAcknowledged();
  45. }
  46. /**
  47. * 判断索引是否存在
  48. *
  49. * @param index
  50. * @return
  51. */
  52. public static boolean isIndexExist(String index) {
  53. IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index)).actionGet();
  54. if (inExistsResponse.isExists()) {
  55. LOGGER.info("Index [" + index + "] is exist!");
  56. } else {
  57. LOGGER.info("Index [" + index + "] is not exist!");
  58. }
  59. return inExistsResponse.isExists();
  60. }
  61. /**
  62. * @Description: 判断inde下指定type是否存在
  63. */
  64. public boolean isTypeExist(String index, String type) {
  65. return isIndexExist(index)
  66. ? client.admin().indices().prepareTypesExists(index).setTypes(type).execute().actionGet().isExists()
  67. : false;
  68. }
  69. /**
  70. * 数据添加,正定ID
  71. *
  72. * @param jsonObject 要增加的数据
  73. * @param index 索引,类似数据库
  74. * @param type 类型,类似表
  75. * @param id 数据ID
  76. * @return
  77. */
  78. public static String addData(JSONObject jsonObject, String index, String type, String id) {
  79. IndexResponse response = client.prepareIndex(index, type, id).setSource(jsonObject).get();
  80. LOGGER.info("addData response status:{},id:{}", response.status().getStatus(), response.getId());
  81. return response.getId();
  82. }
  83. /**
  84. * 数据添加
  85. *
  86. * @param jsonObject 要增加的数据
  87. * @param index 索引,类似数据库
  88. * @param type 类型,类似表
  89. * @return
  90. */
  91. public static String addData(JSONObject jsonObject, String index, String type) {
  92. return addData(jsonObject, index, type, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
  93. }
  94. /**
  95. * 通过ID删除数据
  96. *
  97. * @param index 索引,类似数据库
  98. * @param type 类型,类似表
  99. * @param id 数据ID
  100. */
  101. public static void deleteDataById(String index, String type, String id) {
  102. DeleteResponse response = client.prepareDelete(index, type, id).execute().actionGet();
  103. LOGGER.info("deleteDataById response status:{},id:{}", response.status().getStatus(), response.getId());
  104. }
  105. /**
  106. * 通过ID 更新数据
  107. *
  108. * @param jsonObject 要增加的数据
  109. * @param index 索引,类似数据库
  110. * @param type 类型,类似表
  111. * @param id 数据ID
  112. * @return
  113. */
  114. public static void updateDataById(JSONObject jsonObject, String index, String type, String id) {
  115. UpdateRequest updateRequest = new UpdateRequest();
  116. updateRequest.index(index).type(type).id(id).doc(jsonObject);
  117. client.update(updateRequest);
  118. }
  119. /**
  120. * 通过ID获取数据
  121. *
  122. * @param index 索引,类似数据库
  123. * @param type 类型,类似表
  124. * @param id 数据ID
  125. * @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
  126. * @return
  127. */
  128. public static Map<String, Object> searchDataById(String index, String type, String id, String fields) {
  129. GetRequestBuilder getRequestBuilder = client.prepareGet(index, type, id);
  130. if (StringUtils.isNotEmpty(fields)) {
  131. getRequestBuilder.setFetchSource(fields.split(","), null);
  132. }
  133. GetResponse getResponse = getRequestBuilder.execute().actionGet();
  134. return getResponse.getSource();
  135. }
  136. /**
  137. * 使用分词查询,并分页
  138. *
  139. * @param index 索引名称
  140. * @param type 类型名称,可传入多个type逗号分隔
  141. * @param startPage 当前页
  142. * @param pageSize 每页显示条数
  143. * @param query 查询条件
  144. * @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
  145. * @param sortField 排序字段
  146. * @param highlightField 高亮字段
  147. * @return
  148. */
  149. public static EsPage searchDataPage(String index, String type, int startPage, int pageSize, QueryBuilder query, String fields, String sortField, String highlightField) {
  150. SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index);
  151. if (StringUtils.isNotEmpty(type)) {
  152. searchRequestBuilder.setTypes(type.split(","));
  153. }
  154. searchRequestBuilder.setSearchType(SearchType.QUERY_THEN_FETCH);
  155. // 需要显示的字段,逗号分隔(缺省为全部字段)
  156. if (StringUtils.isNotEmpty(fields)) {
  157. searchRequestBuilder.setFetchSource(fields.split(","), null);
  158. }
  159. //排序字段
  160. if (StringUtils.isNotEmpty(sortField)) {
  161. searchRequestBuilder.addSort(sortField, SortOrder.DESC);
  162. }
  163. // 高亮(xxx=111,aaa=222
  164. if (StringUtils.isNotEmpty(highlightField)) {
  165. HighlightBuilder highlightBuilder = new HighlightBuilder();
  166. //highlightBuilder.preTags("<span style='color:red' >");//设置前缀
  167. //highlightBuilder.postTags("</span>");//设置后缀
  168. // 设置高亮字段
  169. highlightBuilder.field(highlightField);
  170. searchRequestBuilder.highlighter(highlightBuilder);
  171. }
  172. //searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery());
  173. searchRequestBuilder.setQuery(query);
  174. // 分页应用
  175. searchRequestBuilder.setFrom(startPage).setSize(pageSize);
  176. // 设置是否按查询匹配度排序
  177. searchRequestBuilder.setExplain(true);
  178. //打印的内容 可以在 Elasticsearch head 和 Kibana 上执行查询
  179. LOGGER.info("\n{}", searchRequestBuilder);
  180. // 执行搜索,返回搜索响应信息
  181. SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
  182. long totalHits = searchResponse.getHits().totalHits;
  183. long length = searchResponse.getHits().getHits().length;
  184. LOGGER.debug("共查询到[{}]条数据,处理数据条数[{}]", totalHits, length);
  185. if (searchResponse.status().getStatus() == 200) {
  186. // 解析对象
  187. List<Map<String, Object>> sourceList = setSearchResponse(searchResponse, highlightField);
  188. return new EsPage(startPage, pageSize, (int) totalHits, sourceList);
  189. }
  190. return null;
  191. }
  192. /**
  193. * 使用分词查询
  194. *
  195. * @param index 索引名称
  196. * @param type 类型名称,可传入多个type逗号分隔
  197. * @param query 查询条件
  198. * @param size 文档大小限制
  199. * @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
  200. * @param sortField 排序字段
  201. * @param highlightField 高亮字段
  202. * @return
  203. */
  204. public static List<Map<String, Object>> searchListData(
  205. String index, String type, QueryBuilder query, Integer size,
  206. String fields, String sortField, String highlightField) {
  207. SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index);
  208. if (StringUtils.isNotEmpty(type)) {
  209. searchRequestBuilder.setTypes(type.split(","));
  210. }
  211. if (StringUtils.isNotEmpty(highlightField)) {
  212. HighlightBuilder highlightBuilder = new HighlightBuilder();
  213. // 设置高亮字段
  214. highlightBuilder.field(highlightField);
  215. searchRequestBuilder.highlighter(highlightBuilder);
  216. }
  217. searchRequestBuilder.setQuery(query);
  218. if (StringUtils.isNotEmpty(fields)) {
  219. searchRequestBuilder.setFetchSource(fields.split(","), null);
  220. }
  221. searchRequestBuilder.setFetchSource(true);
  222. if (StringUtils.isNotEmpty(sortField)) {
  223. searchRequestBuilder.addSort(sortField, SortOrder.DESC);
  224. }
  225. if (size != null && size > 0) {
  226. searchRequestBuilder.setSize(size);
  227. }
  228. //打印的内容 可以在 Elasticsearch head 和 Kibana 上执行查询
  229. LOGGER.info("\n{}", searchRequestBuilder);
  230. SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
  231. long totalHits = searchResponse.getHits().totalHits;
  232. long length = searchResponse.getHits().getHits().length;
  233. LOGGER.info("共查询到[{}]条数据,处理数据条数[{}]", totalHits, length);
  234. if (searchResponse.status().getStatus() == 200) {
  235. // 解析对象
  236. return setSearchResponse(searchResponse, highlightField);
  237. }
  238. return null;
  239. }
  240. /**
  241. * 高亮结果集 特殊处理
  242. *
  243. * @param searchResponse
  244. * @param highlightField
  245. */
  246. private static List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String highlightField) {
  247. List<Map<String, Object>> sourceList = new ArrayList<Map<String, Object>>();
  248. StringBuffer stringBuffer = new StringBuffer();
  249. for (SearchHit searchHit : searchResponse.getHits().getHits()) {
  250. searchHit.getSourceAsMap().put("id", searchHit.getId());
  251. if (StringUtils.isNotEmpty(highlightField)) {
  252. System.out.println("遍历 高亮结果集,覆盖 正常结果集" + searchHit.getSourceAsMap());
  253. Text[] text = searchHit.getHighlightFields().get(highlightField).getFragments();
  254. if (text != null) {
  255. for (Text str : text) {
  256. stringBuffer.append(str.string());
  257. }
  258. //遍历 高亮结果集,覆盖 正常结果集
  259. searchHit.getSourceAsMap().put(highlightField, stringBuffer.toString());
  260. }
  261. }
  262. sourceList.add(searchHit.getSourceAsMap());
  263. }
  264. return sourceList;
  265. }
  266. }

6.创建controller

 

  1. @RestController
  2. @RequestMapping("/es")
  3. public class EsController {
  4. /**
  5. * 测试索引
  6. */
  7. private String indexName = "megacorp";
  8. /**
  9. * 类型
  10. */
  11. private String esType = "employee";
  12. /**
  13. * 创建索引
  14. * http://127.0.0.1:8080/es/createIndex
  15. * @param request
  16. * @param response
  17. * @return
  18. */
  19. @RequestMapping("/createIndex")
  20. public String createIndex(HttpServletRequest request, HttpServletResponse response) {
  21. if (!ElasticsearchUtil.isIndexExist(indexName)) {
  22. ElasticsearchUtil.createIndex(indexName);
  23. } else {
  24. return "索引已经存在";
  25. }
  26. return "索引创建成功";
  27. }
  28. /**
  29. * 插入记录
  30. *
  31. * @return
  32. */
  33. @RequestMapping("/insertJson")
  34. public String insertJson() {
  35. JSONObject jsonObject = new JSONObject();
  36. jsonObject.put("id", DateUtil.formatDate(new Date()));
  37. jsonObject.put("age", 25);
  38. jsonObject.put("first_name", "j-" + new Random(100).nextInt());
  39. jsonObject.put("last_name", "cccc");
  40. jsonObject.put("about", "i like xiaofeng baby");
  41. jsonObject.put("date", new Date());
  42. String id = ElasticsearchUtil.addData(jsonObject, indexName, esType, jsonObject.getString("id"));
  43. return id;
  44. }
  45. /**
  46. * 插入记录
  47. *
  48. * @return
  49. */
  50. @RequestMapping("/insertModel")
  51. public String insertModel() {
  52. Employee employee = new Employee();
  53. employee.setId("66");
  54. employee.setFirstName("m-" + new Random(100).nextInt());
  55. employee.setAge("24");
  56. JSONObject jsonObject = (JSONObject) JSONObject.toJSON(employee);
  57. String id = ElasticsearchUtil.addData(jsonObject, indexName, esType, jsonObject.getString("id"));
  58. return id;
  59. }
  60. /**
  61. * 删除记录
  62. *
  63. * @return
  64. */
  65. @RequestMapping("/delete")
  66. public String delete(String id) {
  67. if (StringUtils.isNotBlank(id)) {
  68. ElasticsearchUtil.deleteDataById(indexName, esType, id);
  69. return "删除id=" + id;
  70. } else {
  71. return "id为空";
  72. }
  73. }
  74. /**
  75. * 更新数据
  76. *
  77. * @return
  78. */
  79. @RequestMapping("/update")
  80. public String update(String id) {
  81. if (StringUtils.isNotBlank(id)) {
  82. JSONObject jsonObject = new JSONObject();
  83. jsonObject.put("id", id);
  84. jsonObject.put("age", 31);
  85. jsonObject.put("name", "修改");
  86. jsonObject.put("date", new Date());
  87. ElasticsearchUtil.updateDataById(jsonObject, indexName, esType, id);
  88. return "id=" + id;
  89. } else {
  90. return "id为空";
  91. }
  92. }
  93. /**
  94. * 获取数据
  95. * http://127.0.0.1:8080/es/getData?id=2018-04-25%2016:33:44
  96. *
  97. * @param id
  98. * @return
  99. */
  100. @RequestMapping("/getData")
  101. public String getData(String id) {
  102. if (StringUtils.isNotBlank(id)) {
  103. Map<String, Object> map = ElasticsearchUtil.searchDataById(indexName, esType, id, null);
  104. return JSONObject.toJSONString(map);
  105. } else {
  106. return "id为空";
  107. }
  108. }
  109. /**
  110. * 查询数据
  111. * 模糊查询
  112. *
  113. * @return
  114. */
  115. @RequestMapping("/queryMatchData")
  116. public String queryMatchData() {
  117. BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
  118. boolean matchPhrase = false;
  119. if (matchPhrase == Boolean.TRUE) {
  120. //不进行分词搜索
  121. boolQuery.must(QueryBuilders.matchPhraseQuery("first_name", "cici"));
  122. } else {
  123. boolQuery.must(QueryBuilders.matchQuery("last_name", "cici"));
  124. }
  125. List<Map<String, Object>> list = ElasticsearchUtil.
  126. searchListData(indexName, esType, boolQuery, 10, "first_name", null, "last_name");
  127. return JSONObject.toJSONString(list);
  128. }
  129. /**
  130. * 通配符查询数据
  131. * 通配符查询 ?用来匹配1个任意字符,*用来匹配零个或者多个字符
  132. *
  133. * @return
  134. */
  135. @RequestMapping("/queryWildcardData")
  136. public String queryWildcardData() {
  137. QueryBuilder queryBuilder = QueryBuilders.wildcardQuery("first_name.keyword", "cici");
  138. List<Map<String, Object>> list = ElasticsearchUtil.searchListData(indexName, esType, queryBuilder, 10, null, null, null);
  139. return JSONObject.toJSONString(list);
  140. }
  141. /**
  142. * 正则查询
  143. *
  144. * @return
  145. */
  146. @RequestMapping("/queryRegexpData")
  147. public String queryRegexpData() {
  148. QueryBuilder queryBuilder = QueryBuilders.regexpQuery("first_name.keyword", "m--[0-9]{1,11}");
  149. List<Map<String, Object>> list = ElasticsearchUtil.searchListData(indexName, esType, queryBuilder, 10, null, null, null);
  150. return JSONObject.toJSONString(list);
  151. }
  152. /**
  153. * 查询数字范围数据
  154. *
  155. * @return
  156. */
  157. @RequestMapping("/queryIntRangeData")
  158. public String queryIntRangeData() {
  159. BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
  160. boolQuery.must(QueryBuilders.rangeQuery("age").from(24)
  161. .to(25));
  162. List<Map<String, Object>> list = ElasticsearchUtil.searchListData(indexName, esType, boolQuery, 10, null, null, null);
  163. return JSONObject.toJSONString(list);
  164. }
  165. /**
  166. * 查询日期范围数据
  167. *
  168. * @return
  169. */
  170. @RequestMapping("/queryDateRangeData")
  171. public String queryDateRangeData() {
  172. BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
  173. boolQuery.must(QueryBuilders.rangeQuery("age").from("20")
  174. .to("50"));
  175. List<Map<String, Object>> list = ElasticsearchUtil.searchListData(indexName, esType, boolQuery, 10, null, null, null);
  176. return JSONObject.toJSONString(list);
  177. }
  178. /**
  179. * 查询分页
  180. *
  181. * @param startPage 第几条记录开始
  182. * 从0开始
  183. * 第1页 :http://127.0.0.1:8080/es/queryPage?startPage=0&pageSize=2
  184. * 第2页 :http://127.0.0.1:8080/es/queryPage?startPage=2&pageSize=2
  185. * @param pageSize 每页大小
  186. * @return
  187. */
  188. @RequestMapping("/queryPage")
  189. public String queryPage(String startPage, String pageSize) {
  190. if (StringUtils.isNotBlank(startPage) && StringUtils.isNotBlank(pageSize)) {
  191. BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
  192. boolQuery.must(QueryBuilders.rangeQuery("age").from("20")
  193. .to("100"));
  194. EsPage list = ElasticsearchUtil.searchDataPage(indexName, esType, Integer.parseInt(startPage), Integer.parseInt(pageSize), boolQuery, null, null, null);
  195. return JSONObject.toJSONString(list);
  196. } else {
  197. return "startPage或者pageSize缺失";
  198. }
  199. }
  200. }

利用postman一个个请求。
截取结果如下:方法都能正常准确访问

 

七、总结

本文我们对ES的基本概念进行了清晰的解释,并用最有效率的方式进行了安装配置,也对基础的增删改查进行了图文并茂的演示。掌握了这些可以说对ES已经入门了,写这篇文章的目的也已经达到了!ES还有很多复杂的查询,中文分词,倒排索引等技术点需要我们去掌握,后续我将会整理出来,咱们一起学习!

如果您对我的文章感兴趣,欢迎关注点赞收藏,如果您有疑惑或发现文中有不对的地方,还请不吝赐教,非常感谢!!

扩展参考:

Elasticsearch面试题(2021最新版)

elasticsearch 常见查询及聚合的JAVA API

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/310598
推荐阅读
相关标签
  

闽ICP备14008679号