Elasticsearch全文搜索引擎

安装

安装方法参见 SpringBoot + ELK 快速搭建日志分析系统

基本概念

什么是Elasticsearch

Elasticsearch(简称Elastic或ES)是一个开源的分布式、RESTful风格的搜索和数据分析引擎。
采用Java编写,基于Lucene搜索引擎库做了一层封装,提供了简单方便的存储和检索接口。
具有以下特点:

  • 一个分布式的实时文档存储,每个字段可以被索引与搜索;
  • 一个分布式实时分析搜索引擎;
  • 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据。

名词解释

Node & Cluster

Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。

单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。

Inverted Index

Inverted Index(反向索引)是相对于正向索引来说的。

正向索引是从文档角度来找其中的单词,表示每个文档(用文档ID标识)都含有哪些单词,以及每个单词出现了多少次(词频)及其出现位置(相对于文档首部的偏移量)。所以每次搜索都是遍历所有文章。

反向索引是从单词角度找文档,标识每个单词分别在那些文档中出现(文档ID),以及在各自的文档中每个单词分别出现了多少次(词频)及其出现位置(相对于该文档首部的偏移量)。

Index

Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。

所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。它是单个数据库的同义词。每个 Index (即数据库)的名字必须是小写。

Document

Index 里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。

Document 使用 JSON 格式表示,同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

Type

Document 可以分组,比如weather这个 Index 里面,可以按城市分组(北京和上海),也可以按气候分组(晴天和雨天)。这种分组就叫做 Type,它是虚拟的逻辑分组,用来过滤 Document。

不同的 Type 应该有相似的结构(schema),举例来说,id字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如products和logs)应该存成两个 Index,而不是一个 Index 里面的两个 Type(虽然可以做到)。

6.0 版本默认只支持一个索引一个 type,7.0 版本新增了一个参数 include_type_name,让所有的 API 是 type 相关的,这个参数在 7.0 默认是 true,不过在 8.0 的时候,会默认改成 false,也就是不包含 type 信息了,这个是 type 用于移除的一个开关。

接口说明

管理 Index

1
2
3
4
5
6
7
8
# 新建 'weather' index
$ curl -X PUT 'localhost:9200/weather'

# 查看当前节点的所有 index
$ curl -X GET 'http://localhost:9200/_cat/indices?v'

# 删除 'weather' index
$ curl -X DELETE 'localhost:9200/weather'

管理 Type

1
2
# 列出每个 Index 所包含的 Type。
$ curl 'localhost:9200/_mapping'

定义字段类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
curl --location --request PUT 'http://192.168.0.40:9200/weather' \
--header 'Content-Type: application/json' \
--data-raw '{
"mappings": {
"properties": {
"city": {
"type": "text"
},
"weather": {
"type": "text"
},
"date": {
"type": "date"
}
}
}
}'

数据操作

新增记录

PUT方法
1
2
3
4
5
6
7
8
9
# url中的 '_doc' 是 type 名
# url中的 '1' 是文档 ID,put 方法需要指定 ID
curl --location --request PUT 'http://192.168.0.40:9200/weather/_doc/1' \
--header 'Content-Type: application/json' \
--data-raw '{
"city": "New York",
"weather": "Fine",
"date": "2020-09-01"
}'
POST方法
1
2
3
4
5
6
7
8
# post方法可以省略ID,将生成随机字符串
curl --location --request POST 'http://192.168.0.40:9200/weather/_doc' \
--header 'Content-Type: application/json' \
--data-raw '{
"city": "New York",
"weather": "Rain",
"date": "2020-09-02"
}'
  • 注意:如果没有预先创建index,在新增记录时会直接新增index,所以要注意不要写错index了。

查看记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# get请求
$ curl 'localhost:9200/weather/_doc/1

# 返回值
{
_index: "weather",
_type: "_doc",
_id: "1",
_version: 1,
_seq_no: 0,
_primary_term: 1,
found: true, # 如果找不到记录,found 就是 false
_source: { # 文档原始记录
city: "New York",
weather: "Fine",
date: "2020-09-01"
}
}

更新记录

1
2
3
4
5
6
7
8
# 更新记录就是再次 put 或 post,更新后 _version 字段会自增长
curl --location --request PUT 'http://192.168.0.40:9200/weather/_doc/1' \
--header 'Content-Type: application/json' \
--data-raw '{
"city": "New York",
"weather": "Rain",
"date": "2020-09-01"
}'
  • 注意:更新操作是文档全覆盖,不是动态更新字段

删除记录

1
$ curl -X DELETE 'localhost:9200/weather/_doc/1'

数据查询

查询所有记录

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
# url 中的 '_doc' 可以省略
curl 'http://192.168.0.40:9200/weather/_doc/_search'

# 返回值
{
"took": 3, # 查询耗时(毫秒)
"timed_out": false, # 查询超时
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2, # 查询到的总数
"relation": "eq"
},
"max_score": 1.0, # 最高匹配度
"hits": [ # 记录数组
{
"_index": "weather",
"_type": "_doc",
"_id": "2",
"_score": 1.0, # 匹配度,数组默认降序排列
"_source": {
"city": "New York",
"weather": "Rain",
"date": "2020-09-02"
}
},
{
"_index": "weather",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"city": "New York",
"weather": "Fine",
"date": "2020-09-01"
}
}
]
}
}

全文搜索

1
2
3
4
5
6
7
8
9
10
11
12
# 带 json 数据体的 get 请求
curl --location --request GET 'http://192.168.0.40:9200/weather/_doc/_search' \
--header 'Content-Type: application/json' \
--data-raw '{
"query": {
"match": {
"city": "New York" # 查询 city 包含 'New' 或 'York' 的记录
}
},
"from": 2 # 偏移量,从0开始
"size": 5 # 默认一次返回10条数据
}'
  • 示例中的 ‘New York’ 其实是两个搜索关键字:’New’ 和 ‘York’,逻辑上是 or 的关系。
  • 如果要搜索 and 关系的多个关键字,应该写成:
{
  "query": {
    "bool": {
      "must": [
        { "match": { "city": "New" } },
        { "match": { "city": "York" } }
      ]
    }
  }
}