ElasticSearch

Elasticsearch 是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene 可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。

一、基本概念

1、Index(索引)

类比 MySQL 的数据库

2、Type(类型)

类比 MySQL 的数据表

3、Document(文件)

类比 MySQL 的数据

4、倒排索引机制

二、Docker 安装

1、下载镜像文件

1
2
3
4
# 存储和检索数据
docker pull elasticsearch:7.4.2
# 可视化检索数据
docker pull kibana:7.4.2

2、创建实例

  • 启动 ElasticSearch
1
2
3
4
5
6
7
8
9
10
11
12
mkdir -p $HOME/mydata/elasticsearch/config
mkdir -p $HOME/mydata/elasticsearch/data
echo "http.host: 0.0.0.0" > $HOME/mydata/elasticsearch/config/elasticsearch.yml
chmod -R 777 $HOME/mydata/elasticsearch/

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v $HOME/mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v $HOME/mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v $HOME/mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
  • 启动 kibana
1
2
3
4
5
# ELASTICSEARCH_HOSTS一定虚拟机的host
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://localhost:9200 -p 5601:5601 -d kibana:7.4.2

# 或者此方法,但要记住先启动es再启动kibana
docker run -d --name kibana --link elasticsearch -p 5601:5601 kibana:7.4.2
  • 设置开机启动
1
2
docker update elasticsearch --restart=always
docker update kibana --restart=always

三、初步探索

1、_cat

1
2
3
4
5
6
7
8
# 查看所有节点
GET /_cat/nodes
# 查看es健康情况
GET /_cat/health
# 查看主节点
GET /_cat/master
# 查看所有索引
GET /_cat/indices

2、索引一个文档

  • 保存一个数据,保存在哪个索引的哪个类型下,指定用那个唯一标识
    PUT customer/external/1;在 customer 索引下的 external 类型下保存 1 号数据为
1
PUT customer/external/1
1
2
3
{
"name":"John Doe"
}
  • PUT 和 POST 对比
    项目功能id一般用途
    POST新增可以不指定新增
    PUT新增/修改必须指定修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# _前缀表示他为元数据
{
# 表明该数据在customer数据库下
"_index": "customer",
# 表明类型
"_type": "external",
# id值
"_id": "1",
# 版本,修改之后升级
"_version": 1,
# 创建时返回created,修改时返回updated
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}

3、查看文档

1
GET /customer/external/1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"_index": "customer",
"_type": "external",
"_id": "1",
"_version": 1,
# 并发控制字段,每次更新+1,做乐观锁
"_seq_no": 0,
# 同上,主分片重新分配,如重启就会变化
"_primary_term": 1,
"found": true,
"_source": {
"name": "John Doe"
}
}

4、更新文档

  • 三种实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 方法一:post带update
# 需要doc标签,数据无变化不操作,version不更新,result为noop(no operation)
POST customer/external/1/_update
{
"doc":{
"name":"John Doew"
}
}

# 方法二:post不带update
# 不需要doc,重复执行更新也能成功,也version会更新
POST customer/external/1
{
"name":"John Doew2"
}

# 方法三:put操作(不存在带update的情况)
# 更新,不管是否有变化,version都会增加
#
PUT customer/external/1
{
"name":"John Doew3"
}

# 更新同时增加属性
POST customer/external/1/_update
{
"doc":{
"name":"John Doew4",
"age":20
}
}
  • 对比
    • POST 会对比原文件数据,相同不会有操作,version 不增加
    • PUT 总会更新数据且增加 version 字段
    • 带_update 对比原数据一样就不进行任何操作
    • 使用场景: - 大并发更新:不带 update - 大并发查询偶尔更新,带 update;对比更新,重新计算分配规则
      带 update不带 update
      POSTbody 带 doc 标签
      PUT

5、删除文档或索引

1
DELETE customer/external/1
1
DELETE customer

注:elasticsearch 并没有提供删除类型的操作,只提供了删除索引和文档的操作

6、bulk 批量 API

  • 语法
1
2
3
4
5
6
7
8
POST customer/external/_bulk
# 元数据
{action:{metadata}}
# 具体数据
{request body }

{action:{metadata}}
{request body}
  • 例子

  • 规则
