• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

简历没写间件,面试5分钟就被请出去了,ElasticSearch 改咋学呢?

武飞扬头像
FARO_Z
帮助1

ElasticSearch 简明教程

一、ES 简介

学之前,我们要知道 ES 是做什么的

咱们先来用用看搜索引擎:

学新通

可以发现,搜索引擎以一个极快的速度,将包含我们搜索关键字的内容展示了出来

那你有没有想过,这是怎么实现的呢?

有同学可能会说了:“那还不简单,直接遍历服务器中所有的内容,然后把有对应关键字的内容提取出来不就好了?”。这么想的同学只能说你太天真,现在这个信息爆炸的年代,你就算是想遍历全世界一天产生的数据,估计跑一年都跑不出来,何况是互联网上所有的数据?真这么做,谷歌 ,百度早破产了,这个决议直接 pass

其实,众多搜索引擎的背后,都使用了一个倒排索引的结构(这个结构我们后面会讲),通过构建索引,可以大幅度减少我们引擎的搜索时间。其中,将这个结构用得最溜的中间件,就是 ElasticSearch 了,现在绝大多数搜索引擎,背后都有 ES 的帮忙(当然,其中肯定还有其他优化的,但是不是我们今天的主题)

当然,我们不可能说自己写个搜索引擎,那么学习 ES 的目的在哪里呢?其实在很多 B 端应用里,也是有全站搜索的需求的:

  • 学习网站 How2j 的站内搜索

学新通

咱们以后做项目,肯定也是有类似需求的,所以说 ES 是一个不得不学的中间件

二、倒排索引

在上一章简介中我们就提到,ES 背后的结构是倒排索引

那什么是倒排索引?这里我们先来看看下面的对比图

  • 正排索引

咱们的数据库就是这个结构,主键在前,内容在后。走索引的时候,就算查的是二级索引,也是查出的聚簇索引的值,然后在聚簇索引中查整行的值的

学新通

  • 倒排索引

倒排索引就好玩了,内容在前,id 在后

学新通

那倒排索引是怎么起作用的?这个索引又是怎么分出来的?

索引分出来,依靠的是分词器。分词器会将一句话冲的词拆出来,作为索引 key(具体怎么分的词,这就涉及语言学的知识了,这里不加讨论)

当咱们进行查询的时候,会将关键词与索引 key 进行对照,找到一致的话,就会将对应的 文档 ID 返回,从而帮助我们找到这个关键词所在的对应文档

学新通

当然,上面这个是简易模型,其问题也十分明显,比如如果索引一多,一样会产生效率问题。但因为我们是 ES 简明教程,这些问题的解决方案我们暂且按下不表。

三、ES 关键字

这里主要介绍三个关键词

当然,balabala 一大串话打出来各位估计也是懵的,所以我们还是请出我们熟悉的关系型数据库-mysql 来做一个对比

1、索引 index

ES 的索引,相当于 mysql 中的一个数据库

2、类型 type

ES 的类型,相当于 mysql 中的表

3、文档 document

文档,相当于 mysql 中,一张表的一行数据

为了方便记忆,我也将他们三者的对应关系用图示标出了

学新通

学新通

四、ES 安装

ES 我们一共要安装两个东西,elasticsearch 本体kibana图形化工具

这两个又是个啥?

elasticsearch 本体其实就相当于我们的 mysql,kibana 就相当于 navicat 了

安装环境是 linux,使用 docker 进行安装,这里要注意,elasticsearch 本体kibana图形化工具版本一定要一致!!!

  • 安装 es 和 kibana
# 拉去镜像
docker pull elasticsearch:7.13.3
docker pull kibana:7.13.3

# 配置 es 数据卷
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
chmod -R 777 /mydata/elasticsearch

# 配置 kibana 数据卷
mkdir -p /mydata/kibana/config/
vi /mydata/kibana/config/kibana.yml

kibana 配置文件内容如下:

#
# ** THIS IS AN AUTO-GENERATED FILE **
#

# Default Kibana configuration for docker target
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://101.35.142.79:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true
  • 启动 ES

