全文检索

只处理文本,不处理语义
搜索时英文不区分大小写
结果列表有相关度排序

ElasticSearch

ES 是基于 Apache Lucene 构建的开源搜索引擎,采用 Java 语言编写,提供了 Restful API 简化操作。

ES 默认使用的是标准分词器(StandardAnalyzer):中文使用单字分词;英文使用单词分词。
ES 中只有 text 类型是分词的,剩下的 keyword、integer、date 等类型都是不分词的。

版本异同

es5 一个索引可以创建多个类型,在es6中仍可以使用,但是已经不推荐。
es6 之后一个索引只对应一个类型。
es7 的默认分片(即备份)从之前的5调到了1,如果想用原来的可以自己设置;
es7 使用 jdk11+,已经内置。不过可能会出现使用本地的jdk的情况,如果本地jdk版本低于11,需要配置。

编辑环境变量:vim /etc/profile,添加变量 export ES_JAVA_HOME=指定ES安装目录中的jdk,然后 source /etc/profile
类型已经在新版本中删除,es7 之后的版本不在有类型。

安装配置

注意,kibana 必须和 ES 同版本。这里以安装的 7.14 版本为例,安装方式都是解压 .tar.gz 包的方式,docker 方式安装比较简单。ES 默认 web 端口:9200;tcp端口:9300(集群通信);kibana默认端口:5601。启动 Kibana 之前确保 ES 服务正常启动。

1. ES

ES 不能以 root 用户启动,否则会报运行时异常,请使用普通用户启动 ES 服务;首先关闭防火墙。之后修改安装目录下的 config 文件夹内的 elasticsearch.yml 文件,修改以下内容:

1
2
3
4
# 开启远程连接
network.host: 0.0.0.0
# 使用一个节点初始化集群
cluster.initial_master_nodes: ["node-1"]

设置完成以后可以启动服务;进入安装的 bin 目录,两个命令二选一,如果是直接启动的,操作时可以再打开一个会话窗口。

1
2
3
4
# 直接启动
./elasticsearch
# 后台启动 以守护进程启动
./elasticsearch -d

插曲:ES启动可能出现的问题

另外,一般在启动会出现默认配置较小的错误,我们需要更改系统文件。启动失败时 es 服务会在终端给出建议的大小,我们去修改即可:

  1. 以 root 用户修改文件 /etc/security/limits.conf ,在最后加入以下内容(CentOS系统需要。其它的系统可跳过)

    1
    2
    3
    4
    *       soft	nofile      65536
    * hard nofile 65536
    * soft nproc 4096
    * hard nproc 4096
  2. 编辑文件 sudo vim /etc/sysctl.conf,如果没有该文件可以手动创建,加入内容:vm.max_map_count=262144。然后使用 sysctl -p 使其生效并查看输出内容是否和自己设置的一样。

2. Kibana

同样是修改 kibana 安装目录下的 config/kibana.yml 文件,修改以下内容:

1
2
3
4
# 开启远程连接
server.host: 0.0.0.0
# 监控 ES 的地址信息
elasticsearch.hosts: [es的ip地址:端口号]

3. podman/docker

1
2
3
4
5
6
7
8
podman run -d \
--name es7 \
-p 9200:9200 \
-p 9300:9300 \
-v /home/server/podman/es7/plugins:/usr/share/elasticsearch/plugins \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
docker.elastic.co/elasticsearch/elasticsearch:7.17.27

docker 默认就开启了远程连接的权限。
官方文档指导地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html

ES基本使用

1. 核心概念

  1. 索引(Index):一个索引就是一个拥有几分相似特征的文档的集合,ES索引的名称必须是小写
  2. 映射(Mapping):映射是定义一个文档和它所包含的字段如何被存储和索引的过程,原因是 mapping 中主要包括字段名、字段类型等信息。
  3. 文档(Document):文档是索引中存储的一条条数据。一条文档是一个可被索引的最小单元,采用轻量级的 JSON 格式数据来表示。

2. 索引操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 查看es中的索引
GET /_cat/indices?v
// 创建索引,指定不创建副本分片,health 为 green,默认是 yellow
PUT /index_name
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
}
}

// 删除索引
DELETE /index_name

