第四讲—RESTful API探索(二)

本节大部分内容转载至(

         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)分组,然后在用性别分组,然后为每一个年龄段的每一个性别计算平均账户余额: 

curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' {

        "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"

                                        },

                                        "aggs": {

                                                "average_balance": {

                                                        "avg": {
                                                                "field": "balance"

                                                         }

                                                }

                                         }

                                 }

                         }

                 }

         }

}'

有很多关于聚合的细节,我们没有涉及。如果你想做更进一步的实验, http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations.html 是 一个非常好的起点。 

 

总结

        Elasticsearch 既是一个简单的产品,也是一个复杂的产品。我们现在已经学习到了基础 部分,它的一些原理,以及怎样用 REST API 来做一些工作。我希望这个教程已经使你对

Elasticsearch 是什么有了一个更好的理解,跟重要的是,能够激发你继续实验 Elasticsearch 的其它特性。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值