这里我们要限制一下 es 使用的内存,不然用起来会特别的卡(毕竟咱买的是乞丐版云服务器)

这里多提一句,一般情况下,企业里用的 ES 服务器,光是内存占用就会达到 32 G

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/user/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/user/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/user/share/elasticsearch/plugins \
-d elasticsearch:7.13.3
  • 启动 kibana
docker run -d \
--name=kibana \
--restart=always \
-p 5601:5601 \
-v /mydata/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml \
kibana:7.13.3

只要输入上面的命令,应该就不会有什么问题了

五、ES 客户端操作

1、elasticsearch-head

这个方法过于老旧,现在没人用它了,我们这里也就不演示了

2、使用 es 的 restful api(ES 操作入门)

这个是现在的主流 ES 操作方式,我们这里可以使用 postman 进行操作

1)查询 es 节点信息

使用 _cat

# 查询所有节点
GET /_cat/nodes
# 查看 es 健康状况
GET /_cat/health
# 查看主节点
GET /_cat/master
# 查看所有索引
GET /_cat/indices
2)插入/更新

使用 post 和 put 都可以进行插入更新操作,但是二者有区别

1- 使用 post

使用 post ,可以带 id ,可以不带 id

不带 id 的时候,每次都会自动生成随机 id ,这样就永远是插入操作:

学新通

带 id 的时候,当对应 Index-type 中,当前 id 存在的时候,是更新操作:

学新通

2- 使用 put

使用 put 的时候,必须带上 id

当对应 Index-type 中,当前 id 不存在的时候,是插入操作:

学新通

当对应 Index-type 中,当前 id 存在的时候,是更新操作:

学新通

3- 更新操作(带乐观锁

我们可以看到,我们的每次更新的返回值,都会带上一个 _seq_no ,这个是序列号,用来控制并发更新的

那我们该怎么用到这个乐观锁呢?

乐观锁有不懂的小伙伴,我先来介绍一下:使用乐观锁在更新的时候需要带三个值—旧的乐观锁号、要修改的值、要修改的位置

当我们进行修改的时候,如果修改位置的乐观锁版本号与我们旧的版本号一致,说明之前没有其他人进行修改,我们就可以放心大胆的进行修改,修改前,我们还需要将乐观锁版本号 1,从而达到并发安全的目的

ES 中使用这个乐观锁,我们需要带上下面两个参数:

学新通

发送请求后,版本号 1了:

学新通

这个时候,如果我们还是以版本号为 6 的情况进行更新:

就会报错

学新通

4- 更新操作(带 _update)

我们之前操作的时候,如果 index type id 一致,就会进行更新,无论其内容是否一致

学新通

但是如果带上 _update,如果信息是一致的话,不会进行更新操作,且版本号信息 version 也不会发生变化

3)查询操作

查询操作好说,其 url 是 http://<es宿主机 ip>:9200/<index>/<type>/<id>

学新通

4)删除操作

删除操作使用 DELETE 即可

我们可以删除 Index,也可以是 Index/type ,也可以是 /index/type/id

这里要注意一点 ES 7 已经不建议使用 type ,ES8 完全删除了 type

学新通

5)批量操作

批量操作一定要用到 kibana

第四部分,我们已经介绍了怎么安装 kibana 了,访问的话,只需要在浏览器输入 kiubana宿主机ip:5601 即可,如果无法访问,说明你的 kibana 安装出错了

学新通

进入kibana 主页后,我们点击开发者工具:

学新通

我们输入批量查询的语句,点击执行:

POST consumer/name/_bulk
{"index":{"_id":"3"}}
{"name":"kkk"}

{"index":{"_id":"4"}}
{"name":"qqq"}

可以看到,这边执行成功

学新通

从上面的例子,我们可以总结批量操作的语法格式:

POST /index/<type>/_bulk
{action: {metadata}}
{request body}
{action: {metadata}}
{request body}

这里要注意一下几点:

1、如果要批量操作,不同的操作之间不能有空行

学新通

更加复杂的例子:

学新通

3、ES 进阶

