本文中使用的 ES 为 7.x 版本。
Elasticsearch(以下简称 ES)是一个开源的高扩展的分布式全文搜索引擎,它可以近乎实时的存储、查询数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。
ES 的底层是开源库 Apache Lucene,由于Lucene 库的原理十分复杂,还需要我们自己写代码实现接口,所以 ES 就使用 RESTful 风格的 API 封装了 Lucene,使其更加简单易用。
导入 ES 的 PGP 密钥:
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
上安装 apt-transport-https 包:
sudo apt-get install apt-transport-https
添加存储库:
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list
安装 ES:
sudo apt-get update && sudo apt-get install elasticsearch
启动服务:
sudo systemctl start elasticsearch.service
测试安装:
curl -X GET "localhost:9200/?pretty"
出现主机、版本等信息,就说明安装成功。
设置开机自启动(可选):
sudo systemctl enable elasticsearch.service
到官网下载压缩包:传送门,这里以 windows 系统为例。
解压到合适的目录。
修改 config/kibana.yml 文件,找到 elasticsearch.hosts,去掉注释,改为 ES 的链接地址:
elasticsearch.hosts: ["http://localhost:9200"]
运行 bin/kibana.bat(windows下)。
用浏览器打开 http://localhost:5601。
主要是使用 dev tools,其他的看着玩玩。
集群(Cluster):
ES 可以运行在许多互相合作的服务器上,以实现容错和高可用性,这些服务器的集合称为集群。
节点(Node):
集群中的每一个服务器称为一个节点。
分片(Shard):
所有数据存储的最小单元块。
副本(Replia):
副本是一个分片的精确复制,可以提高查询吞吐量或实现高可用性。每个分片可以有零个或多个副本。
全文查询(Full-text Search):
全文索引就是把内容根据词的意义进行分词,然后分别创建索引,例如”今日是周日我们出去玩” 可能会被分词成:“今天“,”周日“,“我们“,”出去玩“ 等token,这样当你搜索“周日” 或者 “出去玩” 都会把这句搜出来。
ES 本质上是一个分布式数据库,所以,其中的很多概念在关系型数据库中都能找到与之相类似的概念。
ES | MySQL |
---|---|
Index 索引 | Database 数据库 |
Type 类型(7.x 版本后被移除!) | Table 表 |
Document 文档 | Row 记录或行 |
Field 字段 | Column 字段或列 |
Mapping 映射 | Schema 结构 |
Everything is indexed | Index 索引 |
Query DSL | SQL |
GET http://…… | SELECT * FROM table …… |
PUT http://…… | UPDATE table SET …… |
补充说明:
ES 会索引所有字段,经过处理后写入一个反向索引(Inverted Index,也叫倒排索引)。查找数据的时候,直接查找该索引。所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。
同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。
类型可以看作是对同一索引下文档的分组(7.x 版本已经移除这一概念)。
在创建索引的时候,可以预先定义字段的数据类型及相关属性(静态映射),ES 也可以自己推断出数据类型(动态映射,不建议)。映射就是对字段数据类型的定义。
所有的操作都是通过 http 请求完成,而不是使用特定客户端的命令。
反向索引(Inverted Index)也叫倒排索引,与之对应的是正向索引(Forward Index)。简单来说,正向索引是通过 key 找 value,反向索引则是通过 value 找 key。
我们以古诗的搜索为例,看看这两种索引建立方法各自的表现:
ES 本身只支持英文分词,所以我们需要安装一个 Elasticsearch-Analysis-IK 插件(以下简称IK)来使其支持中文分词,我们就以此为例,学习一下 ES 的插件安装。
从 github 下载 IK:传送门,注意版本要和 ES 的版本对应,IK 的 github 首页有对应表。
创建插件目录:
cd ES的根目录/plugins/ && mkdir ik
使用 apt 安装的 ES 根目录为 /usr/share/elasticsearch。
解压到 plugins/ik 目录。
使用 elasticsearch-plugin 插件安装,在 ES 根目录下执行:
sudo ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.0/elasticsearch-analysis-ik-7.17.0.zip
注意!将“7.17.0”更换为你自己的 ES 版本。
安装完成后,重启 ES 使用即可。
下面的操作,均在 Kibana 的 Dev Tools 中完成。当然,也可以使用 curl 命令(如果会用 curl 的话),甚至 postman 等工具。
新增索引:
PUT /索引名称
{
"settings": {
"index": {
"number_of_shards": 分片数量,
"number_of_replicas": 副本数量
}
}
}
注意!每个 Index (即数据库)的名字必须是小写。
settings 是可选的。
查询索引:
GET /索引名称
删除索引:
DELETE /索引名称
ES 6.x 及以上版本中创建的索引只包含一个类型,不过旧版本创建的索引可以正常使用。而在 7.x 版本中类型被完全移除。
属性 | 描述 | 适合类型 |
---|---|---|
store | 值为yes表示存储,no表示不存储,默认为no | all |
index | yes表示分析,no表示不分析,默认为true | text |
null_value | 如果字段为空,可以设置一个默认值,比如"NA"(传过来为空,不能搜索,na可以搜索) | all |
analyzer | 可以设置索引和搜索时用的分析器,默认使用的是standard分析器,还可以使用whitespace,simple。都是英文分析器 | all |
include_in_all | 默认es为每个文档定义一个特殊域_all,它的作用是让每个字段都被搜索到,如果想让某个字段不被搜索到,可以设置为false | all |
format | 时间格式字符串模式 | date |
创建索引的同时创建映射:
PUT 索引名称
{
"mapping":{
"字段名称":类型,
"age": { "type": "integer" },
"email": { "type": "keyword" },
"name": { "type": "text" }
}
}
向已存在映射添加字段:
PUT 索引名称/_mapping
{
"properties": {
"字段名称":类型,
}
}
更新映射:
不允许更新,只能新建索引,然后将重新索引数据。
查询映射:
GET 索引名称/_mapping
删除映射:
只能连通索引一起删除。
索引文档(即插入文档或者说给文档建立索引):
POST 索引名称/_doc
{
"@timestamp": "2099-05-06T16:21:15.000Z",
"event": {
"original": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736"
}
}
_doc 就是7.x 及更高版本默认的类型(type),不允许再创建。
插入文档时,可以不按照映射来,多几个或少几个字段都能正常插入。
如果向不存在的索引插入文档,会自动创建索引。
查询文档:
GET 索引名称/_doc/id号
修改文档:
POST 索引名称/_update/id号
{
"doc": {
"name": "new_name"
}
}
删除文档:
DELETE 索引名称/_doc/id号
全文查询会将用户给出的关键字进行分词处理,然后用分好的词进行查询。
match:
分词后任意一个词项匹配,文档就会查询到。
GET 索引名称/_doc/_search
{
"query":{
"match":{
"字段":"值"
"name":"hugh"
}
}
}
match_phrase:
文档满足以下条件才能被匹配:
GET 索引名称/_doc/_search
{
"query":{
"match_phrase":{
"name":"hugh"
}
}
}
match_phrase_prefix:
和match_phrase搜索类似,只不过支持词项的前缀匹配。
GET 索引名称/_search
{
"query": {
"match_phrase_prefix": {
"name":"hu"
}
}
}
multi_match搜索:
用于搜索多个字段。
GET 索引名称/_search
{
"query": {
"multi_match": {
"query": "hugh",
"fields": ["student", "Chinese"]
}
}
}
上述语句会在student
和Chinese
字段中查询包含单词 hugh 的文档。
与全文查询不同,词语级查询不会将关键字分词,而是精确匹配关键字。
term:
返回“地址”字段包含关键字“亚洲中国”的文档。
GET 索引名称/_search
{
"query": {
"term": {
"地址":"亚洲中国"
}
}
}
terms:
可以指定多个关键字,在文档中匹配到其中一个或全部,就返回该文档。
GET 索引名称/_search
{
"query": {
"terms": {
"user.id": [ "kimchy", "elkbee" ],
"boost": 1.0
}
}
}
通过下面四个操作符,将多个简单查询组合在一起:
must:必须匹配其中的条件,等价于 and。
should:可以匹配也可以不匹配,但是匹配到的文档,会增加评分(评分越高,排名越靠前)。
must_not:会剔除匹配到的文档。
filter:过滤子句,实现更加复杂的过滤。(下一小节单独学习)
GET 索引名称/_search
{
"query": {
"bool": {
"must": [{
"term": {
"text_entry": {
"value": "must"
}
}}
],
"should": [{
"term": {
"text_entry": {
"value": "have"
}
}}
]
}
}
}
直接过滤:
给出字段和值,不符合的直接被过滤掉。
GET 索引名称/_search
{
"query": {
"bool": {
"filter": {
"term": {
"字段": "值"
}
}
}
}
}
范围过滤:
和 range 合用,过滤掉不在范围内的结果。range 选项:
GET 索引名称/_search
{
"query": {
"bool": {
"must": {
"term": {
"merchant_id": "2501"
}
},
"filter": {
"range": {
"age": {
"lte":25
}
}
}
}
}
}
GET 索引名称/_doc/_search
{
"query":{
"match":{
"字段":"值"
"name":"hugh"
}
},
"sort":[
{
"排序字段":{
"order":"asc升序或者desc降序"
}
}
]
}
GET 索引名称/_doc/_search
{
"query":{
"match":{
"字段":"值"
"name":"hugh"
}
},
"from": 起始位置,
"size": 每页文档数
}
GET 索引名称/_doc/_search
{
"query":{
"match":{
"字段":"值"
"name":"hugh"
}
},
"highlight":[
{
"pre_tags":"在搜索结果之前加的样式标签",
"post_tags":"在结果后面加的样式标签",
"fields":{
"要高亮的字段":{}
}
}
]
}
max:
GET 索引名称/_doc/_search
{
"aggs": {
"max_price": { "max": { "field": "price" } }
}
}
min:
GET 索引名称/_doc/_search
{
"aggs": {
"min_price": { "min": { "field": "price" } }
}
}
sum:
GET 索引名称/_doc/_search
{
"query": {
"constant_score": {
"filter": {
"match": { "type": "hat" }
}
}
},
"aggs": {
"hat_prices": { "sum": { "field": "price" } }
}
}
avg:
{
"aggs": {
"avg_grade": { "avg": { "field": "grade" } }
}
}
IK 的两种分词方式:
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。
设置方法:
在建立索引时,通过 analyzer 指定:
PUT 索引名称
{
"mappings":{
"properties":{
"字段名称":{
"type":"text",
"analyzer":"分词方式"
},
"字段名称":{
"type":"text",
"analyzer":"分词方式"
}
}
}
}
Python Elasticsearch Client 是 ES 官方提供的底层客户端。
安装:
pip install elasticsearch
如果使用 async/await ,则使用下面的方法安装:
pip install elasticsearch[async]
快速入门:
# 导入客户端
from elasticsearch import Elasticsearch
# 实例化客户端实例
es = Elasticsearch()
# 插入文档
doc = {
'author': 'kimchy',
'text': 'Elasticsearch: cool. bonsai cool.',
'timestamp': datetime.now(),
}
res = es.index(index="test-index", id=1, document=doc)
# 查询文档
res = es.search(index="test-index", body={"query": {"match_all": {}}})