一、前言

ES 提供多种不同的客户端:

不推荐使用:

1、TransportClient

ES 提供的传统客户端,官方计划 8.0 版本删除此客户端。

2、RestClient

Elasticsearch 提供了两个 JAVA REST Client 版本:

  • java low level rest client

    低级别的 rest 客户端,通过 http 与集群交互,用户需自己编组请求 JSON 串,及解析响应 JSON 串。兼容所有 Elasticsearch 版本。

  • java high rest client

    高级别的 REST 客户端,基于低级别的 REST 客户端,增加了编组请求 JSON 串、解析响应 JSON 串等相关 API,使用的版本需要保存和 ES 服务一致的版本,否则会有版本问题。

兼容性说明:

依赖 jdk1.8 和 Elasticsearch core project

这里使用高级客户端:java high rest client。

二、准备工作

创建一个 SpringBoot 项目 elasticsearch-demo

导入 pom 坐标

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
<!--引入es的坐标-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.4.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.4.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

resource 文件夹下修改 application.yml 文件

1
2
3
elasticsearch:
host: 192.168.88.88
port: 9200

创建 ElasticSearchConfig配置类

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
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {
private String host;
private int port;

public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

@Bean
public RestHighLevelClient client() {
return new RestHighLevelClient(RestClient.builder(
new HttpHost(
host,
port,
"http"
)
));
}
}

新建测试类ElasticsearchTest,测试是否能正常连接

1
2
3
4
5
6
7
8
9
10
@RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchTest {
@Autowired
private RestHighLevelClient client;

@Test
public void contextLoads() {
System.out.println(client);
}

三、具体操作

1、创建索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 添加索引
*/
@Test
public void addIndex() throws Exception {
// 使用client获取操作索引的对象
IndicesClient indicesClient = client.indices();
// 具体操作,获取返回值
CreateIndexRequest createRequest = new CreateIndexRequest("abc");
CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);

// 根据返回值判断结果true或者false
System.out.println(response.isAcknowledged());
}

2、添加索引和映射

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
/**
* 添加索引
*/
@Test
public void addIndexAndMapping() throws IOException {
// 使用client获取操作索引的对象
IndicesClient indicesClient = client.indices();
// 具体操作,获取返回值
CreateIndexRequest createRequest = new CreateIndexRequest("aaa");
// 设置mappings
String mapping = "{\n" +
" \"properties\" : {\n" +
" \"address\" : {\n" +
" \"type\" : \"text\",\n" +
" \"analyzer\" : \"ik_max_word\"\n" +
" },\n" +
" \"age\" : {\n" +
" \"type\" : \"long\"\n" +
" },\n" +
" \"name\" : {\n" +
" \"type\" : \"keyword\"\n" +
" }\n" +
" }\n" +
" }";
createRequest.mapping(mapping, XContentType.JSON);

CreateIndexResponse response = indicesClient.create(createRequest, RequestOptions.DEFAULT);

// 根据返回值判断结果
System.out.println(response.isAcknowledged());
}

3、查询索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 查询索引
*/
@Test
public void queryIndex() throws IOException {
IndicesClient indices = client.indices();

GetIndexRequest getReqeust = new GetIndexRequest("aaa");
GetIndexResponse response = indices.get(getReqeust, RequestOptions.DEFAULT);

// 获取结果
Map<String, MappingMetaData> mappings = response.getMappings();
for (String key : mappings.keySet()) {
System.out.println(key + ":" + mappings.get(key).getSourceAsMap());
}
}

4、删除索引

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 删除索引
*/
@Test
public void deleteIndex() throws IOException {
IndicesClient indices = client.indices();

DeleteIndexRequest deleteRequest = new DeleteIndexRequest("abc");
AcknowledgedResponse response = indices.delete(deleteRequest, RequestOptions.DEFAULT);

System.out.println(response.isAcknowledged());
}

