Elasticsearch入门
# Elasticsearch 入门
# 官方文档
# 简介
Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。作为 Elastic Stck 的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。
The Elastic Stack,包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为ELK Stack)。能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。经常用来做日志收集、系统监控和状态分析等等。
- Elasticsearch:用于数据存储、计算和搜索
- Logstash/Beats:用于数据收集
- Kibana:用于数据可视化
Elaticsearch,简称为 ES,ES 是一个开源的高扩展的分布式全文搜索引擎,是整个 Elastic Sack 技术栈的核心。它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。
搜索引擎技术排名:
- Elasticsearch:开源的分布式搜索引擎
- Splunk:商业项目
- Solr:Apache 的开源搜索引擎
# 全文搜索引擎
Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日志的搜索等等。对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。
一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对 SQL 的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。
基于以上原因可以分析得出,在一些生产环境中,使用常规的搜索方式,性能是非常差的:
- 搜索的数据对象是大量的非结构化的文本数据。
- 文件记录量达到数十万或数百万个甚至更多。
- 支持大量基于交互式文本的查询。
- 需求非常灵活的全文搜索查询。
- 对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。
- 对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。
为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全 文搜索引擎
这里说到的全文搜索引擎指的是目前广泛应用的主流搜索引擎。
它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。
这个过程类似于通过字典中的检索字表查字的过程。
# 环境准备
# ES 安装(Linux)
通过下面的 Docker 命令即可安装单机版本的 elasticsearch:
- 安装完成后,访问 9200 端口,即可看到响应的 Elasticsearch 服务的基本信息
docker run -d \
--name es \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
--privileged \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1
2
3
4
5
6
7
8
9
10
# Kibana 安装
通过下面的 Docker 命令,即可部署 Kibana:
- 安装完成后,直接访问 5601 端口,即可看到控制台页面:
- 选择
Explore on my own
之后,进入主页面 - 然后选中
Dev tools
,进入开发工具页面
# 这里配置的 [服务器ip] 地址,可以改写成 es,前提是需要设置 --network 属性,将 es 和 kibana she'z
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://[服务器ip]:9200 \
-p 5601:5601 \
kibana:7.12.1
2
3
4
5
6
图解:
- 这里就没有必要再加上 es 的地址了,因为 kibana 已经知道 es 的地址了(在创建kibana容器时就知道了)。所以这里的地址就可以用
/
代替
# ES 安装(Windows)
Windows 版的 Elasticsearch 压缩包,解压即安装完毕,解压后的 Elasticsearch 的目录结构如下 :
- 解压后,进入
bin
文件目录,点击elasticsearch.bat
文件启动 ES 服务。
目录 | 含义 |
---|---|
bin | 可执行脚本目录 |
config | 配置目录 |
jdk | 内置 JDK 目录 |
lib | 类库 |
logs | 日志目录 |
modules | 模块目录 |
plugins | 插件目录 |
注意:
- 9300 端口为 Elasticsearch 集群间组件的通信端口,
- 9200 端口为浏览器访问的 http 协议 RESTful 端口。
打开浏览器,输入地址:http://localhost:9200 (opens new window),测试返回结果,返回结果如下:
- 正常有返回结果即可
{
"name": "CHENMENG",
"cluster_name": "elasticsearch",
"cluster_uuid": "kzJynYXKQge03U7WpEu8fg",
"version": {
"number": "7.8.0",
"build_flavor": "default",
"build_type": "zip",
"build_hash": "757314695644ea9a1dc2fecd26d1a43856725e65",
"build_date": "2020-06-14T19:35:50.234439Z",
"build_snapshot": false,
"lucene_version": "8.5.1",
"minimum_wire_compatibility_version": "6.8.0",
"minimum_index_compatibility_version": "6.0.0-beta1"
},
"tagline": "You Know, for Search"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 正排和倒排索引
elasticsearch 之所以有如此高性能的搜索表现,正是得益于底层的倒排索引技术。
# 正排索引(传统)
id(索引) | content |
---|---|
1001 | my name is zhang san |
1002 | my name is li si |
# 倒排索引
倒排索引中有两个非常重要的概念:
- 文档(
Document
):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息 - 词条(
Term
):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:我、是、中国人、中国、国人这样的几个词条
创建倒排索引是对正向索引的一种特殊处理和应用,流程如下:
- 将每一个文档的数据利用分词算法根据语义拆分,得到一个个词条
- 创建表,每行数据包括词条、词条所在文档id、位置等信息
- 因为词条唯一性,可以给词条创建正向索引
Term 词条(索引) | 文档 id |
---|---|
name | 1001,1002 |
zhang | 1001 |
# 概念小结
- 正排索引是最传统的,根据 id 索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程。
- 而倒排索引则相反,是先找到用户要搜索的词条,根据词条得到保护词条的文档的 id,然后根据 id 获取文档。是根据词条找文档的过程。
# 优缺点分析
正排索引
- 优点:
- 可以给多个字段创建索引
- 根据索引字段搜索、排序速度非常快
- 缺点:
- 根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描。
倒排索引
- 优点:
- 根据词条搜索、模糊搜索时,速度非常快
- 缺点:
- 只能给词条创建索引,而不是字段
- 无法根据字段做排序
# Elasticsearch vs MySQL
Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。
为了方便大家理解,我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比:
Elasticsearch | MySQL | 说明 |
---|---|---|
Cluster | 多个数据库实例 | 整个 Elasticsearch 集群(多个节点) |
索引 Index | ✅ 更像 Table | 文档的集合,一个索引相当于一个“表” |
Type(已废弃) | 表(Table) | 旧版本中的 Type 类似于 MySQL 中的表,但 7.x+ 已废弃 |
文档 Document | 行(Row) | 就是一条条的数据,类似数据库中的行(Row),文档都是 JSON 格式 |
字段 Field | 列(Column) | 每个文档中的属性,就是 JSON 文档中的字段,类似数据库中的列 |
映射 Mapping | 表结构(Schema) | 定义字段类型和结构,是索引中文档的约束,例如字段类型约束。类似数据库的表结构 |
DSL | SQL | DSL 是 ES 提供的 JSON 风格的请求语句,用来操作 ES,实现 CRUD |
Shard | 分片机制 | 类似分库分表中的分片概念 |
Replica | 主从复制 | 高可用副本机制 |
对比分析:
- Mysql:擅长事务类型操作,可以确保数据的安全和一致性
- Elasticsearch:擅长海量数据的搜索、分析、计算
因此,在企业中,往往是两者结合使用:
- 对安全性要求较高的写操作,使用 mysql 实现
- 对查询性能要求较高的搜索需求,使用 elasticsearch 实现
- 两者再基于某种方式,实现数据的同步,保证一致性
# DSL 语法
json 格式,好理解,和 http 请求最兼容,应用最广
官方文档:
# IK 分词器
Elasticsearch 的关键就是倒排索引,而倒排索引依赖于对文档内容的分词,而分词则需要高效、精准的分词算法,IK 分词器就是这样一个中文分词算法。
# 安装
1、在线安装
# 命令行在线安装(地址对应官网)
docker exec -it es ./bin/elasticsearch-plugin install https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip
# 重启 es 容器
docker restart es
2
3
4
5
2、离线安装
如果网速较差,也可以选择离线安装。
(1)首先,查看之前安装的 Elasticsearch 容器的 plugins 数据卷目录:
docker volume inspect es-plugins
(2)然后,需要把本地的 IK分词器文件夹 上传至这个目录。(官网下载压缩包,放到 Linux 环境下再解压,Windows 下解压的话会丢失文件)
(3)最后,重启 es 容器
# 使用
IK 分词器包含两种模式:
ik_smart
:智能语义切分ik_max_word
:最细粒度切分
我们在 Kibana 的 DevTools 上来测试分词器,首先测试 Elasticsearch 官方提供的标准分词器:
POST /_analyze
{
"analyzer": "standard",
"text": "这是分词器测试示例"
}
2
3
4
5
结果分析:
- 标准分词器智能 1 字 1 词条,无法正确对中文做分词。
{
"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" : 4,
"type" : "CN_WORD",
"position" : 2
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
我们再测试 IK 分词器:
POST /_analyze
{
"analyzer": "ik_smart",
"text": "这是分词器测试示例"
}
2
3
4
5
结果分析:
- 会根据中文语义智能分词
{
"tokens" : [
{
"token" : "这是",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "分词器",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "测试",
"start_offset" : 5,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "示例",
"start_offset" : 7,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 3
}
]
}
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
# 拓展词典
随着互联网的发展,“造词运动” 也越发的频繁。出现了很多新的词语,在原有的词汇列表中并不存在。比如:“泰裤辣”,“传智播客” 等。
- IK 分词器无法对这些词汇分词
- 所以要想正确分词,IK 分词器的词库也需要不断的更新,IK 分词器提供了扩展词汇的功能
添加自定义词典步骤如下:
- 打开 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">ext.dic</entry>
</properties>
2
3
4
5
6
7
- 在 IK 分词器的 config 目录新建一个
ext.dic
,可以参考 config 目录下复制一个配置文件进行修改:
传智播客
泰裤辣
2
- 重启 elasticsearch
docker restart es
# 查看 日志
docker logs -f elasticsearch
2
3
4
# 总结
分词器的作用是什么?
- 创建倒排索引时,对文档分词
- 用户搜索时,对输入的内容分词
IK 分词器有几种模式?
ik_smart
:智能切分,粗粒度ik_max_word
:最细切分,细粒度
IK 分词器如何拓展词条?如何停用词条?
- 利用 config 目录的
IkAnalyzer.cfg.xml
文件添加拓展词典和停用词典 - 在词典中添加拓展词条或者停用词条
# Mapping 映射属性
Mapping 是对索引库中文档的约束,常见的 Mapping 属性包括:
type
:字段数据类型,常见的简单类型有:- 字符串:
text
(可分词的文本)、keyword
(精确值,例如:品牌、国家、ip地址) - 数值:
long
、integer
、short
、byte
、double
、float
、 - 布尔:
boolean
- 日期:
date
- 对象:
object
- 字符串:
index
:是否创建索引,默认为true
analyzer
:使用哪种分词器properties
:该字段的子字段
例如下面的 json 文档:
{
"age": 21,
"weight": 52.1,
"isMarried": false,
"info": "沉梦听雨学 Elasticsearch",
"email": "zy@itcast.cn",
"score": [99.1, 99.5, 98.9],
"name": {
"firstName": "云",
"lastName": "赵"
}
}
2
3
4
5
6
7
8
9
10
11
12
对应的每个字段映射(Mapping):
字段名 | 字段类型 | 类型说明 | 是否参与搜索 | 是否参与分词 | 分词器 |
---|---|---|---|---|---|
age | integer | 整数 | ✅ | —— | |
weight | float | 浮点数 | ✅ | —— | |
isMarried | boolean | 布尔 | ✅ | —— | |
info | text | 字符串,但需要分词 | ✅ | ✅ | IK |
keyword | 字符串,但是不分词 | —— | |||
score | float | 只看数组中元素类型 | ✅ | —— | |
(name)firstName | keyword | 字符串,但是不分词 | ✅ | —— | |
(name)lastName | keyword | 字符串,但是不分词 | ✅ | —— |
# 索引库操作
- 创建索引库:
PUT /索引库名
- 查询索引库:
GET /索引库名
- 删除索引库:
DELETE /索引库名
- 修改索引库,添加字段:
PUT /索引库名/_mapping
# 文档操作
# CRUD
- 创建文档:
POST /{索引库名}/_doc/文档id { json文档 }
- 查询文档:
GET /{索引库名}/_doc/文档id
- 删除文档:
DELETE /{索引库名}/_doc/文档id
- 修改文档:
- 全量修改:
PUT /{索引库名}/_doc/文档id { json文档 }
- 局部修改:
POST /{索引库名}/_update/文档id { "doc": {字段}}
- 全量修改:
# 批处理
语法
批处理采用 POST
请求,基本语法如下:
POST _bulk
{"index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{"delete" : { "_index" : "test", "_id" : "2" } }
{"create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{"update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
2
3
4
5
6
7
8
操作类型分析
操作类型 | 幂等性 | 文档存在性处理 | 数据变更方式 | 典型场景 |
---|---|---|---|---|
index | 否 | 存在则覆盖,不存在则创建 | 全量替换 | 数据覆盖、不确定存在性 |
create | 否 | 仅当不存在时创建,存在则冲突 | 新增 | 强制唯一性写入 |
update | 否 | 必须存在(除非用 upsert ) | 部分更新/脚本修改 | 增量更新、条件逻辑更新 |
delete | 是 | 存在则删除,不存在也视为成功 | 删除 | 清理数据 |
常见误区纠正
index
不是单纯的“新增”:- 它是 “创建或全量替换”,与
create
的 “仅创建” 有本质区别。
- 它是 “创建或全量替换”,与
update
不支持新增字段?:update
的doc
中可以包含新字段,Elasticsearch 会自动合并到文档中。
- 批量操作中的 HTTP 方法:
_bulk
API 必须使用POST
请求,且请求体格式严格依赖换行符分隔 JSON。
- 操作顺序与原子性:
- 批量操作中的每个动作独立执行,无事务保证。部分操作失败不影响其他操作。
示例
批量新增:
POST /_bulk
{"index": {"_index":"heima", "_id": "3"}}
{"info": "黑马程序员C++讲师", "email": "ww@itcast.cn", "name":{"firstName": "五", "lastName":"王"}}
{"index": {"_index":"heima", "_id": "4"}}
{"info": "黑马程序员前端讲师", "email": "zhangsan@itcast.cn", "name":{"firstName": "三", "lastName":"张"}}
2
3
4
5
批量删除:
POST /_bulk
{"delete":{"_index":"heima", "_id": "3"}}
{"delete":{"_index":"heima", "_id": "4"}}
2
3
# RestAPI
- ES 官方提供了各种不同语言的客户端,用来操作 ES。
- 这些客户端的本质就是组装 DSL 语句,通过 http 请求发送给 ES。
- 官方文档地址:
# 索引
JavaRestClient
操作 elasticsearch 的流程基本类似。核心是 client.indices()
方法来获取索引库的操作对象。
索引库操作的基本步骤:
- 初始化
RestHighLevelClient
- 创建 XxxIndexRequest。
- XXX 是
Create
、Get
、Delete
- XXX 是
- 准备请求参数(
Create
时需要,其它是无参,可以省略) - 发送请求。调用
RestHighLevelClient#indices().xxx()
方法,xxx 是create
、exists
、delete
代码示例:
public class IndexTest {
String ES_URL = "http://192.168.207.129:9200";
private RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create(ES_URL)
));
System.out.println("setUp success");
}
@Test
void testConnect() {
System.out.println(client);
}
@Test
void testCreateIndex() throws IOException {
// 1.创建Request对象
CreateIndexRequest request = new CreateIndexRequest("user");
// 2.准备请求参数
request.source(MAPPING_TEMPLATE, XContentType.JSON);
// 3.发送请求
client.indices()
.create(request, RequestOptions.DEFAULT);
}
@Test
void testDeleteIndex() throws IOException {
// 1.创建Request对象
DeleteIndexRequest request = new DeleteIndexRequest("user");
// 2.发送请求
AcknowledgedResponse response = client.indices()
.delete(request, RequestOptions.DEFAULT);
System.out.println(response.isAcknowledged());
}
@Test
void testGetIndex() throws IOException {
// 1.创建Request对象
GetIndexRequest request = new GetIndexRequest("user");
// 2.发送请求
GetIndexResponse getIndexResponse = client.indices()
.get(request, RequestOptions.DEFAULT);
// 3.输出
System.err.println(getIndexResponse.getAliases());
System.err.println(getIndexResponse.getMappings());
System.err.println(getIndexResponse.getSettings());
}
@Test
void testExistsIndex() throws IOException {
// 1.创建Request对象
GetIndexRequest request = new GetIndexRequest("user");
// 2.发送请求
boolean exists = client.indices()
.exists(request, RequestOptions.DEFAULT);
// 3.输出
System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}
static final String MAPPING_TEMPLATE = "{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\": {\n" +
" \"type\": \"long\"\n" +
" },\n" +
" \"user_account\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"user_password\": {\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"user_name\": {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"fields\": {\n" +
" \"keyword\": {\n" +
" \"type\": \"keyword\",\n" +
" \"ignore_above\": 256\n" +
" }\n" +
" }\n" +
" },\n" +
" \"user_avatar\": {\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"gender\": {\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"phone\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"email\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"user_profile\": {\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" },\n" +
" \"user_role\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"create_user\": {\n" +
" \"type\": \"long\"\n" +
" },\n" +
" \"create_time\": {\n" +
" \"type\": \"date\",\n" +
" \"format\": \"yyyy-MM-dd HH:mm:ss||epoch_millis\"\n" +
" },\n" +
" \"update_user\": {\n" +
" \"type\": \"long\"\n" +
" },\n" +
" \"update_time\": {\n" +
" \"type\": \"date\",\n" +
" \"format\": \"yyyy-MM-dd HH:mm:ss||epoch_millis\"\n" +
" },\n" +
" \"is_deleted\": {\n" +
" \"type\": \"byte\"\n" +
" },\n" +
" \"tenant_id\": {\n" +
" \"type\": \"long\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
@AfterEach
void tearDown() throws IOException {
this.client.close();
System.out.println("tearDown success");
}
}
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
129
130
131
132
133
134
135
136
137
映射属性解释:
用户名字段的多字段(multi-field)配置
- 主字段配置:
"type": "text"
:表示这是一个全文检索字段,会被分词处理"analyzer": "ik_max_word"
:指定使用IK分词器的ik_max_word
模式,这种模式会做最细粒度的拆分(适合中文搜索)
- 子字段配置:
fields
属性定义了多字段映射,允许同一个字段以不同方式索引- keyword 子字段:
"type": "keyword"
:表示这是一个关键字字段,不会被分词,用于精确匹配"ignore_above": 256
:超过256个字符的字段值不会被索引或存储
使用场景:
- 搜索"张三"时,使用主字段
user_name
(支持分词搜索) - 精确匹配或聚合时,使用
user_name.keyword
字段 - 例如:
GET /user/_search { "query": { "term": { "user_name.keyword": "张三" } } }
日期字段的格式配置
"format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
- 日期格式定义:
yyyy-MM-dd HH:mm:ss
:表示接受 "2023-01-01 12:00:00" 这样的字符串格式epoch_millis
:表示接受时间戳格式(从1970-01-01开始的毫秒数)
- 双竖线
||
:- 表示"或"的关系,即字段可以接受多种格式的日期值
- 这是一个格式列表分隔符,Elasticsearch 会尝试从左到右依次匹配
使用场景:
- 可以插入格式化的日期字符串:
"2023-01-01 12:00:00"
- 也可以插入时间戳:
1672560000000
- Elasticsearch 内部会统一存储为 UTC 时间,但查询时可以按原始格式检索
# 文档
文档操作的基本步骤:
- 初始化
RestHighLevelClient
- 创建 XxxRequest。
- XXX 是
Index
、Get
、Update
、Delete
、Bulk
- XXX 是
- 准备参数(
Index
、Update
、Bulk
时需要) - 发送请求。
- 调用
RestHighLevelClient#.xxx()
方法,xxx是index
、get
、update
、delete
、bulk
- 调用
- 解析结果(
Get
时需要)
public class DocumentTest {
String ES_URL = "http://192.168.207.129:9200";
private RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create(ES_URL)
));
System.out.println("setUp success");
}
@Test
void testInsertDocument() throws IOException {
// 1.准备Request对象
IndexRequest request = new IndexRequest();
request.index("user").id("1001");
// 2.准备Json文档,向ES插入数据,必须将数据转换位JSON格式
UserDoc userDoc = new UserDoc();
userDoc.setUserName("zhangsan");
userDoc.setGender(1);
request.source(JSONUtil.toJsonStr(userDoc), XContentType.JSON);
// 3.发送请求
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
System.out.println("res=" + response.getResult());
}
@Test
void testGetDocumentById() throws IOException {
// 1.准备Request对象
GetRequest request = new GetRequest("user", "1001");
// 2.发送请求
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.获取响应结果中的source
String json = response.getSourceAsString();
UserDoc userDoc = JSONUtil.toBean(json, UserDoc.class);
System.out.println("userDoc= " + userDoc);
}
@Test
void testDeleteDocument() throws IOException {
// 1.准备Request,两个参数,第一个是索引库名,第二个是文档id
DeleteRequest request = new DeleteRequest("user", "1001");
// 2.发送请求
client.delete(request, RequestOptions.DEFAULT);
}
@Test
void testUpdateDocument() throws IOException {
// 1.准备Request
UpdateRequest request = new UpdateRequest("user", "1001");
// 2.准备请求参数
request.doc(
"user_name", "王五",
"gender", 0
);
// 3.发送请求
UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
System.out.println("updateResponse.getResult() = " + updateResponse.getResult());
}
@Test
void testBulk() throws IOException {
ArrayList<UserDoc> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
UserDoc userDoc = new UserDoc();
userDoc.setUserName("zhangsan_" + i);
userDoc.setGender(1);
list.add(userDoc);
}
// 1.创建Request
BulkRequest request = new BulkRequest();
// 2.准备请求参数
for (int i = 0; i < list.size(); i++) {
UserDoc userDoc = list.get(i);
request.add(new IndexRequest("user")
.id("" + (i + 1))
.source(JSONUtil.toJsonStr(userDoc), XContentType.JSON));
}
// 3.发送请求
BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
System.out.println("bulkResponse.getTook() = " + bulkResponse.getTook());
System.out.println(Arrays.toString(bulkResponse.getItems()));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
System.out.println("tearDown success");
}
}
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
# 学习参考
- 视频地址:【尚硅谷】ElasticSearch教程入门到精通(基于ELK技术栈elasticsearch 7.x+8.x新特性)_bilibili (opens new window)
- 笔记地址:Elasticsearch学习笔记-CSDN博客 (opens new window)
- Elasticsearch 基础入门详文 (opens new window)
- Elasticsearch 保姆级入门篇 (opens new window)
- ElasticSearch在项目中具体怎么用? - 知乎 (opens new window)
- https://t.zsxq.com/4K41D (opens new window)