1
2
3
4
批量操作,彼此之间是独立:当发生某一条执行发生失败时,其他的数据仍然能够接着执行.
bulkapi 以此按顺序执行所有的 action(动作)。
如果一个单个的动作因任何原因失败,它将继续处理它后面剩余的动作。
当 bulk api 返回时,它将提供每个动作的状态(与发送的顺序相同),可以检查是否一个指定的动作是否失败了。

7、样本测试数据

测试数据,将所有数据保存,基于此进行测试

四、进阶检索

1、Search API

1、检索信息

ES 支持两种基本方式检索;

  • 通过 REST request uri 发送搜索参数 (uri +检索参数);
1
GET bank/_search/?q=*&sort=account_number:asc

1
2
3
4
5
6
7
8
9
took:执行时间
time_out:是否超时
\_shards:被搜索的分片数量
hits:搜索结果
hits.total:搜索结果
hits.hits:实际的搜索结果数组
sort:结果排序的 key
score:相关性得分
max_score:最高得分
  • 通过 REST request body 来发送它们(uri+请求体);
1
2
3
4
5
6
7
8
9
10
11
GET bank/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"account_number": "asc"
}
]
}

2、Query DSL

1、基本语法

Elasticsearch 提供了一个可以执行查询的 JSON 风格的 DSL。这个被称为 Query DSL,该查询语言非常全面。

  • 一个查询语句的典型结构
1
2
3
4
QUERY_NAME:{
  ARGUMENT:VALUE,
  ARGUMENT:VALUE,...
}
1
2
3
4
5
6
7
8
{
 QUERY_NAME:{
    FIELD_NAME:{
      ARGUMENT:VALUE,
      ARGUMENT:VALUE,...
    }  
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET bank/_search
{
# 查询规则
 "query": {
   "match_all": {}
},
# 分页规则,从哪开始,页码大小
 "from": 0,
 "size": 5,
# 排序规则
 "sort": [
  {
     "account_number": {
       "order": "desc"
    }
  }
]
}
  • match_all 查询类型【代表查询所有的所有】,es 中可以在 query 中组合非常多的查询类型完成复杂查询;
  • 除了 query 参数之外,我们可也传递其他的参数以改变查询结果,如 sort,size;
  • from+size 限定,完成分页功能;
  • sort 排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准;

2、返回部分

  • 规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GET bank/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 5,
"sort": [
{
"account_number": {
"order": "desc"
}
}
],
# 返回,直接指定需要返回的字段
"_source": ["balance","firstname"]
}
  • 例子

3、match - 匹配查询

全文检索,按照评分进行排序,依赖倒排索引

  • 语法
1
2
3
4
5
6
7
8
9
10
11
12
# match匹配字符串,可以做模糊查询
# match匹配数值,为精确查询

GET bank/_search
{
"query": {
# 满足account_number=20的
"match": {
"account_number": 20
}
}
}
  • 例子

4、match_phrase - 短句匹配

将需要匹配的值当成一整个单词(不分词)进行检索。

文本字段的匹配,使用 keyword,匹配的条件就是要显示字段的全部值,要进行精确匹配的。

match_phrase 是做短语匹配,只要文本中包含匹配条件,就能匹配到。

1
2
3
4
5
6
7
8
GET bank/_search
{
"query": {
"match_phrase": {
"address": "mill road"
}
}
}

5、multi_match - 多字段匹配

从几个字段中查询,相当于多个 match 叠加,会进行分词

1
2
3
4
5
6
7
8
9
10
11
12
13
# 从state和address中分词查询mill
GET bank/_search
{
"query": {
"multi_match": {
"query": "mill",
"fields": [
"state",
"address"
]
}
}
}

6、bool - 复合查询

复合查询,即可以进行嵌套,实现复杂的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# must:必须达到must所列举的所有条件
# must_not,必须不匹配must_not所列举的所有条件。
# should,应该满足should所列举的条件。如果到达会增加相关文档的评分,并不会改变查询的
# 结果。如果query中只有should且只有一种匹配规则,那么should的条件就会被作为默认匹配
# 条件二区改变查询结果。
GET bank/_search
{
"query":{
"bool":{
"must":[
{"match":{"address":"mill"}},
{"match":{"gender":"M"}}
]
}
}
}

7、filter - 结果过滤

对结果进行过滤,相当于 MySQL 的having。使用时不计算相关性得分。

并不是所有的查询都需要产生分数,特别是哪些仅用于 filtering 过滤的文档。为了不计算分数,elasticsearch 会自动检查场景并且优化查询的执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 这里先是查询所有匹配address=mill的文档。
# 然后再根据10000<=balance<=20000进行过滤查询结果
GET bank/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address": "mill"
}
}
],
"filter": {
"range": {
"balance": {
"gte": "10000",
"lte": "20000"
}
}
}
}
}
}

