本节大部分内容转载至(
Elasticsearch 基础教程
翻译:潘飞(tinylambda@gmail.com)
)
一、样本数据集
载入样本数据,提供10条数据
{
"index"
:{
"_id"
:
"1"
}}
{
"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"
}
{
"index"
:{
"_id"
:
"6"
}}
{
"account_number"
:6,
"balance"
:5686,
"firstname"
:
"Hattie"
,
"lastname"
:
"Bond"
,
"age"
:36,
"gender"
:
"M"
,
"address"
:
"671 Bristol Street"
,
"employer"
:
"Netagy"
,
"email"
:
"hattiebond@netagy.com"
,
"city"
:
"Dante"
,
"state"
:
"TN"
}
{
"index"
:{
"_id"
:
"13"
}}
{
"account_number"
:13,
"balance"
:32838,
"firstname"
:
"Nanette"
,
"lastname"
:
"Bates"
,
"age"
:28,
"gender"
:
"F"
,
"address"
:
"789 Madison Street"
,
"employer"
:
"Quility"
,
"email"
:
"nanettebates@quility.com"
,
"city"
:
"Nogal"
,
"state"
:
"VA"
}
{
"index"
:{
"_id"
:
"18"
}}
{
"account_number"
:18,
"balance"
:4180,
"firstname"
:
"Dale"
,
"lastname"
:
"Adams"
,
"age"
:33,
"gender"
:
"M"
,
"address"
:
"467 Hutchinson Court"
,
"employer"
:
"Boink"
,
"email"
:
"daleadams@boink.com"
,
"city"
:
"Orick"
,
"state"
:
"MD"
}
{
"index"
:{
"_id"
:
"20"
}}
{
"account_number"
:20,
"balance"
:16418,
"firstname"
:
"Elinor"
,
"lastname"
:
"Ratliff"
,
"age"
:36,
"gender"
:
"M"
,
"address"
:
"282 Kings Place"
,
"employer"
:
"Scentric"
,
"email"
:
"elinorratliff@scentric.com"
,
"city"
:
"Ribera"
,
"state"
:
"WA"
}
{
"index"
:{
"_id"
:
"25"
}}
{
"account_number"
:25,
"balance"
:40540,
"firstname"
:
"Virginia"
,
"lastname"
:
"Ayala"
,
"age"
:39,
"gender"
:
"F"
,
"address"
:
"171 Putnam Avenue"
,
"employer"
:
"Filodyne"
,
"email"
:
"virginiaayala@filodyne.com"
,
"city"
:
"Nicholson"
,
"state"
:
"PA"
}
{
"index"
:{
"_id"
:
"32"
}}
{
"account_number"
:32,
"balance"
:48086,
"firstname"
:
"Dillard"
,
"lastname"
:
"Mcpherson"
,
"age"
:34,
"gender"
:
"F"
,
"address"
:
"702 Quentin Street"
,
"employer"
:
"Quailcom"
,
"email"
:
"dillardmcpherson@quailcom.com"
,
"city"
:
"Veguita"
,
"state"
:
"IN"
}
{
"index"
:{
"_id"
:
"37"
}}
{
"account_number"
:37,
"balance"
:18612,
"firstname"
:
"Mcgee"
,
"lastname"
:
"Mooney"
,
"age"
:39,
"gender"
:
"M"
,
"address"
:
"826 Fillmore Place"
,
"employer"
:
"Reversus"
,
"email"
:
"mcgeemooney@reversus.com"
,
"city"
:
"Tooleville"
,
"state"
:
"OK"
}
{
"index"
:{
"_id"
:
"44"
}}
{
"account_number"
:44,
"balance"
:34487,
"firstname"
:
"Aurelia"
,
"lastname"
:
"Harding"
,
"age"
:37,
"gender"
:
"M"
,
"address"
:
"502 Baycliff Terrace"
,
"employer"
:
"Orbalix"
,
"email"
:
"aureliaharding@orbalix.com"
,
"city"
:
"Yardville"
,
"state"
:
"DE"
}
{
"index"
:{
"_id"
:
"49"
}}
{
"account_number"
:49,
"balance"
:29104,
"firstname"
:
"Fulton"
,
"lastname"
:
"Holt"
,
"age"
:23,
"gender"
:
"F"
,
"address"
:
"451 Humboldt Street"
,
"employer"
:
"Anocha"
,
"email"
:
"fultonholt@anocha.com"
,
"city"
:
"Sunriver"
,
"state"
:
"RI"
}
|
将以上数据保存到一个account.json,然后在当前位置执行:curl -XPOST 'localhost:9200/bank/account/_bulk?pretty' --data-binary @account.json,返回结果如下:
此时查看一下我们的当前的索引:http://localhost:9200/_cat/indices?v 已经见了索引,并且有10条数据
二、搜索API
现在,让我们以一些简单的搜索来开始。有两种基本的方式来运行搜索:一种是在 REST 请求的 URI 中发送搜索参数,另一种是将搜索参数发送到 REST 请求体中。请求体方 法的表达能力更好,并且你可以使用更加可读的 JSON 格式来定义搜索。我们将尝试使用一 次请求 URI 作为例子,但是教程的后面部分,我们将仅仅使用请求体方法。
搜索的 REST API 可以通过_search 端点来访问。下面这个例子返回 bank 索引中 的所有的文档:
curl 'localhost:9200/bank/_search?q=*&pretty'
我们仔细研究一下这个查询调用。我们在 bank 索引中搜索(_search 端点),并且 q=*参数指示 Elasticsearch 去匹配这个索引中所有的文档。pretty 参数,和以前一样,仅仅是 告诉 Elasticsearch 返回美观的 JSON 结果。
对于这个响应,我们看到了以下的部分:
- took —— Elasticsearch 执行这个搜索的耗时,以毫秒为单位
- timed_out —— 指明这个搜索是否超时
- _shards —— 指出多少个分片被搜索了,同时也指出了成功/失败的被搜索的shards 的数量
- hits —— 搜索结果
- hits.total —— 能够匹配我们查询标准的文档的总数目
- hits.hits —— 真正的搜索结果数据(默认只显示前 10 个文档) - _score 和 max_score —— 现在先忽略这些字段
使用请求体方法的等价搜索是:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match_all": {} } }'
这里的不同之处在于,并不是向 URI 中传递 q=*,取而代之的是,我们在_search API 的请求体中 POST 了一个 JSON 格式请求体。我们将在下一部分中讨论这个 JSON 查询,返回结果:
有一点需要重点理解一下,一旦你取回了你的搜索结果,Elasticsearch 就完成了使 命,它不会维护任何服务器端的资源或者在你的结果中打开游标。这是和其它类似 SQL 的 平台的一个鲜明的对比, 在那些平台上,你可以在前面先获取你查询结果的一部分,然后 如果你想获取结果的剩余部分,你必须继续返回服务端去取,这个过程使用一种有状态的服 务器端游标技术。
三、介绍查询语言
Elasticsearch 提供一种 JSON 风格的特定领域语言,利用它你可以执行查询。这杯称为 查询 DSL。这个查询语言相当全面,第一眼看上去可能有些咄咄逼人,但是最好的学习方 法就是以几个基础的例子来开始。
3.1 查询全部:query 部分告诉我查询的定义,match_all 部分就是我们想要运行的查询的类型。match_all 查询,就是简单地查询一个指定索引下的所有的文档。
{
"query": { "match_all": {} }
}
3.2 查询全部时指定返回长度,没指定默认为10
{
"query": { "match_all": {} },
"size":1
}
3.3 分页查询:from 表示开始位置,默认为0
{
"query": { "match_all": {} },
"from":1,
"size":1
}
3.4 根据余额降序排列
{
"query": { "match_all": {} },
"sort":{"balance":{"order":"desc"}}
}
四、执行搜索
4.1 返回指定的字段(返回_source里的account_number和balance字段)
{
"query": { "match_all": {} },
"_source":["account_number","balance"]
}
现在让我们进入到查询部分。之前,我们看到了 match_all 查询是怎样匹配到所有的文 档的。现在我们介绍一种新的查询,叫做 match 查询,这可以看成是一个简单的字段搜索查 询(比如对应于某个或某些特定字段的搜索)。
4.2 按指定字段搜索
{
"query": { "match": {"account_number":20} },
}
4.3 按指定字符搜索扩展
{
"query": { "match": {"address":"Holmes Lane"} },
}
从结果看出,这里是模糊匹配的,只要包含查询字段就能查出结果.
附注一: 假如我们改一下搜索的条件,在刚刚执行的搜索条件的两个单词中间多加几个空格,那么查询结果会怎么样呢?
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match": {"address":"Holmes Lane"} }}' 结果如下,依然能搜索出结果:
附注二:假如我们把搜索的两个单词顺序换一下:curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match": {"address":"Lane Holmes"} }}' 结果如下,依然能搜索出结果:
附注三:假如我们加一个字段里并不存在的单词,看看结果会怎么样:curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match": {"address":"Lane Holmes GOGOGO"} }}'
Oh My God,竟然也可以搜索出来!
总结:也就是说,使用match搜索时,系统会自动做分词,只要能部分匹配,就可以搜索出结果。
4.4 短语匹配
{
"query": { "match_phrase ": {"address":"Holmes Lane"} },
}
我们搜索一下:curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match_phrase": {"address":"Holmes Lane"} }}' 结果显示能够搜索出结果.
附注一:我们往搜索的词组加上几个空格:curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match_phrase": {"address":"Holmes Lane"} }}' 结果显示依然能搜出结果。
附注二:假如我们把搜索的两个单词顺序换一下: curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match_phrase": {"address":"Lane Holmes"} }}' 结果显示无法搜索出想要的结果。
附注三:假如我们在原始词组后面加一个单词:curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match_phrase": {"address":"Holmes Lane GO"} }}' 结果显示依然搜不出结果。
总结:也就是说,使用match_phrase搜索时,系统会自动做分词,最后会按顺序去做匹配,必须全部匹配才能返回正确结果。
4.5 布尔查询
现在,让我们介绍一下布尔查询。布尔查询允许我们利用布尔逻辑将较小的查询组合成 较大的查询。
现在这个例子组合了两个 match 查询,这个组合查询返回地址包含“Quentin”和“Street”的所有的账户
4.5.1 curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {
"query": { "bool": {
"must": [
{ "match": { "address": "Quentin" } },
{ "match": { "address": "Street" } }
] }
} }'
在上面的例子中,bool must 语句指明了,对于一个文档,所有的查询都必须为真,这 个文档才能够匹配成功。
相反的,下面的例子组合了两个 match 查询,它返回的是地址中包含“Quentin”或者“Street” 的所有的账户:
4.5.2 curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {
"query": { "bool": {
"should": [
{ "match": { "address": "Quentin" } },
{ "match": { "address": "Street" } }
] }
} }'
在上面的例子中,bool should 语句指明,对于一个文档,查询列表中,只要有一个查询 匹配,那么这个文档就被看成是匹配的。
现在这个例子组合了两个查询,它返回地址中既不包含“Quentin”,同时也不包含“Street” 的所有的账户信息:
4.5.3 curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {
"query": { "bool": {
"must_not": [
{ "match": { "address": "Quentin" } },
{ "match": { "address": "Street" } }
] }
} }'
在上面的例子中, bool must_not 语句指明,对于一个文档,查询列表中的的所有查询 都必须都不为真,这个文档才被认为是匹配的。
我们可以在一个 bool 查询里一起使用 must、should、must_not。此外,我们可以将 bool 查询放到这样的 bool 语句中来模拟复杂的、多等级的布尔逻辑。
下面这个例子返回 40 岁以上并且不生活在 ID(daho)的人的账户:
4.5.4 curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {
"query": { "bool": {
"must": [
{ "match": { "age": "40" } }
], "must_not": [
{ "match": { "state": "ID" } } ]
} }
}'
五、执行过滤器
在先前的章节中,我们跳过了文档得分的细节(搜索结果中的_score 字段)。这个得分 是与我们指定的搜索查询匹配程度的一个相对度量。得分越高,文档越相关,得分越低文档 的相关度越低。
Elasticsearch 中的所有的查询都会触发相关度得分的计算。对于那些我们不需要相关度 得分的场景下,Elasticsearch 以过滤器的形式提供了另一种查询功能。过滤器在概念上类似 于查询,但是它们有非常快的执行速度,这种快的执行速度主要有以下两个原因
- 过滤器不会计算相关度的得分,所以它们在计算上更快一些
- 过滤器可以被缓存到内存中,这使得在重复的搜索查询上,其要比相应的查询快 出许多。
为了理解过滤器,我们先来介绍“被过滤”的查询,这使得你可以将一个查询(像是 match_all,match,bool 等)和一个过滤器结合起来。作为一个例子,我们介绍一下范围过 滤器,它允许我们通过一个区间的值来过滤文档。这通常被用在数字和日期的过滤上。
这个例子使用一个被过滤的查询,其返回值是越在 20000 到 30000 之间(闭区间)的账 户。换句话说,我们想要找到越大于等于 20000 并且小于等于 30000 的账户
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {"query": { "match_all": {} }, "filter":{"range":{"balance":{"gte":20000,"lte":30000}}}}'
分解上面的例子,被过滤的查询包含一个 match_all 查询(查询部分)和一个过滤器(filter 部分)。我们可以在查询部分中放入其他查询,在 filter 部分放入其它过滤器。在上面的应用 场景中,由于所有的在这个范围之内的文档都是平等的(或者说相关度都是一样的),没有 一个文档比另一个文档更相关,所以这个时候使用范围过滤器就非常合适了。
通常情况下,要决定是使用过滤器还是使用查询,你就需要问自己是否需要相关度得分。 如果相关度是不重要的,使用过滤器,否则使用查询。如果你有 SQL 背景,查询和过滤器 在概念上类似于 SELECT WHERE 语句, although more so for filters than queries。
除了 match_all, match, bool,filtered 和 range 查询,还有很多其它类型的查询过滤器, 我们这里不会涉及。由于我们已经对它们的工作原理有了基本的理解,将其应用到其它类型 的查询、过滤器上也不是件难事。
六、执行聚合
聚合提供了分组并统计数据的能力。理解聚合的最简单的方式是将其粗略地等同为 SQL 的 GROUP BY 和 SQL 聚合函数。在 Elasticsearch 中,你可以在一个响应中同时返回命中的 数据和聚合结果。你可以使用简单的 API 同时运行查询和多个聚合,并以一次返回,这避 免了来回的网络通信,这是非常强大和高效的。
6.1作为开始的一个例子,我们按照gender分组,按照性别的计数倒序排序:
在 SQL 中,上面的聚合在概念上类似于:SELECT COUNT(*) from bank GROUP BY gender ORDER BY COUNT(*) DESC
注意我们将 size 设置成 0,这样我们就可以只看到聚合结果了,而不会显示命中的结果。
6.2在先前聚合的基础上,现在这个例子计算了每个州的账户的平均余额(还是按照账户数 量倒序排序的前 10 个州):
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}'
注意,我们把 average_balance 聚合嵌套在了 group_by_state 聚合之中。这是所有聚合的 一个常用模式。你可以任意的聚合之中嵌套聚合,这样你就可以从你的数据中抽取出想要的 概述。
6.3 基于前面的聚合,现在让我们按照平均余额进行排序:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state",
"order": {
"average_balance": "desc"
}
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}'
6.4下面的例子显示了如何使用年龄段(20-29,30-39,40-49)分组,然后在用性别分组,然后为每一个年龄段的每一个性别计算平均账户余额:
"range": {
"field": "age",
"ranges": [
{"from": 20, "to": 30},
{"from": 30,"to": 40 },
{"from": 40, "to": 50}
]
},
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
}
}'
有很多关于聚合的细节,我们没有涉及。如果你想做更进一步的实验, http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html 是 一个非常好的起点。
总结
Elasticsearch 既是一个简单的产品,也是一个复杂的产品。我们现在已经学习到了基础 部分,它的一些原理,以及怎样用 REST API 来做一些工作。我希望这个教程已经使你对
Elasticsearch 是什么有了一个更好的理解,跟重要的是,能够激发你继续实验 Elasticsearch 的其它特性。