Elasticsearch常见用法-入门

时间:2024-05-27 21:02:50
  1. 前台启动

    默认是只有本地可以访问
./bin/elasticsearch
  1. 远程访问

修改elasticsearch.yml,把network.host(注意配置文件格式不是以 # 开头的要空一格, :后要空一格)

network.host: 0.0.0.0

  1. 在后台以守护进程模式运行

添加 -d 参数

./bin/elasticsearch -d
  1. 查看状态
curl 'http://localhost:9200/?pretty'

{
"name": "node-1",
"cluster_name": "my-application",
"cluster_uuid": "x6eQJV6nR6SLjm3cvqSJ6A",
"version": {
"number": "7.5.0",
"build_flavor": "default",
"build_type": "tar",
"build_hash": "e9ccaed468e2fac2275a3761849cbee64b39519f",
"build_date": "2019-11-26T01:06:52.518245Z",
"build_snapshot": false,
"lucene_version": "8.3.0",
"minimum_wire_compatibility_version": "6.8.0",
"minimum_index_compatibility_version": "6.0.0-beta1"
},
"tagline": "You Know, for Search"
}
  1. 集群和节点

节点(node)是一个运行着的Elasticsearch实例。集群(cluster)是一组具有相同 cluster.name 的节点集合,他们协同工作,共享数据并提供故障转移和扩展功能,当然一个节点也可以组成一个集群。可以通过修改 config/ 目录下的 elasticsearch.yml 文件,然后启ELasticsearch来做到这一点。

  1. 关闭ES

    当Elasticsearch在前台运行,可以使用 Ctrl-C 快捷键终止,或者你可以调

    shutdown API来关闭:
curl -XPOST 'http://localhost:9200/_shutdown'
  1. 与Elasticsearch交互

    如何与Elasticsearch交互取决于你是否使用Java。
  • Java API

    Elasticsearch为Java用户提供了两种内置客户端:
    • 节点客户端(node client):

      节点客户端以无数据节点(none data node)身份加入集群,换言之,它自己不存储任何数据,但是它知道数据在集群中的具体位置,并且能够直接转发请求到对应的节点上。
    • 传输客户端(Transport client):这个更轻量的传输客户端能够发送请求到远程集群。它自己不加入集群,只是简单转发请求给集群中的节点。

      两个Java客户端都通过9300端口与集群交互,使用Elasticsearch传输协议(Elasticsearch Transport Protocol)。集群中的节点之间也通过9300端口进行通信。如果此端口未开放,你的节点将不能组成集群。

TIP

Java客户端所在的Elasticsearch版本必须与集群中其他节点一致,否则,它们可能互相

无法识别。

  • 基于HTTP协议,以JSON为数据交互格式的RESTful API

    其他所有程序语言都可以使用RESTful API,通过9200端口的与Elasticsearch进行通信,你可以使用你喜欢的WEB客户端,事实上,如你所见,你甚至可以通过 curl 命令与Elasticsearch通信。

向Elasticsearch发出的请求的组成部分与其它普通的HTTP请求是一样的:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'

VERB: HTTP方法: GET , POST , PUT , HEAD , DELETE

PROTOCOL: http或者https协议(只有在Elasticsearch前面有https代理的时候可用)

HOST: Elasticsearch集群中的任何一个节点的主机名,如果是在本地的节点,那么就叫localhost

PORT: Elasticsearch HTTP服务所在的端口,默认为9200

PATH: API路径(例如_count将返回集群中文档的数量),PATH可以包含多个组件,例如_cluster/stats或者_nodes/stats/jvm

QUERY_STRING: 一些可选的查询请求参数,例如 ?pretty 参数将使请求返回更加美观易读的JSON数据

BODY: 一个JSON格式的请求主体(如果请求需要的话)

举例说明,为了计算集群中的文档数量,我们可以这样做:

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
}
}'

Elasticsearch返回一个类似 200 OK 的HTTP状态码和JSON格式的响应主体(除了 HEAD 请求)。上面的请求会得到如下的JSON格式的响应主体:

{
"count" : 5507,
"_shards" : {
"total" : 9,
"successful" : 9,
"skipped" : 0,
"failed" : 0
}
}