Each must, should, and must_not element in a Boolean query is referred to as a query clause. How well a document meets the criteria in each must or should clause contributes to the document’s relevance score. The higher the score, the better the document matches your search criteria. By default, Elasticsearch returns documents ranked by these relevance scores.

在 boolean 查询中,must, shouldmust_not 元素都被称为查询子句 。 文档是否符合每个“must”或“should”子句中的标准,决定了文档的“相关性得分”。   得分越高,文档越符合您的搜索条件。   默认情况下,Elasticsearch 返回根据这些相关性得分排序的文档。

The criteria in a must_not clause is treated as a filter. It affects whether or not the document is included in the results, but does not contribute to how documents are scored. You can also explicitly specify arbitrary filters to include or exclude documents based on structured data.

“must_not”子句中的条件被视为“过滤器”。 它影响文档是否包含在结果中,   但不影响文档的评分方式。   还可以显式地指定任意过滤器来包含或排除基于结构化数据的文档。

8、term - 匹配查询

和 match 一样。匹配某个属性的值。全文检索字段用 match,其他非 text 字段匹配用 term。

1
2
3
4
5
6
7
8
GET bank/_search
{
"query": {
"term": {
"address": "mill Road"
}
}
}

9、aggregation - 执行聚合

从数据中分组和提取数据。

最简单的聚合方法大致等于 SQL Group by 和 SQL 聚合函数。在 elasticsearch 中,执行搜索返回 this(命中结果),并且同时返回聚合结果,把以响应中的所有 hits(命中结果)分隔开的能力。这是非常强大且有效的,你可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的 API 啦避免网络往返。

3、Mapping

1、字段类型

2、映射

Maping 是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和索引的

3、新版本改变

创建映射

1
2
3
4
5
6
7
8
9
10
PUT /my_index
{
"mappings": {
"properties": {
"age": {"type": "integer"},
"email": {"type": "keyword"},
"name": {"type": "text"}
}
}
}

查看映射

添加新字段映射

更新映射

对于已经存在的字段映射,我们不能更新。更新必须创建新的索引,进行数据迁移。

数据迁移

1
2
3
4
5
6
7
8
9
10
POST _reindex [固定写法]
{
"source":{
"index":"twitter",
"twitter":"twitter"
},
"dest":{
"index":"new_twitters"
}
}

更多详情见: 数据迁移

4、分词

一个 tokenizer(分词器)接收一个字符流,将之分割为独立的 tokens(词元,通常是独立的单词),然后输出 tokens 流。

例如:whitespace tokenizer 遇到空白字符时分割文本。它会将文本“Quick brown fox!”分割为[Quick,brown,fox!]。

该 tokenizer(分词器)还负责记录各个 terms(词条)的顺序或 position 位置(用于 phrase 短语和 word proximity 词近邻查询),以及 term(词条)所代表的原始 word(单词)的 start(起始)和 end(结束)的 character offsets(字符串偏移量)(用于高亮显示搜索的内容)。

elasticsearch 提供了很多内置的分词器,可以用来构建 custom analyzers(自定义分词器)。

关于分词器: 分词器

  • 使用