上一小节,我们介绍了 ES 中的基本增删改查操作,但是有些时候,我们是有更加复杂的查询需求的,这就需要我们了解 ES 的一些进阶知识了

在开始之前,我们先要准备待操作的数据源,这个我们可以在 ES 官方文档中找到:点击获取批量插入文档,获取后在 kibana 控制台执行即可

ES 进阶查询有两种方式,一种是 url 带参数的方式:

学新通

但是这种方式不够明了,我们更多的还是使用下面这种方式

1)进阶查询

使用请求体 Query DSL:

  • 查询所有
GET /bank/_search

注意,ES 会对我们的查询结果进行默认分页的

学新通

  • 查询升降序

按照 balance 字段进行升序排列

GET /bank/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "balance": {
        "order": "asc"
      }
    }
  ]
}
  • 分页查询

我们只要添加 from size 字段即可

学新通

  • 只查询部分字段:

在 _source 属性中,添加字段名称(数组形式)

学新通

  • 按照特定字段值进行搜索:

之前我们都是写的 match_all,其实我们是可以依照字段值进行搜索的

比如说我们搜索 balanece 为 19655 的账户信息:

GET bank/_search
{
  "query": {
    "match": {
      "balance": "19655"
    }
  }
}

学新通

当字段中的内容是空格分隔的时候,会默认进行分析,将其作为两个条件进行匹配

学新通

  • 多字段匹配

我们也可以让多个字段进入匹配的备选列表中:

查询 address 和 state 字段中有 “mill” 的

GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "mill",
      "fields": ["address","state"]
    }
  }
}
2)查询得分

我们在查询完后,都能返现有一个 _score 字段,那这个字段是啥意思呢?

学新通

其实,得分字段表示我们查询到的信息的匹配程度

比如说我们进行有两个信息的查询:

学新通

这个时候看得分最高的,它是两个信息全中了

学新通

咱们再来看看得分第二高的,它只中了一个,得分5.4

学新通

es 就是通过这种方式,将查询到的信息,按照相关程度,由上到下进行排列,是不是和咱们的搜索引擎很像?(付费购买排名这事儿我们暂且不谈)

学新通

3)复合查询

上面的查询方式都是小打小闹,在 mysql 中,充其量也就是一个 where 的事情

真正要查询对应的信息,还得看复合查询

复合查询,是要用一个 boolean 类型包裹的,里面包含了查询条件(和 lambda 表达式十分类似)

查询条件包含 must(必须满足),must_not(必须不满足),should(可以满足,如果有 mast ,以 mast 为主)

  • 查询地址中包含 mill ,但是不为男性的信息:
GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "address": "mill"
        }}
      ],
      "must_not": [
        {"match": {
          "gender": "m"
        }}
      ]
    }
  }
}
学新通

学新通

  • 带 should 的查询

有 sholud 的话,以 mast 为主,如果 mast 中的查询结果中,有囊括了 should 的话那最好

如果 mast 的插叙结果中没有 should 的信息,那么 should 就不查了

学新通

  • 过滤查询 filter

写过 stream 流式编程的小伙伴,肯定对 filter 不陌生

filter 可以过滤出我们所需要的条件

比如,我们想查出地址包含 mill,但是 balance 范围在 10000 - 20000 的信息

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "address": "mill"
        }}
      ],
      "filter": [
        {
          "range": {
            "balance": {
              "gte": 10000,
              "lte": 20000
            }
          }
        }
      ]
    }
  }
}
学新通

学新通

  • term 匹配:

term 和 match 差不多,都是用来进行匹配的

区别在于 match 是匹配字符串的,但是 term 是匹配非字符串的数据

学新通

当然,也不是说用 match 就不行,用 match 查询的结果是一样的

这里介绍 match ,是为了防止各位在以后看到别人写 match 不认识

4)聚合

mysql 有聚合函数,比如 sum(),min(),max() 这些

当然 ,ES 中也有聚合函数

  • 分组查询和查询平均值:

学新通

那个 size:0 是为了不把查询的信息显示出来的

学新通