5、判断索引是否存在

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 判断索引是否存在
*/
@Test
public void existIndex() throws IOException {
IndicesClient indices = client.indices();

GetIndexRequest getRequest = new GetIndexRequest("aaa");
boolean exists = indices.exists(getRequest, RequestOptions.DEFAULT);

System.out.println(exists);
}

6、添加文档,使用 map 作为数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 添加文档,使用map作为数据
*/
@Test
public void addDoc() throws IOException {
//数据对象,map
Map data = new HashMap();
data.put("address", "深圳");
data.put("name", "南山");
data.put("age", 20);

// 获取操作文档的对象
IndexRequest request = new IndexRequest("aaa").id("1").source(data);
// 添加数据,获取结果
IndexResponse response = client.index(request, RequestOptions.DEFAULT);

// 打印响应结果
System.out.println(response.getId());
}

7、添加文档,使用对象作为数据

创建 Person 实体类

1
2
3
4
5
6
7
8
public class Person {
private String id;
private String name;
private int age;
private String address;

// get和set方法... ... ...
}

使用对象作为数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 添加文档,使用对象作为数据
*/
@Test
public void addDoc2() throws IOException {
//数据对象,javaObject
Person p = new Person();
p.setId("2");
p.setName("上海");
p.setAge(30);
p.setAddress("宝山区");

//将对象转为json
String data = JSON.toJSONString(p);

// 获取操作文档的对象
IndexRequest request = new IndexRequest("aaa").id(p.getId()).source(data, XContentType.JSON);
// 添加数据,获取结果
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
// 打印响应结果
System.out.println(response.getId());
}

8、修改文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 修改文档:添加文档时,如果id存在则修改,id不存在则添加
*/
@Test
public void updateDoc() throws IOException {
// 数据对象,javaObject
Person p = new Person();
p.setId("2");
p.setName("北京");
p.setAge(30);
p.setAddress("北京昌平区");

// 将对象转为json
String data = JSON.toJSONString(p);

// 获取操作文档的对象
IndexRequest request = new IndexRequest("aaa").id(p.getId()).source(data, XContentType.JSON);
// 添加数据,获取结果
IndexResponse response = client.index(request, RequestOptions.DEFAULT);

// 打印响应结果
System.out.println(response.getId());
}

9、根据 id 查询文档

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 根据id查询文档
*/
@Test
public void findDocById() throws IOException {

GetRequest getReqeust = new GetRequest("aaa", "1");
// getReqeust.id("1");
GetResponse response = client.get(getReqeust, RequestOptions.DEFAULT);
// 获取数据对应的json
System.out.println(response.getSourceAsString());
}

10、根据 id 删除文档

1
2
3
4
5
6
7
8
9
/**
* 根据id删除文档
*/
@Test
public void delDoc() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest("aaa", "1");
DeleteResponse response = client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(response.getId());
}

11、批量操作-脚本

Bulk 批量操作是将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。

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
/**
* 1. 批量操作 bulk
*/
@Test
public void testBulk() throws IOException {
// 创建bulkRequest对象,整合所有操作
BulkRequest bulkRequest = new BulkRequest();

/*
# 删除1号记录
# 添加6号记录
# 修改3号记录 名称为 “三号”
*/

// 删除1号记录
DeleteRequest deleteRequest = new DeleteRequest("person", "1");
bulkRequest.add(deleteRequest);

// 添加6号记录
Map map = new HashMap();
map.put("name", "六号");
IndexRequest indexRequest = new IndexRequest("person").id("6").source(map);
bulkRequest.add(indexRequest);

// 修改3号记录 名称为 “三号”
Map map2 = new HashMap();
map2.put("name", "三号");
UpdateRequest updateReqeust = new UpdateRequest("person", "3").doc(map2);
bulkRequest.add(updateReqeust);

// 执行批量操作
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
RestStatus status = response.status();
System.out.println(status);
}

12、导入数据

将数据库中 Goods 表的数据导入到 ElasticSearch 中

添加坐标