1
2
3
4
5
POST _analyze
{
"analyzer": "standard",
"text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}

1、安装 ik 分词器

1
2
3
4
5
# 注意和es版本对应
[root@server1 elasticsearch]# wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
[root@server1 elasticsearch]# unzip elasticsearch-analysis-ik-7.4.2.zip -d ik
[root@server1 elasticsearch]# mv ik plugins/
[root@server1 elasticsearch]# docker restart elasticsearch kibana

2、测试

  • 使用默认分词器
1
2
3
4
GET my_index/_analyze
{
  "text":"我是中国人"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
 "tokens" : [
  {
     "token" : "我",
     "start_offset" : 0,
     "end_offset" : 1,
     "type" : "<IDEOGRAPHIC>",
     "position" : 0
  },
  {
     "token" : "",
     "start_offset" : 1,
     "end_offset" : 2,
     "type" : "<IDEOGRAPHIC>",
     "position" : 1
  },
  {
     "token" : "",
     "start_offset" : 2,
     "end_offset" : 3,
     "type" : "<IDEOGRAPHIC>",
     "position" : 2
  },
  {
     "token" : "",
     "start_offset" : 3,
     "end_offset" : 4,
     "type" : "<IDEOGRAPHIC>",
     "position" : 3
  },
  {
     "token" : "",
     "start_offset" : 4,
     "end_offset" : 5,
     "type" : "<IDEOGRAPHIC>",
     "position" : 4
  }
]
}
  • 使用智能分词器
1
2
3
4
5
GET my_index/_analyze
{
  "analyzer": "ik_smart",
  "text":"我是中国人"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
 "tokens" : [
  {
     "token" : "我",
     "start_offset" : 0,
     "end_offset" : 1,
     "type" : "CN_CHAR",
     "position" : 0
  },
  {
     "token" : "",
     "start_offset" : 1,
     "end_offset" : 2,
     "type" : "CN_CHAR",
     "position" : 1
  },
  {
     "token" : "中国人",
     "start_offset" : 2,
     "end_offset" : 5,
     "type" : "CN_WORD",
     "position" : 2
  }
]
}
  • 使用 max 分词器
1
2
3
4
5
GET my_index/_analyze
{
  "analyzer": "ik_max_word",
  "text":"我是中国人"
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "中国人",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "中国",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 3
},
{
"token" : "国人",
"start_offset" : 3,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 4
}
]
}

3、自定义词库

安装 nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 随便启动一个,为了复制出配置
lqs@PolarisiMac ~/mydata  docker run -p 80:80 --name nginx -d nginx:1.10
# 把配置文件复制到mydata文件夹下
lqs@PolarisiMac ~/mydata  docker container cp nginx:/etc/nginx .
# 将nginx命名为conf,新建nginx文件夹,将conf移进nginx
└── nginx
└── conf
├── conf.d
│   └── default.conf
├── fastcgi_params
├── koi-utf
├── koi-win
├── mime.types
├── modules -> /usr/lib/nginx/modules
├── nginx.conf
├── scgi_params
├── uwsgi_params
└── win-utf
# 创建新的nginx
docker run -p 80:80 --name nginx \
-v $HOME/mydata/nginx/html:/usr/share/nginx/html \
-v $HOME/mydata/nginx/logs:/var/log/nginx \
-v $HOME/mydata/nginx/conf:/etc/nginx \
-d nginx:1.10

添加字典

1
echo "尚硅谷 乔碧罗" > /mydata/nginx/html/fenci.txt

修改配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vim $HOME/mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict"></entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">http://#/es/fenci.txt</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

使用

  • 先重启 es

五、ElasticSearch-Rest-Client

有几种办法

  1. 9300: TCP
  • spring-data-elasticsearch:transport-api.jar;
    • springboot 版本不同,ransport-api.jar 不同,不能适配 es 版本
    • 7.x 已经不建议使用,8 以后就要废弃
  1. 9200: HTTP

1、SpringBoot 整合 ES

  • 1、maven 依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.4.2</elasticsearch.version>
</properties>

<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch.version}</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>${elasticsearch.version}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch.version}</version>
</dependency>
  • 2、yml 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 应用名称
spring:
application:
name: gulimall-search
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
elasticsearch:
jest:
proxy:
port: 9200
host: localhost
username: elastic
password: changme
  • 3、配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//以后会修改
@Configuration
public class GulimallElasticSearchConfig {

//通用设置项
public static final RequestOptions COMMON_OPTIONS;
static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
COMMON_OPTIONS = builder.build();
}