当然,聚合中也可以嵌套聚合

  • 查询不同年龄层的平均薪资:
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "group_by_age": {
      "terms": {
        "field": "age",
        "size": 1000
      },
      "aggs": {
        "avg_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}
学新通

这样,咱就查出了不同年龄层的平均薪资

学新通

咱再来个更复杂的聚合查询

  • 查询不同年龄层、不同性别的平均薪资
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "group_by_name": {
      "terms": {
        "field": "age",
        "size": 1000
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword",
            "size": 10
          },
          "aggs": {
            "avg_balance": {
              "avg": {
                "field": "balance"
              }
            }
          }
        }
      }
    }
  },
  "size": 0
}
学新通

我们择其中一条结果解读一下:

这条记录表示 32岁的,有52个人

其中男28人,女24人

男平均工资为:22941,女平均工资为 25128

学新通

这里可能有同学要问了,“我为什么不能将这些数据从 ES 中查出来,然后在服务端进行计算呢?”,这是因为,ES 它快啊

通过 ES 计算出结果,可能几个 G 的数据,只要几十毫秒,如果在服务层计算,那可能几秒都不止了

就好像你用计算器进行一个符合公式计算,你非要把这个公式拆开来,将每个部分算好之后,再手动在纸上进行相加,这不是很离谱吗?

把数据查出来,在服务层进行计算,也是同样的道理

5)索引与映射

这里可别把这个索引和关系型数据库的索引搞混了

ES 里的索引,指的就是 ES 中的 index,对应与关系型数据库也就是数据库表

咱么这里创建索引和映射,说白了也就是创建数据库表,并指定其字段类型

下面就是一段创建索引并指定映射的语句:

这里指定映射,其实就是指定字段的数据类型了

学新通

说到数据类型,ES 中的基本数据类型我们还是有必要介绍一下的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k0VQ47cv-1638100742755)(https://gitee.com/faro/images/raw/master/img/20211123185227.png)]

其他类型都好说(毕竟和 java 中的差不多),这里我们要说一下 text 和 keyword 的区别

这里假如要插入一个数据:“faroz is handsome”

这个时候,如果是 text 类型,会对这个关键词进行分析,将其分为如下形式:

# 分词器对词语的定义我们这里暂时忽略
faroz
is 
handsome
faroz is
is handsome
faroz is handsome

这样,想查 faroz 的相关信息的话,输入上面关键词的任何一个都是可以的

而 keyword 类型的话,就不会进行分词:

# keyword 类型,我们输什么,就存什么
faroz is handsome

这样,如果我们要查 faroz,就必须输入 faroz is handsome

下面是创建一个索引的示例:

其含义是创建一个名叫 my-index 的索引,其字段有 age,emali,name,对应的类型为 integer,keyword,text

PUT /my-index
{
  "mappings": {
    "properties": {
      "age":{"type": "integer"},
      "email":{"type": "keyword"},
      "name":{"type": "text"}
    }
  }
}

当然,我们也可以对索引进行修改:

添加索引的字段

PUT /my-index/_mapping
{
  "properties": {
    "employ-id":{
      "type":"integer",
      "index":false
    }
  }
}

我们也可以用如下命令查询当前索引的所有信息:

GET /my-index/_mapping

学新通

这里可能有细心的小伙伴要发问了,“诶,说好的 index,type,document ,你这里怎么只有两个部分?”,其实,在 ES7往后,官方就不推荐使用 type 了,而是建议我们直接在 index 下创建 document 就好

那可能又会有小伙伴问,“如果我们用老版本的 ES,已经存了大量数据了,有什么办法对这些数据进行迁移,将其移到新版本中呢?”,我们可以用下面的指令

学新通

六、分词器

1、介绍

什么是分词器?为什么要分词器?

分词器,顾名思义,就是将存储的信息,拆分成词语进行存储

那为什么要分词器呢?

你想想,如果有一篇文章的标题为 “elasticsearch 教程”,我们希望在搜索 elasticsearch 或者 教程的时候,都能显示这篇文章的信息,该怎么办呢?有小伙伴可能要说了,“用模糊查啊”,nonono,ES 没有模糊查这个功能,我们必须将一句话拆成多个词存储在 ES 中

这里要注意一下中英文的区别,对于英文,如果想要进行分词的话,我们只需要进行空格拆分即可,但是对于中文,总不能单纯的对每个字进行拆分吧,也就是说,这个分词器要强大到能够区分一句中国话中的词语,这个可不是一个简单的事情,需要涉及到语言学,不信邪的同学可以自己尝试实现分词逻辑,看你能不能将任意一句话中的中文单词准确的拆分出来

对于这么一个复杂的事情,我们必须借助其他中间件实现,这里我们一般选用 ik 分词器

2、安装 ik 分词器

这里要注意,ik 分词器的版本,必须要和我们的 es 相同

如果是走咱这个教程的,咱么就要下载 7.13.3 版本的 ik

下载 7.13.3 版本的 ik 分词器

我们在 ES 挂载的数据卷目录中,找到 plugins 目录,然后在里面创建 ik 文件夹

学新通

创建好后,将我们下载的 ik 分词器的压缩包传输到这个 ik 文件夹中,并进行解压

这里要注意一下几点:

1、解压完后一定要删除压缩包

2、 es 和 kibana 都要一起重启

七、JavaAPI

我们之前学完 mysql 后,紧接着就学了 jdbc ,为的就是让后端能和数据库进行交互

ES 同样也有一套对应的 API,在这一章我就带大家熟悉一下

1、后端配置

创建 SpringBoot 项目我就不说了,默认各位都有过 web 开发经验了

  • 导入 Maven 依赖
<dependency>
  <groupId>org.elasticsearch</groupId>
  <artifactId>elasticsearch</artifactId>
  <version>7.13.3</version>
</dependency>
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>transport</artifactId>
  <version>7.13.3</version>
</dependency>
  • 编写配置类
@Configuration
public class ElasticSearchConfig {
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("<es 宿主机 ip>", 9200, "http")
                )
        );
        return restHighLevelClient;
    }

}