1
2
3
4
5
6
7
8
9
10
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

application.yml 配置文件添加 mybatis 相关配置

1
2
3
4
5
6
7
8
9
10
11
12
# datasource
spring:
datasource:
url: jdbc:mysql://192.168.88.88:3306/es?serverTimezone=UTC
username: root
password: root

# mybatis
mybatis:
# mapper映射文件路径
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.domain

添加 javabean

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
package com.atguigu.config.domain;

import com.alibaba.fastjson.annotation.JSONField;

import java.util.Date;
import java.util.Map;

public class Goods {
private int id;
private String title;
private double price;
private int stock;
private int saleNum;
private Date createTime;
private String categoryName;
private String brandName;
private Map spec;

//在转换JSON时,忽略该字段
@JSONField(serialize = false)
//接收数据库的信息 "{}"
private String specStr;

// 生成get和set方法和toString方法
}

创建 dao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.atguigu.mapper;

import com.atguigu.domain.Goods;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@Mapper
public interface GoodsMapper {

/**
* 查询所有
*/
public List<Goods> findAll();

}

resource 文件夹下面创建 mapper/GoodMapper.xml 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.mapper.GoodsMapper">
<select id="findAll" resultType="goods">
select
`id` ,
`title` ,
`price` ,
`stock` ,
`saleNum` ,
`createTime` ,
`categoryName`,
`brandName` ,
`spec` as specStr

from goods
</select>

</mapper>

添加测试方法

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
@Autowired
private GoodsMapper goodsMapper;
/**
* 批量导入
*/
@Test
public void importData() throws IOException {
// 查询所有数据,mysql
List<Goods> goodsList = goodsMapper.findAll();

// bulk导入
BulkRequest bulkRequest = new BulkRequest();

// 循环goodsList,创建IndexRequest添加数据
for (Goods goods : goodsList) {
String specStr = goods.getSpecStr();
// 将json格式字符串转为Map集合
Map map = JSON.parseObject(specStr, Map.class);
// 设置spec map
goods.setSpec(map);
// 将goods对象转换为json字符串
String data = JSON.toJSONString(goods);
IndexRequest indexRequest = new IndexRequest("goods");
indexRequest.id(goods.getId() + "").source(data, XContentType.JSON);
bulkRequest.add(indexRequest);
}
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(response.status());
}

13、查询所有 matchAll 查询

