创建一个索引
创建一个名为customer的索引,然后再次列出所有索引:
PUT /customer?pretty
GET /_cat/indices?v
第一条命令使用PUT动词创建名为“ customer”的索引。我们只需pretty在调用末尾追加内容,以告诉它漂亮地打印JSON响应(如果有)。
以及响应:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open customer 95SQ4TSUT7mWBT7VNHH67A 5 1 0 0 260b 260b
第二个命令的结果告诉我们,我们现在有1个名为customer的索引,它具有5个主分片和1个副本(默认值),并且其中包含0个文档。现在customer索引带有标记为黄色的健康状况。默认情况下,Elasticsearch为此索引创建了一个副本。由于目前我们只有一个节点在运行,因此直到另一节点加入群集的稍后时间,才能分配一个副本(以实现高可用性)。一旦将该副本分配到第二个节点,此索引的运行状况将变为绿色。
索引和查询文件
现在在customer索引中添加一些内容。我们将一个简单的客户文档编入客户索引,其ID为1,如下所示:
PUT /customer/_doc/1?pretty
{
"name": "John Doe"
}
以及响应:
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
从上面可以看到,在客户索引中成功创建了一个新的客户文档。该文档的内部ID为1,这是我们在索引时指定的。重要的是要注意,Elasticsearch不需要先显式创建索引,然后才能将文档建立索引。
现在,让我们检索刚刚索引的文档:
GET /customer/_doc/1?pretty
响应:
{
"_index" : "customer",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : { "name": "John Doe" }
}
found字段为true,我们找到了具有请求的ID 1的文档,另一个字段_source,该字段返回上一步中索引的完整JSON文档。
删除索引
现在,删除刚刚创建的索引:
DELETE /customer?pretty
修改数据
Elasticsearch提供近乎实时的数据处理和搜索功能。默认情况下,从索引/更新/删除数据到显示在搜索结果中的时间可能会有一秒钟的延迟(刷新间隔)。这是与其他平台(例如SQL)的重要区别,在其他平台上,数据在事务完成后可用。
索引/更新文件
前面我们已经看到了如何为单个文档建立索引。让我们再次调用该命令:
PUT /customer/_doc/1?pretty
{
"name": "John Doe"
}
同样,上面的代码会将指定文档的ID索引到客户索引中,其ID为1。如果我们再对另一个(或相同)文档执行上述命令,Elasticsearch将更新(即重新索引)一个新文档。现有的ID为1的1:
PUT /customer/_doc/1?pretty
{
"name": "John Doe"
}
上面将ID为1的文档的名称从“ John Doe”更改为“ Jane Doe”。另一方面,如果我们使用其他ID,则将为新文档建立索引,而索引中已经存在的现有文档保持不变。
PUT /customer/_doc/2?pretty
{
"name": "Jane Doe"
}
上面的代码索引了ID为2的新文档。
编制索引时,ID部分是可选的。如果未指定,Elasticsearch将生成一个随机ID,然后将其用于索引文档。Elasticsearch生成的实际ID(或我们在前面的示例中明确指定的ID)将作为索引API调用的一部分返回。
此示例显示如何在没有显式ID的情况下索引文档:
POST /customer/_doc?pretty
{
"name": "Jane Doe"
}
在上述情况下,由于未指定ID ,因此我们使用动词POST代替PUT。
更新文件
除了能够索引和替换文档之外,我们还可以更新文档。注意,Elasticsearch实际上并未在后台进行就地更新。每当我们进行更新时,Elasticsearch都会删除旧文档,然后以一个快照将应用了更新的新文档编入索引。
本例说明如何通过将名称字段更改为“ Jane Doe”来更新我们以前的文档(ID为1):
POST /customer/_doc/1/_update?pretty
{
"doc": { "name": "Jane Doe" }
}
本示例显示如何通过将名称字段更改为“ Jane Doe”并同时向其添加年龄字段来更新我们之前的文档(ID为1):
POST /customer/_doc/1/_update?pretty
{
"doc": { "name": "Jane Doe", "age": 20 }
}
也可以使用简单的脚本执行更新。本示例使用脚本将年龄增加5:
POST /customer/_doc/1/_update?pretty
{
"script" : "ctx._source.age += 5"
}
在上面的示例中,ctx._source指的是当前源文档。
删除文件
删除文档非常简单。此示例显示了如何删除ID为2的先前客户:
DELETE /customer/_doc/2?pretty
注意,删除整个索引比使用Delete By Query API删除所有文档要有效得多。
批量处理
除了能够索引,更新和删除单个文档外,Elasticsearch还提供了使用_bulkAPI批量执行上述任何操作的功能。此功能可以以尽可能少的网络往返来尽可能快地执行多项操作。
作为快速示例,以下调用在一个批量操作中为两个文档(ID 1-John Doe和ID 2-Jane Doe)建立了索引:
POST /customer/_doc/_bulk?pretty
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
本例在一个批量操作中更新第一个文档(ID为1),然后删除第二个文档(ID为2):
POST /customer/_doc/_bulk?pretty
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}
注意,对于删除操作,在其后没有相应的源文档,因为删除仅需要删除文档的ID。
批量API不会因其中一项操作失败而失败。如果单个操作由于任何原因而失败,它将继续处理其后的其余操作。批量API返回后,它将为每个操作提供状态(以发送顺序相同),以便可以检查特定操作是否失败。
搜索API
现在让我们从一些简单的搜索开始。有运行检索两种基本方式:一种是通过发送搜索参数REST请求URI和其他通过发送他们REST请求主体。request body方法使用更具可读性的JSON格式定义搜索。可从_search端点访问用于搜索的REST API 。
本示例返回bank索引中的所有单据:
GET /bank/_search?q=*&sort=account_number:asc&pretty
我们在bank索引中搜索,并且该q=*参数指示Elasticsearch匹配索引中的所有文档。该sort=account_number:asc参数指示使用account_number每个文档的字段以升序对结果进行排序。pretty同样,该参数只是告诉Elasticsearch返回漂亮打印的JSON结果。
以及响应(部分显示):
{
"took" : 63,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1000,
"max_score" : null,
"hits" : [ {
"_index" : "bank",
"_type" : "_doc",
"_id" : "0",
"sort": [0],
"_score" : null,
"_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}
}, {
"_index" : "bank",
"_type" : "_doc",
"_id" : "1",
"sort": [1],
"_score" : null,
"_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
}, ...
]
}
}
对于响应,我们看到以下部分:
took – Elasticsearch执行搜索的时间(以毫秒为单位)
timed_out –告诉我们搜索是否超时
_shards –告诉我们搜索了多少个分片,以及成功/失败的搜索分片的数量
hits - 搜索结果
hits.total –符合我们搜索条件的文档总数
hits.hits –搜索结果的实际数组(默认为前10个文档)
hits.sort -结果的排序键(如果按分数排序则丢失)
hits._score字段是一个数值,是文档与我们指定的搜索查询匹配程度的相对度量。分数越高,文档越相关,分数越低,文档越不相关。但是查询并不总是需要产生分数,尤其是当查询仅用于“过滤”文档集时。Elasticsearch会检测到这些情况并自动优化查询执行,以免计算出无用的分数。
这是上面使用替代请求正文方法的完全相同的搜索:
GET /bank/_search
{
"query": { "match_all": {} },
"sort": [
{ "account_number": "asc" }
]
}
此处的区别在于,我们没有传递q=*URI,而是使用_search API提供的JSON样式的查询请求主体
重要的是要了解,一旦返回搜索结果,Elasticsearch将完全完成请求,并且不会维护任何类型的服务器端资源或在结果中打开游标。
查询语言
Elasticsearch提供了一种JSON-style domain-specific语言,您可以用来执行查询。这称为查询DSL。查询语言非常全面。
回到上一个示例,我们执行了以下查询:
GET /bank/_search
{
"query": { "match_all": {} }
}
该query部分告诉我们查询定义是什么,而该match_all部分只是我们要运行的查询的类型。该match_all查询是搜索指定索引中的所有文档。
除了query参数外,我们还可以传递其他参数来影响搜索结果。在上面部分的示例中,我们传入 sort,在这里我们传入size:
GET /bank/_search
{
"query": { "match_all": {} },
"size": 1
}
注意,如果size未指定,则默认为10。
本示例执行match_all并返回文档10至19:
GET /bank/_search
{
"query": { "match_all": {} },
"from": 10,
"size": 10
}
该from参数(从0开始)指定从和启动哪些文档索引size参数指定如何许多文件,返回从参数开始的。实现分页搜索结果时,此功能很有用。请注意,如果from未指定,则默认为0。
本示例执行a match_all并按帐户余额的降序对结果进行排序,并返回前10个(默认大小)的文档。
GET /bank/_search
{
"query": { "match_all": {} },
"sort": { "balance": { "order": "desc" } }
}
搜索
默认情况下,完整的JSON文档将作为所有搜索的一部分返回。这称为源文档(_source搜索结果中的字段)。如果我们不希望返回整个源文档,则可以从源中仅请求返回几个字段。
此示例说明了如何从搜索中返回两个字段account_number和balance(在中_source):
GET /bank/_search
{
"query": { "match_all": {} },
"_source": ["account_number", "balance"]
}
请注意,上面的示例只是简化了该_source字段。它仍然只会返回一个名为_source的字段,但只有account_number和balance也包括在内。在概念上与SQL SELECT FROM字段列表有些相似。
matchquery查询,可以将其视为基本的字段搜索查询(即针对特定字段或一组字段进行的搜索)。
本示例返回编号为20的帐户:
GET /bank/_search
{
"query": { "match": { "account_number": 20 } }
}
本示例返回地址中包含“ mill”的所有帐户:
GET /bank/_search
{
"query": { "match": { "address": "mill" } }
}
本示例返回地址中包含“ mill”或“ lane”的所有帐户:
GET /bank/_search
{
"query": { "match": { "address": "mill lane" } }
}
此示例是match(match_phrase)的变体,它返回地址中包含短语“mill lane”的所有帐户,“mill lane”不分词:
GET /bank/_search
{
"query": { "match_phrase": { "address": "mill lane" } }
}
bool查询。该bool查询允许我们使用boolean逻辑将较小的查询组合为较大的查询。
本示例组成两个match查询,并返回地址中包含“ mill”和“ lane”的所有帐户:
GET /bank/_search
{
"query": {
"bool": {
"must": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
在上面的示例中,must子句指定了所有文档进行匹配,返回结果为真的查询。
相反,此示例组成两个match查询,并返回地址中包含“ mill”或“ lane”的所有帐户:
GET /bank/_search
{
"query": {
"bool": {
"should": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
在上面的示例中,该bool should子句指定了一个查询列表,索引中的文档匹配每一个字句,返回的结果至少一个为真。
本示例组成两个match查询,并返回地址中既不包含“ mill”也不包含“ lane”的所有帐户:
GET /bank/_search
{
"query": {
"bool": {
"must_not": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
在上面的示例中,该bool must_not子句指定了一个查询列表,将文档视为匹配项,返回的结果没有一个为真。
我们可以在查询中同时组合must,should和must_not子句bool。此外,也可以将bool嵌套进bool子句中组成查询,适应任何复杂的多级布尔逻辑。
此示例返回40岁但未使用ID(aho)的任何人的所有帐户:
GET /bank/_search
{
"query": {
"bool": {
"must": [
{ "match": { "age": "40" } }
],
"must_not": [
{ "match": { "state": "ID" } }
]
}
}
}
过滤器
bool查询还支持filter子句,这些子句允许使用查询来限制将由其他子句匹配的文档,而无需更改分数的计算方式。作为示例,让我们介绍一下range查询,它允许我们按一定范围的值过滤文档。通常用于数字或日期过滤。
本示例使用布尔查询返回所有余额在20000到30000(含)之间的帐户。换句话说,我们要查找余额大于或等于20000且小于或等于30000的帐户。
GET /bank/_search
{
"query": {
"bool": {
"must": { "match_all": {} },
"filter": {
"range": {
"balance": {
"gte": 20000,
"lte": 30000
}
}
}
}
}
}
bool查询包含一个match_all查询(查询部分)和一个range查询(过滤器部分)。我们可以将任何其他查询替换为查询和过滤器部分。范围查询非常有意义,因为属于该范围的文档都“相等”匹配,即没有文档比另一个文档更相关。
聚合
汇总功能可以对数据进行分组和提取统计信息。考虑聚合的最简单方法是将其大致等同于SQL GROUP BY和SQL聚合函数。在Elasticsearch中,可以执行搜索以返回匹配结果,同时在一个响应中返回与匹配结果分开的汇总结果。这是非常强大和高效的,您可以运行查询和多次聚合,并一次性获得两个(或两个)操作的结果,从而避免使用简单的API进行网络往返。
首先,此示例按状态对所有帐户进行分组,然后返回按计数递减排序的前10个(默认)状态(也是默认):
GET /bank/_search
{
"size": 0,
"aggs": { //聚集api函数名字
"group_by_state": { //自定义的聚集的名字
"terms": {//分组api函数名字
"field": "state.keyword"
}
}
}
}
在SQL中,以上聚合在概念上类似于:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
以及响应(部分显示):
{
"took": 29,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped" : 0,
"failed": 0
},
"hits" : {
"total" : 1000,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
"group_by_state" : {
"doc_count_error_upper_bound": 20,
"sum_other_doc_count": 770,
"buckets" : [ {
"key" : "ID",
"doc_count" : 27
}, {
"key" : "TX",
"doc_count" : 27
}, {
"key" : "AL",
"doc_count" : 25
}, {
"key" : "MD",
"doc_count" : 25
}, {
"key" : "TN",
"doc_count" : 23
}, {
"key" : "MA",
"doc_count" : 21
}, {
"key" : "NC",
"doc_count" : 21
}, {
"key" : "ND",
"doc_count" : 21
}, {
"key" : "ME",
"doc_count" : 20
}, {
"key" : "MO",
"doc_count" : 20
} ]
}
}
}
我们可以看到ID有27个帐户,其次是TX有27个帐户,其次是AL有25个帐户,依此类推。注意,我们设置size=0为不显示搜索结果,因为我们只想查看响应中的汇总结果。
在上一个汇总的基础上,此示例按州计算平均帐户余额(同样,仅对按计数降序排序的前十个州):
{
"took": 29,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped" : 0,
"failed": 0
},
"hits" : {
"total" : 1000,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
"group_by_state" : {
"doc_count_error_upper_bound": 20,
"sum_other_doc_count": 770,
"buckets" : [ {
"key" : "ID",
"doc_count" : 27
}, {
"key" : "TX",
"doc_count" : 27
}, {
"key" : "AL",
"doc_count" : 25
}, {
"key" : "MD",
"doc_count" : 25
}, {
"key" : "TN",
"doc_count" : 23
}, {
"key" : "MA",
"doc_count" : 21
}, {
"key" : "NC",
"doc_count" : 21
}, {
"key" : "ND",
"doc_count" : 21
}, {
"key" : "ME",
"doc_count" : 20
}, {
"key" : "MO",
"doc_count" : 20
} ]
}
}
}
注意我们将average_balance聚合嵌套在聚合内部group_by_state。这是所有多重聚合的通用模式。你可以在聚合中任意嵌套聚合,从数据中提取所需的结果汇总。
基于先前的汇总,现在让我们按降序对平均余额进行排序:
GET /bank/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"order": {
"average_balance": "desc"
}
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
此示例演示了如何按年龄段(20-29、30-39和40-49岁)分组,然后按性别分组,然后最终获得按年龄段分组,按性别分组的平均帐户余额:
GET /bank/_search
{
"size": 0,
"aggs": {
"group_by_age": {
"range": {
"field": "age",
"ranges": [
{
"from": 20,
"to": 30
},
{
"from": 30,
"to": 40
},
{
"from": 40,
"to": 50
}
]
},
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
}
}