学新通

  • 测试配置

我们先写个测试,看看配置完以后是否能成功注入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nsFbLRaW-1638100742756)(https://gitee.com/faro/images/raw/master/img/20211127210500.png)]

2、ES API 的使用

在写测试之前,我我这里要介绍一下项目中 ES 的常见用法,这里咱们要修改一下之前写的 ElasticSearchConfig

@Configuration
public class ElasticSearchConfig {
    public static final RequestOptions COMMON_OPTIONS;
    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        // 这里可以对 COMMOM_OPTIONS 进行配置,COMMOM_OPTIONS 是操作 ES 是需要输入的请求体参数
        // 要是不提前配置的话,就每次都要写一遍了
        //builder.addHeader("Authorization","Bearer"   TOKEN);
        //builder.setHttpAsyncResponseConsumerFactory(
        //        new HttpAsyncResponseConsumerFactory
        //                .HeapBufferedResponseConsumerFactory(30*1024*1024)
        //);
        COMMON_OPTIONS = builder.build();
    }

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("<es 宿主机 ip>", 9200, "http")
                )
        );
        return restHighLevelClient;
    }

}
学新通

这里多出的 COMMOM_OPTIONS ,是我们之后进行 es 操作的函数中,经常需要传入的参数,在这里配置完毕后,方便以后编写的随取随用

1)创建索引
@Data
    class User {
        private String name;
        private String gender;
        private Integer age;
    }

    @Test
    void indexData() throws IOException {
        // 设置索引名称
        IndexRequest indexRequest = new IndexRequest("users");
        // 数据 id
        indexRequest.id("1");

        User user = new User();
        user.setName("FARO_Z");
        user.setAge(21);
        user.setGender("male");
        String jsonString = JSON.toJSONString(user);

        // 设置要保存的数据
        indexRequest.source(jsonString, XContentType.JSON);

        // 执行操作
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, ElasticSearchConfig.COMMON_OPTIONS);

        System.out.println("indexResponse=" indexResponse);
    }
学新通

可以看到咱的返回值

学新通

咱们再在 kibana 中查询一下我们刚刚插入的数据:

可以看到,索引的插入是成功的

学新通

2)复杂查询

还记得我们前面几章插入的银行数据吗