// 谨慎操作,删除掉会导致服务无法正常访问,需要重启服务
DELETE /*
  • 索引没有修改操作。
  • 查看索引有 health 属性,包含三种颜色:yellow 代表可用,但是有危险(默认创建的索引健康值);green 代表健康,可用;red 代表索引不可用。

3. 映射操作

一般都是在创建索引的时候创建映射,脱离了索引,映射也就没有意义了。创建映射时不需要指定类型的长度。

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
// 查看映射信息
GET /index_name/_mapping

// 创建映射,一般在创建索引时手动创建映射
PUT /index_name
{
// 指定分片信息,可以不写,使用默认
"settings":{
// 分片为1
"number_of_shards": 1,
// 副本为0
"number_of_replicas": 0
},
"mappings":{
// 默认字段,映射属性信息必须写在这里面
"properties":{
// 属性名称及其类型
"id": {
"type": "integer"
},
"title": {
"type": "keyword"
},
"price": {
"type": "double"
},
"created_at": {
"type": "date"
},
"desc": {
"type": "text"
},
}
},
}

映射信息不能删除和修改。只能删除索引重新创建。

es 的类型:

字符串类型:keyword(不分词)、text(分词)
数字:integer、long
小数:float、double
布尔:boolean
日期:date

4. 文档操作

文档操作,插入一条文档 put /索引/类型/id(指定Id用put,让系统自动创建用post)

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
// 添加文档 手动指定 id
POST /index_name/_doc/1
{
"id": 1,
"title": "风中捉刀",
"price": 0.5,
"created_at": "2021-12-12",
"description": "天元轮魁"
}

// 添加文档 自动创建文档id:取UUID的一部分
POST /index_name/_doc/
{
"title": "无情葬月",
"price": 0.5,
"created_at": "2021-10-12",
"description": "血不染"
}

// 文档查询 基于 id 查询
GET /index_name/_doc/document_id

// 删除文档 基于 id 删除
DELETE /index_name/_doc/document_id

// 更新文档 先删除再添加,不保留其它数据
PUT /index_name/_doc/document_id
{
"title": "修改值"
}

// 更新文档 保留原始内容,先查询再修改
// es8 简化了该命令
POST /index_name/_doc/document_id/_update
{
// doc为默认字段,必填
"doc": {
"title": "target_value"
}
}

es 文档批量操作:_bulk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /index_name/_doc/_bulk
{
// 手动添加id
"index": {"_id": 2}
}
{"id": 2, "title": "风中捉刀", "price": 0.5, "created_at": "2021-12-12", "description": "天元轮魁"}
{"index": {"_id": 3}}
{"id": 3, "title": "玲珑雪霏", "price": 0.5, "created_at": "2021-12-12", "description": "天元轮魁"}

// 文档批量操作 添加(index) 更新(update) 删除(delete)
POST /index_name/_doc/_bulk
{"index": {"_id": 4}}
{"id": 4,"title": "风中捉刀","price": 0.5,"created_at":"2021-12-12","description": "天元轮魁"}
{"update": {"_id": 3}}
{"doc": {"title": "荻花题叶"}}
{"delete": {"_id": 2}}

es 的批量操作不允许 json 结构体被格式化,只能放在同一行。使用 kibana 可以快速格式化。
es 7 必须使用 _doc,es8 发生了改变。

ES高级查询

ES 8 之后不需要添加 _doc 前缀,加了报错,而 ES7 中可加可不加(不加时在Kibana中会有语法提示)。推荐不要添加。

1. 查询所有

1
2
3
4
5
6
7
// 高版本
GET /index_name/_search
{
"query": {
"match_all": {}
}
}

1. 关键字查询<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
40
GET /index_name/_search
{
"query": {
"term": {
// 根据哪个字段进行查询
"filed_name": {
// 字段包含的值
"value": ""
}
}
}
}

// 关键词查询 term
// keyword 类型需要输入全部内容搜索
// text 类型需要输入单个词/字搜索
GET /index_name/_search
{
"query":{
"term":{
// 搜索 description 关键词
"description":{
// 关键词包含的值
"value":"天元"
}
}
}
}

// 查询 keyword 类型,全部输入
GET /index_name/_search
{
"query":{
"term":{
"title":{
"value":"风中捉刀"
}
}
}
}

由于ES默认使用的是标准分词器:英文单词分词,中文单字分词。所以 description 只能输入单字搜索。
ES 中除了 text 类型之外的其它类型都不分词。不分词的字段必须输入全部内容才能精准查询。

2. 范围查询<range>

1
2
3
4
5
6
7
8
9
10
11
12
GET /index_name/_search
{
"query":{
"range":{
// 根据price查询范围在 0=<price<=5 的数据
"price": {
"gte":0,
"lte":5
}
}
}
}

3. 前缀查询<prefix>

1
2
3
4
5
6
7
8
9
10
GET /index_name/_search
{
"query":{
"prefix":{
"title": {
"value":""
}
}
}
}

4. 通配符查询<wildcard>

?:匹配一个
*:匹配多个

1
2
3
4
5
6
7
8
9
10
GET /index_name/_search
{
"query":{
"wildcard":{
"description": {
"value":"go*"
}
}
}
}

5. 多id查询<ids>

1
2
3
4
5
6
7
8
9
GET /index_name/_search
{
"query":{
"ids":{
// 多个文档id
"values": [1, 2, 4]
}
}
}

6. 模糊查询<fuzzy>

1
2
3
4
5
6
7
8
9
10
11
GET /index_name/_search
{
"query":{
"fuzzy":{
// 也可以搜索到 风中捉刀
"description": "风中捉枪"
// 搜索不到 风中捉刀
// "description": "风中捉西瓜"
}
}
}

最多允许 0-2 次模糊错误(至多模糊匹配 0 ~ 2 个字符)
搜索关键词小于等于 2,不允许模糊。
搜索关键词长度为 3-5,只允许一次模糊。
搜索关键词长度大于 5,最多允许2次模糊。

7. 布尔查询<bool>

包含三种选项:must、should、must_not

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
GET /index_name/_search
{
"query":{
"bool":{
// 以下条件必须都满足才能查询到数据
"must": {
"ids": {
"values":[1]
},
{
"term":{
"title":{
"value": "风中捉刀"
}
}
}
}
}
}
}

must 必须全部满足才能查询,must_not 都不满足才能查询,should 满足一个就行。(与、非、或)

8. 多字段查询<multi_match>

该方式可以根据字段类型进行自动分词,将查询条件分词之后进行查询;如果不分词,就将查询条件作为整体进行查询。

1
2
3
4
5
6
7
8
9
10
11
GET /index_name/_search
{
"query":{
"multi_match":{
// 查询的关键词或文本
"query": "风中捉刀",
// 哪些字段中的文本
"fields": ["title", "description"]
}
}
}

9. 默认字段分词查询<query_string>

根据查询字段的类型进行分词或不分词查询。

1
2
3
4
5
6
7
8
9
10
11
GET /index_name/_search
{
"query":{
"query_string":{
// 要查询的字段
"default_field": "description",
// 该字段的值
"query": "可叹,落叶飘零"
}
}
}

10. 高亮查询<highlight>

让符合条件的文档高亮显示。ES7 可高亮的内容必须要支持分词。

默认情况下查询出来的文档的关键词,ES 会对其加入<em>标签标记,具体高亮样式可由我们自己定义。高亮并没有直接修改原始文档,而是放到了另一个标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GET /index_name/_search
{
"query":{
"query_string":{
"default_field": "description",
"query": "可叹,落叶飘零"
}
},

"highlight": {
"fields": {"*":{}},
// 自定义样式
"pre_tags":["<span style='color:red;'>"],
"post_tags":["</span>"],
"require_field_match": "false"
}
}

11. 返回指定条数<size>

ES查询数据,默认只显示前10条内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET /index_name/_search
{
"query":{
"query_string":{
"default_field": "description",
"query": "可叹,落叶飘零"
}
},
"highlight": {
"fields": {"*":{}},
// 自定义样式
"pre_tags":["<span style='color:red;'>"],
"post_tags":["</span>"],
"require_field_match": "false"
},
// 指定返回条数
"size":3
}

12. 分页查询<from>

from 指定起始的返回值,默认从 0 开始。

1
2
3
4
5
6
7
8
GET /index_name/_search
{
"query":{
"match_all":{}
},
"from": 2,
"size": 3
}

13. 排序<sort>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /index_name/_search
{
"query":{
"match_all":{}
},
"sort":[
{
// 排序的字段
"price":{
// order 指定升序还是降序
"order":"desc"
}
}
]
}

排序会干预 ES 的内部的规则。

14. 返回指定字段<_source>

1
2
3
4
5
6
7
8
GET /index_name/_search
{
"query":{
"match_all":{}
},
// 只获取需要的字段
"_source":["id", "title", "description"]
}

分词器

1. ES 内置分词器

Standard Analyzer:默认分词器,英文按单词拆分,统一小写处理。
Simple Analyzer:按照单词切分,过滤符号,中文按照空格分词,统一小写处理。
Stop Analyzer:统一小写处理,停用词过滤。
Whitespace Analyzer:按照空格切分,不转小写。
Keyword Analyzer:不分词,直接将输入当作输出。

可以在创建索引的时候为映射字段指定分词器,默认的就是标准分词器(standard):

1
2
3
4
5
6
7
8
9
PUT /index_name
{
"mapping":{
"properties":{
"title":"text",
"analyzer": "standard|simple|stop|whitespace|keyword"
}
}
}

2. 中文分词器

ES支持的中文分词器有IK、smartCN等,推荐的是 IK分词器,官方推荐去这个 网站 下载。

IK分词器类型:ik_smart_word(组粒度拆分)、ik_max_word(细粒度拆分)

注意: IK 分词器的版本要和安装的 ES 版本一致。如果是容器安装,docker/podman 容器内插件所在的目录是 /usr/share/elasticsearch/plugins

3. 扩展词、停用词配置

定义扩展词典和停用词典可以修改IK分词器中 config 目录中 IKAnalyzer.cfg.xml 文件。

1
2
3
4
5
6
7
<properties>
<comment>扩展词配置</comment>
<!-- 扩展词典 -->
<entry key="ext_dict">ext_dict.dic</entry>
<!-- 停用词典 -->
<entry key="ext_stopwords">ext_stopwords.dic</entry>
</properties>

在ik分词器目录下config目录中创建 ext_dict.dicext_stopwords.dic 文件,文件编码必须为 UTF-8,添加扩展词即可。

4. 过滤查询

在ES中,可以使用过滤查询获得更快的查询速度。使用过滤查询,就需要前面使用的布尔查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GET /index_name/_search
{
"query":{
"bool":{
"must":[
"term":{
"description":{
"value":"xxx"
}
}
],
"filter":[
{
// 支持 term terms range exists ids 过滤
"term":{
"description":""
}
}
]
}
}
}

会先执行过滤查询,再执行目标查询。一般用于大数据量的查询。
过滤查询是ES中一种重要的优化手段。

SpringBoot整合ES开发(简单基本可以略过)

2025.3.3

更新

  1. 导入es依赖

    pom.xml
    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
  2. 配置文件

    1
    2
    3
    4
    5
    6
    spring:
    elasticsearch:
    rest:
    uris: 192.168.1.36:9200
    connection-timeout: 1s
    read-timeout: 30s
  3. 进行客户端操作

    客户端对象有两个,如果进行了上面的配置,那么都会在Spring工厂中创建:

    • ElasticsearchOperations
    • RestHighLevelClient(6.x 版本推荐使用)

1. ElasticsearchOperations

特点:始终使用面向对象方式操作ES

  • 索引:用来存放相似文档集合
  • 映射:决定文档的每个字段以什么方式录入到ES中 字段类型 分词器
  • 文档:可以被索引的最小单元 json 数据格式

相关注解

注解名称 说明
@Document(indexName = “index_name”, createIndex = true) 用于类,指定索引名,是否创建索引
@Id 将放入对象 id 值作为文档 _id 进行映射
@Field(type = FieldType.Keyword) 指定字段类型

2. RestHighLevelClient

使用 RestHighLevelClient 非常简单,会使用 Kibana 就可以,它支持使用原生的编写 ES 的语句,只要会。可能这也是大家喜欢使用的原因之一吧。

1
2
3
4
5
6
SearchRequest search = new SearchRequest(document);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(条件);
search.source(sourceBuilder);

SearchResponse searchResponse = restHignLevelClient.search(search, RequestOptions.DEFAULT);

RestHighLevelClient 在 7.15 版本后被 标记弃用,推荐使用新的 Java API Client

Java 操作 ES

引入依赖,目前官方推荐使用 Java client。该客户端可以和 HLRC 的 7.17 版本共存(一起使用),所以对于 es 是 7.17+ 的版本来说选择新的 Java client 是不二之选。

pom.xml
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
<!-- 新版 es 客户端 适用于 es 7.17+(推荐) -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>${es.version}</version>
</dependency>

<!-- 旧版 es 客户端(7.15 后标记为废弃)适用于 es 5.6 ~ 7.16 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${es.version}</version>
</dependency>

<!-- 如果用的 spring boot web,已经包含了这个 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>

<!-- https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/7.17/installation.html -->
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>

参考链接:

ES 集群搭建

准备三个节点

cluster-|node-1-|config/elasticsearch.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 集群名称,3个节点必须相同
cluster.name: es-cluster
# 指定节点名称
node.name:
# 开放远程连接
network.host: 0.0.0.0
# 指定使用发布地址进行集群间通信
network.publish_host: 192.168.124.3
# 指定 web 端口
http.port: 9201
# 指定 tcp 端口
transport.tcp.port: 9301
# 指定所有节点的 TCP 通信
discovery.seed_hosts: ["192.168.10.1:9301", "192.168.10.1:9302", "192.168.10.1:9303"]
# 指定可以初始化集群的节点名称
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
# 集群最少几个节点可用
gateway.recover_after_nodes: 2
# 解决跨域问题
http.cors.enabled: true
http.cors.allow-origin: "*"

其它节点配置类似,只要修改对应的节点名称、IP以及对应的端口即可。

查看集群状态:http://任意集群节点ip:端口/_cat/health?v

Head插件查看ES状态

我们可以使用 Github 上的大佬开发的查看 ES 状态信息的插件监控服务,该插件基于 js 编写,需要 Node 环境。

1
2
3
4
5
6
7
8
git clone https://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head

# 安装依赖
npm install
# 启动
npm run start
open http://localhost:9100/

本站由 江湖浪子 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。