我们看不到HTTP头是因为我们没有让 curl 显示它们,如果要显示,使用 curl 命令后跟 -i 参数:

curl -i -XGET 'localhost:9200/'

以后将简写 curl 请求中重复的部分,例如主机名和端口,还有 curl 命令本身。

一个完整的请求形如:

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
}
}'

将简写成这样:

GET /_count
{
"query": {
"match_all": {}
}
}
  1. 在Elasticsearch中存储数据的行为就叫做索引(indexing),在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中。对比图来类比传统关系型数据库:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields

Elasticsearch集群可以包含多个索引(indices)(数据库),每一个索引可以包含多个类型(types)(表),每一个类型包含多个文档(documents)(行),然后每个文档包含多个字段(Fields)(列)。

(新版本中每一个索引只能包含一种类型)

索引含义的区分

  • 索引(名词) 一个索引(index)就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是indices 或indexes。
  • 索引(动词) 「索引一个文档」表示把一个文档存储到索引(名词)里,以便它可以被检索或者查询。这很像SQL中的 INSERT 关键字,差别是,如果文档已经存在,新的文档将覆盖旧的文档。
  • 倒排索引 传统数据库为特定列增加一个索引,例如B-Tree索引来加速检索。Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到相同目的。

默认情况下,文档中的所有字段都会被索引(拥有一个倒排索引),只有这样他们才是可被搜索的。

  1. 添加索引数据
PUT /megacorp/_doc/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}

返回结果:

{
"_index" : "megacorp",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "create",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 4,
"_primary_term" : 1
}
  1. 检索文档

    执行HTTP GET请求并指出文档的“地址”——索引、类型和ID既可。根据这三部分信息,我们就可以返回原始JSON文档:
GET /megacorp/_doc/1

响应的内容中包含一些文档的元信息,John Smith的原始JSON文档包含在 _source 字段中。

{
"_index" : "megacorp",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
}

通过HTTP方法 GET 来检索文档,同样的,可以使用 DELETE 方法删除文档,使用 HEAD 方法检查某文档是否存在。如果想更新已存在的文档,只需再 PUT 一次。

  1. 简单搜索

    尝试一个最简单的搜索全部员工的请求:
GET /megacorp/_search

{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests" : [
"music"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about" : "I like to build cabinets",
"interests" : [
"forestry"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
}
]
}
}

在结尾使用关键字 _search 来取代原来的文档ID。响应内容的 hits 数组中包含了我们所有的四个文档。

默认情况下搜索会返回前10个结果。

注意:响应内容不仅会告诉我们哪些文档被匹配到,而且这些文档内容完整的被包含在其中

搜索姓氏中包含“Smith”的员工。要做到这一点,我们将在命令行中使用轻量级的搜索方法。这种方法常被称作查询字符串(query string)搜索,因为我们像传递URL参数一样去传递查询语句:

GET /megacorp/_search?q=last_name:Smith

在请求中依旧使用 _search 关键字,然后将查询语句传递给参数 q= 。这样就可以得到所有姓氏为Smith的结果:

  1. 使用DSL语句查询

    查询字符串搜索便于通过命令行完成特定(ad hoc)的搜索。Elasticsearch提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。

    DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。我们可以这样表示之前关于“Smith”的查询:
GET /megacorp/_search
{
"query": {
"match": {
"last_name": "Smith"
}
}
}

这会返回与之前查询相同的结果。不再使用查询字符串(query string)做为参数,而是使用请求体代替。这个请求体使用JSON表示,其中使用了 match 语句

  1. 更复杂的搜索

    添加过滤器(filter)
# 之前的语法
GET /megacorp/_search
{
"query" : {
"filtered" : {
"filter" : {
"range" : {
"age" : { "gt" : 30 } <1>
}
},
"query" : {
"match" : {
"last_name" : "smith" <2>
}
}
}
}
}
# 现在的语法
GET /megacorp/_search
{
"query": {
"bool": {
"filter": {
"range": {
"age": {
"gt": 30
}
}
},
"must": [
{"match": {
"last_name": "smith"
}}
]
}
}
}
  1. 全文搜索

    搜索所有喜欢“rock climbing”的员工:

    match是把短语拆分开,一个词一个词的查询
GET /megacorp/_search
{
"query": {
"match": {
"about": "rock climbing"
}
}
}