学新通

这一节,我们就要借助这个数据,进行复杂查询

在此之前,对应的 pojo 我们先写好了:

这里要注意一个小点,咱们的类要是静态内部类

因为我们是在测试类中进行测试的,如果只执行咱们的测试方法,那么如果不是静态内部类,就不会扫描、装载它

从而导致我们最后实例化的时候失败

static class Account {
  private int account_number;
  private int balance;
  private String firstname;
  private String lastname;
  private int age;
  private String gender;
  private String address;
  private String employer;
  private String email;
  private String city;
  private String state;
}

这里,我们用下面这个 DQL 做示例:

其含义是查询居住地址包含 mill 的不同年龄层对应的平均薪资

GET bank/_search
{
  "query": {
    "match": {
      "address": "mill"
    }
  },
  "aggs": {
    "group_by_age": {
      "terms": {
        "field": "age",
        "size": 10
      }
    },
    "avg_balance": {
      "avg": {
        "field": "balance"
      }
    }
  },
  "size": 0
}
学新通

我们要在 javaAPI 中还原这个操作:

@Test
    void searchTest() throws IOException {
        // 创建检索请求
        SearchRequest searchRequest = new SearchRequest();
        // 指定索引
        searchRequest.indices("bank");

        // 指定 DQL ,检索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchQuery("address","mill"));

        // 按照年龄分布进行聚合
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("group_by_age").field("age").size(10);
        searchSourceBuilder.aggregation(termsAggregationBuilder);

        // 计算平均薪资
        AvgAggregationBuilder avgAggregationBuilder = AggregationBuilders.avg("avg_balance").field("balance");
        searchSourceBuilder.aggregation(avgAggregationBuilder);

        // 打印看看检索条件是什么样的
        System.out.println("检索条件=" searchSourceBuilder);
        /*
         打印后就是这个样子的
            GET /bank/_search
            {
              "query": {
                "match": {
                  "address": {
                    "query": "mill",
                    "operator": "OR",
                    "prefix_length": 0,
                    "max_expansions": 50,
                    "fuzzy_transpositions": true,
                    "lenient": false,
                    "zero_terms_query": "NONE",
                    "auto_generate_synonyms_phrase_query": true,
                    "boost": 1
                  }
                }
              },
              "aggregations": {
                "group_by_age": {
                  "terms": {
                    "field": "age",
                    "size": 10,
                    "min_doc_count": 1,
                    "shard_min_doc_count": 0,
                    "show_term_doc_count_error": false,
                    "order": [
                      {
                        "_count": "desc"
                      },
                      {
                        "_key": "asc"
                      }
                    ]
                  }
                },
                "avg_balance": {
                  "avg": {
                    "field": "balance"
                  }
                }
              }
            }

         */

        // 为检索请求添加请求体(就是 javaAPI 生成的请求条件)
        searchRequest.source(searchSourceBuilder);

        // 执行检索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
        System.out.println("searchResponse=" searchResponse);


        // 获取检索结果
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String str = hit.getSourceAsString();
            Account account = JSON.parseObject(str, Account.class);
            System.out.println("account=" account);
        }
    }
学新通

有同学可能会对上面的代码有点懵,我划线解释一下:

可以看到对应的代码操作,都是对应的 DQL

这点和咱么熟悉的 Mybatis 十分类似

学新通

最后查询结果如下:

学新通

可能有同学发现了,这个查询结果不够直观,想获得想要的数据的时候会很麻烦,我们其实也可以在 response 中,通过 Aggregations 的名称,获得咱们对应的聚合查询的值:

Aggregations aggregations = searchResponse.getAggregations();
Terms ageAgg = aggregations.get("group_by_age");
for (Terms.Bucket bucket : ageAgg.getBuckets()) {
		System.out.println("年龄="   bucket.getKeyAsString()   "===>"   bucket.getDocCount());
}
Avg avg_balance = aggregations.get("avg_balance");
System.out.println("平均薪资 = "   avg_balance.getValueAsString());

最后展示结果如下:

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhiabfie
系列文章
更多 icon
同类精品
更多 icon
继续加载