matchAll 查询:查询所有文档

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
/**
* 查询所有
* 1. matchAll
* 2. 将查询结果封装为Goods对象,装载到List中
* 3. 分页。默认显示10条
*/
@Test
public void testMatch() throws IOException {
// 构建查询请求对象,指定查询的名称
SearchRequest searchRequest = new SearchRequest("goods");
// 创建查询条件构建器SearchSourceBuilder
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 查询条件:查询所有文档
QueryBuilder query = QueryBuilders.matchAllQuery();
// 指定查询条件
sourceBuilder.query(query);
// 添加查询条件构建器 SearchSourceBuilder
searchRequest.source(sourceBuilder);
// 添加分页信息
sourceBuilder.from(0);
sourceBuilder.size(20);
// 进行查询,获取查询结果
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 获取命中对象 SearchHits
SearchHits searchHits = searchResponse.getHits();
// 获取总记录数
long value = searchHits.getTotalHits().value;
List<Goods> goodsList = new ArrayList<>();
// 获取Hits数据 数组
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
// 获取json字符串格式的数据
String sourceAsString = hit.getSourceAsString();
// 转为java对象
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

14、term 查询

term 查询:不会对查询条件进行分词。

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
/**
* termQuery:词条查询
*/
@Test
public void testTermQuery() throws IOException {


SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

// term词条查询
QueryBuilder query = QueryBuilders.termQuery("title", "华为");
sourceBulider.query(query);

searchRequest.source(sourceBulider);

SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

// 转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

15、matchQuery:词条分词查询

match 查询:

  • 会对查询条件进行分词。
  • 然后将分词后的查询条件和词条进行等值匹配
  • 默认取并集(OR)
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
/**
* matchQuery:词条分词查询,分词之后的等值匹配
*/
@Test
public void testMatchQuery() throws IOException {


SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

MatchQueryBuilder query = QueryBuilders.matchQuery("title", "华为手机");
// 求并集
query.operator(Operator.AND);
sourceBulider.query(query);

searchRequest.source(sourceBulider);


SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

// 转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

16、模糊查询-脚本

  • wildcard 查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0 个或多个字符)
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
/**
* 模糊查询:WildcardQuery
*/
@Test
public void testWildcardQuery() throws IOException {


SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华*");

sourceBulider.query(query);

searchRequest.source(sourceBulider);


SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

//转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}
  • prefix 查询:前缀查询
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
/**
* 模糊查询:perfixQuery
*/
@Test
public void testPrefixQuery() throws IOException {

SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

PrefixQueryBuilder query = QueryBuilders.prefixQuery("brandName", "华");

sourceBulider.query(query);

searchRequest.source(sourceBulider);


SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


SearchHits searchHits = searchResponse.getHits();
//获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

//转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

17、范围查询-脚本

range 范围查询:查找指定字段在指定范围内包含值

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
/**
* 1. 范围查询:rangeQuery
* 2. 排序
*/
@Test
public void testRangeQuery() throws IOException {

SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

// 范围查询
RangeQueryBuilder query = QueryBuilders.rangeQuery("price");

// 指定下限 gte大于等于
query.gte(2000);
// 指定上限 小于等于
query.lte(3000);

sourceBulider.query(query);

// 排序
sourceBulider.sort("price", SortOrder.DESC);

searchRequest.source(sourceBulider);

SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

// 转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

18、queryString 查询-脚本

queryString:

  • 会对查询条件进行分词。
  • 然后将分词后的查询条件和词条进行等值匹配
  • 默认取并集(OR)
  • 可以指定多个查询字段
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
/**
* queryString
*/
@Test
public void testQueryStringQuery() throws IOException {

SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

// queryString
QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机")
.field("title")
.field("categoryName")
.field("brandName")
.defaultOperator(Operator.AND);

sourceBulider.query(query);

searchRequest.source(sourceBulider);

SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

//转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

19、布尔查询

boolQuery:对多个查询条件连接。连接方式:

  • must(and):条件必须成立
  • must_not(not):条件必须不成立
  • should(or):条件可以成立
  • filter:条件必须成立,性能比 must 高。不会计算得分
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
/**
* 布尔查询:boolQuery
* 1. 查询品牌名称为:华为
* 2. 查询标题包含:手机
* 3. 查询价格在:2000-3000
*/
@Test
public void testBoolQuery() throws IOException {

SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

// 构建boolQuery
BoolQueryBuilder query = QueryBuilders.boolQuery();

// 构建各个查询条件
// 查询品牌名称为:华为
QueryBuilder termQuery = QueryBuilders.termQuery("brandName", "华为");
query.must(termQuery);

// 查询标题包含:手机
QueryBuilder matchQuery = QueryBuilders.matchQuery("title", "手机");
query.filter(matchQuery);

// 查询价格在:2000-3000
QueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
((RangeQueryBuilder) rangeQuery).gte(2000);
((RangeQueryBuilder) rangeQuery).lte(3000);
query.filter(rangeQuery);

// 使用boolQuery连接
sourceBulider.query(query);

searchRequest.source(sourceBulider);

SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

// 转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

20、聚合查询

  • 指标聚合:相当于 MySQL 的聚合函数。max、min、avg、sum 等
  • 桶聚合:相当于 MySQL 的 group by 操作。不要对 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
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
/**
* 聚合查询:桶聚合,分组查询
* 1. 查询title包含手机的数据
* 2. 查询品牌列表
*/
@Test
public void testAggQuery() throws IOException {

SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

// 查询title包含手机的数据
MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");

sourceBulider.query(query);

// 查询品牌列表
/*
参数:
1. 自定义的名称,将来用于获取数据
2. 分组的字段
*/
AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
sourceBulider.aggregation(agg);


searchRequest.source(sourceBulider);


SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:" + value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

// 转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}

// 获取聚合结果
Aggregations aggregations = searchResponse.getAggregations();

Map<String, Aggregation> aggregationMap = aggregations.asMap();


Terms goods_brands = (Terms) aggregationMap.get("goods_brands");

List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();

List brands = new ArrayList();
for (Terms.Bucket bucket : buckets) {
Object key = bucket.getKey();
brands.add(key);
}

for (Object brand : brands) {
System.out.println(brand);
}
}

21、高亮查询

高亮三要素:

  • 高亮字段
  • 前缀
  • 后缀
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
/**
*
* 高亮查询:
* 1. 设置高亮
* * 高亮字段
* * 前缀
* * 后缀
* 2. 将高亮了的字段数据,替换原有数据
*/
@Test
public void testHighLightQuery() throws IOException {


SearchRequest searchRequest = new SearchRequest("goods");

SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

// 查询title包含手机的数据
MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");

sourceBulider.query(query);

// 设置高亮
HighlightBuilder highlighter = new HighlightBuilder();
// 设置三要素
highlighter.field("title");
highlighter.preTags("<font color='red'>");
highlighter.postTags("</font>");

sourceBulider.highlighter(highlighter);

// 查询品牌列表
/*
参数:
1. 自定义的名称,将来用于获取数据
2. 分组的字段
*/
AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
sourceBulider.aggregation(agg);

searchRequest.source(sourceBulider);

SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

SearchHits searchHits = searchResponse.getHits();
// 获取记录数
long value = searchHits.getTotalHits().value;
System.out.println("总记录数:"+value);

List<Goods> goodsList = new ArrayList<>();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();

// 转为java
Goods goods = JSON.parseObject(sourceAsString, Goods.class);

// 获取高亮结果,替换goods中的title
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField HighlightField = highlightFields.get("title");
Text[] fragments = HighlightField.fragments();
//替换
goods.setTitle(fragments[0].toString());

goodsList.add(goods);
}

for (Goods goods : goodsList) {
System.out.println(goods);
}
}

22、重建索引

随着业务需求的变更,索引的结构可能发生改变。
ElasticSearch 的索引一旦创建,只允许添加字段,不允许改变字段。因为改变字段,需要重建倒排索引,影响内部缓
存结构,性能太低。
那么此时,就需要重建一个新的索引,并将原有索引的数据导入到新索引中。

原索引库 :student_index_v1

新索引库 :student_index_v2

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
# 新建student_index_v1索引,索引名称必须全部小写
PUT student_index_v1
{
"mappings": {
"properties": {
"birthday":{
"type": "date"
}
}
}
}
# 查询索引
GET student_index_v1
# 添加数据
PUT student_index_v1/_doc/1
{
"birthday":"2021-11-11"
}
# 查询数据
GET student_index_v1/_search
# 随着业务的变更,换种数据类型进行添加数据,程序会直接报错
PUT student_index_v1/_doc/1
{
"birthday":"2021年11月11号"
}
# 业务变更,需要改变birthday数据类型为text
# 1:创建新的索引 student_index_v2
# 2:将student_index_v1 数据拷贝到 student_index_v2

# 创建新的索引
PUT student_index_v2
{
"mappings": {
"properties": {
"birthday":{
"type": "text"
}
}
}
}

DELETE student_index_v2

# 2:将student_index_v1 数据拷贝到 student_index_v2
POST _reindex
{
"source": {
"index": "student_index_v1"
},
"dest": {
"index": "student_index_v2"
}
}

# 查询新索引库数据
GET student_index_v2/_search

# 在新的索引库里面添加数据
PUT student_index_v2/_doc/2
{
"birthday":"2021年11月13号"
}