可以看到我们使用了之前的 match 查询,从 about 字段中搜索"rock climbing",我们得到了两个匹配文档:

{
"took" : 16,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : 1.0314757,
"hits" : [
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_score" : 1.0314757,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_score" : 1.0314757,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "3",
"_score" : 0.35044178,
"_source" : {
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests" : [
"music"
]
}
}
]
}
}

默认情况下,Elasticsearch根据结果相关性评分来对结果集进行排序,所谓的「结果相关性评分」就是文档与查询条件的匹配程度。很显然,排名第一的 John Smith 的 about 字段明确的写到“rock climbing”。

但是为什么 Jane Smith 也会出现在结果里呢?原因是“rock”在她的 abuot 字段中被提及了。因为只有“rock”被提及而“climbing”没有,所以她的 _score 要低于John。

这个例子很好的解释了Elasticsearch如何在各种文本字段中进行全文搜索,并且返回相关性最大的结果集。

  1. 短语搜索

    也就是说把查询条件作为一个整体来查询,不拆开进行查询。将 match 查询变更为 match_phrase 查询即可:
GET /megacorp/_search
{
"query": {
"match_phrase": {
"about": "rock climbing"
}
}
}

毫无疑问,该查询返回John Smith的文档:

{
"took" : 19,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0314757,
"hits" : [
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_score" : 1.0314757,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_score" : 1.0314757,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
}
]
}
}
  1. 高亮搜索

    很多应用喜欢从每个搜索结果中高亮(highlight)匹配到的关键字,这样用户可以知道为什么这些文档和查询相匹配。在Elasticsearch中高亮片段是非常容易的。

    在之前的语句上增加 highlight 参数:
GET /megacorp/_search
{
"query": {
"match_phrase": {
"about": "rock climbing"
}
},
"highlight": {
"fields": {
"about": {}
}
}
}

在返回结果中会有一个新的部分叫做 highlight ,这里包含了来自 about 字段中的文本,并且用 <em></em> 来标识匹配到的单词。

{
"took" : 112,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0314757,
"hits" : [
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_score" : 1.0314757,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
},
"highlight" : {
"about" : [
"I love to go <em>rock</em> <em>climbing</em>"
]
}
},
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_score" : 1.0314757,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
},
"highlight" : {
"about" : [
"I love to go <em>rock</em> <em>climbing</em>"
]
}
}
]
}
}
  1. 分析-聚合

    Elasticsearch有一个功能叫做聚合(aggregations),它允许你在数据上生成复杂的分析统计。它很像SQL中的 GROUP BY 但是功能更强大。

老版本用法如下:

GET /megacorp/_search
{
"aggs": { "all_interests": {
"terms": {
"field": "interests"
}
}
}
}

如果使用的是Elasticsearch 5版本以上的话,将会出现如下异常:

{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [interests] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
}
],
"type": "search_phase_execution_exception",
"reason": "all shards failed",
"phase": "query",
"grouped": true,
"failed_shards": [
{
"shard": 0,
"index": "megacorp",
"node": "PiLgJiUqTHmF-CtvlliYVA",
"reason": {
"type": "illegal_argument_exception",
"reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [interests] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
}
}
],
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [interests] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [interests] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
}
}
},
"status": 400
}

大概的意思是:Fielddata可以消耗大量的堆空间,特别是在加载高基数文本字段时。一旦fielddata已经加载到堆中,它在该段的生存期内保持。此外,加载fielddata是一个昂贵的过程,可以导致用户体验延迟命中。

所以fielddata默认禁用。如果尝试对文本字段上的脚本进行排序,聚合或访问值,就会看到这个异常,具体使用可以参考手册。

  1. 分布式

    Elasticsearch致力于隐藏分布式系统的复杂性。以下这些操作都是在底层自动完成的:
  • 将你的文档分区到不同的容器或者分片(shards)中,它们可以存在于一个或多个节点中。
  • 将分片均匀的分配到各个节点,对索引和搜索做负载均衡。
  • 冗余每一个分片,防止硬件故障造成的数据丢失。
  • 将集群中任意一个节点上的请求路由到相应数据所在的节点。
  • 无论是增加节点,还是移除节点,分片都可以做到无缝的扩展和迁移。