@Bean
public RestHighLevelClient esRestClient(){
RestClientBuilder builder = null;

builder = RestClient.builder(new HttpHost("localhost",9200,"http"));

RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
}
  • 4、测试使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallSearchApplicationTests {

@Resource
private RestHighLevelClient client;

@Data
@AllArgsConstructor
@NoArgsConstructor
class User{
private String userName;
private String gender;
private Integer age;
}

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
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;
}
/**
* 更新保存二合一
* @throws IOException 网络操作必定需要处理异常
*/
@Test
public void contextLoads() throws IOException {
IndexRequest indexRequest = new IndexRequest("users");
indexRequest.id("1");
//使用方法一
// indexRequest.source("userName","zhangsan","age",18,"gender","male");
//使用方法二
User user = new User("lqs", "male", 21);
String jsonString = JSON.toJSONString(user);
indexRequest.source(jsonString, XContentType.JSON);

//执行操作
IndexResponse index = client.index(indexRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

//提取响应数据
System.out.println(index);
}

@Test
public void testSearch() throws IOException {
//1、创建检索请求
SearchRequest searchRequest = new SearchRequest();
//指定索引
searchRequest.indices("bank");
//指定DSL,检索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
searchRequest.source(sourceBuilder);
//11构建检索条件
sourceBuilder.query(QueryBuilders.matchQuery("address","mill"));
// System.out.println(sourceBuilder.toString());
// sourceBuilder.from();
// sourceBuilder.size();
//12按照年龄聚合
TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
sourceBuilder.aggregation(ageAgg);
//13计算平均薪资
AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
// System.out.println("检索条件"+sourceBuilder.toString());
sourceBuilder.aggregation(balanceAvg);

//2、执行检索
SearchResponse response = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);

//3、结果分析
// System.out.println(response.toString());
Map map = JSON.parseObject(response.toString(), Map.class);
//{"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.0}}}}
//{"took":24,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":4,"relation":"eq"},"max_score":5.4032025,"hits":[{"_index":"bank","_type":"account","_id":"970","_score":5.4032025,"_source":{"account_number":970,"balance":19648,"firstname":"Forbes","lastname":"Wallace","age":28,"gender":"M","address":"990 Mill Road","employer":"Pheast","email":"forbeswallace@pheast.com","city":"Lopezo","state":"AK"}},{"_index":"bank","_type":"account","_id":"136","_score":5.4032025,"_source":{"account_number":136,"balance":45801,"firstname":"Winnie","lastname":"Holland","age":38,"gender":"M","address":"198 Mill Lane","employer":"Neteria","email":"winnieholland@neteria.com","city":"Urie","state":"IL"}},{"_index":"bank","_type":"account","_id":"345","_score":5.4032025,"_source":{"account_number":345,"balance":9812,"firstname":"Parker","lastname":"Hines","age":38,"gender":"M","address":"715 Mill Avenue","employer":"Baluba","email":"parkerhines@baluba.com","city":"Blackgum","state":"KY"}},{"_index":"bank","_type":"account","_id":"472","_score":5.4032025,"_source":{"account_number":472,"balance":25571,"firstname":"Lee","lastname":"Long","age":32,"gender":"F","address":"288 Mill Street","employer":"Comverges","email":"leelong@comverges.com","city":"Movico","state":"MT"}}]}}
//31 获取所有查到的数据
SearchHits hits = response.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits){
/*{
"_index" : "bank",
"_type" : "account",
"_id" : "970",
"_score" : 5.4032025,
"_source" : {
"account_number" : 970,
"balance" : 19648,
"firstname" : "Forbes",
"lastname" : "Wallace",
"age" : 28,
"gender" : "M",
"address" : "990 Mill Road",
"employer" : "Pheast",
"email" : "forbeswallace@pheast.com",
"city" : "Lopezo",
"state" : "AK"
}
}*/
// System.out.println(hit.toString());
String str = hit.getSourceAsString();
Account account = JSON.parseObject(str, Account.class);
// System.out.println("account: "+account);
}
//32 获取检索到的分析信息
Aggregations aggregations = response.getAggregations();
for(Aggregation aggregation : aggregations.asList()){
// System.out.println("当前聚合"+aggregation.getName());
}

Terms ageAgg1 = aggregations.get("ageAgg");
for (Terms.Bucket bucket : ageAgg1.getBuckets()){
String keyAsString = bucket.getKeyAsString();
System.out.println("年龄"+keyAsString);
}
Avg balanceAvg1 = aggregations.get("balanceAvg");
Aggregation balanceAvg2 = aggregations.get("balanceAvg");
}
}


ElasticSearch
https://polarisink.github.io/20220813/yuque/ElasticSearch/
作者
Areis
发布于
2022年8月13日
许可协议