elasticsearch入门使用

该文章基于:http://blog.csdn.net/gl775307284/article/details/44731009 整理而来

基础概念

1、接近实时(NRT)
Elasticsearch是一个接近实时的搜索平台。这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒)

2、集群(cluster)
一个集群就是由一个或多个节点组织在一起,它们共同持有你整个的数据,并一起提供索引和搜索功能。
一个集群由一个唯一的名字标识,这个名字默认就是 “elasticsearch”。
这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群。
在产品环境中显式地设定这个名字是一个好习惯,但是使用默认值来进行测试/开发也是不错的。

3、节点(node)
一个节点是你集群中的一个服务器,作为集群的一部分,它存储你的数据,参与集群的索引和搜索功能。
和集群类似,一个节点也是由一个名字来标识的,默认情况 下,这个名字是一个随机的漫威漫画角色的名字,
这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,
你会去确定网 络中的哪些服务器对应于Elasticsearch集群中的哪些节点。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。
默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中,这意 味着,
如果你在你的网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中。
在一个集群里,只要你想,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,
这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。

4、索引(index)
一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。
一个索引由一个名 字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。
在一个集群中,如果你想,可以定义任意多的索引。

5、类型(type)
在一个索引中,你可以定义一种或多种类型。一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。
通常,会为具有一组共同字段的文档定义一个 类型。比如说,我们假设你运营一个博客平台并且将你所有的数据存储到一个索引中。
在这个索引中,你可以为用户数据定义一个类型,为博客数据定义另一个类 型,当然,也可以为评论数据定义另一个类型。

6、文档(document)
一个文档是一个可被索引的基础信息单元。比如,你可以拥有某一个客户的文档,某一个产品的一个文档,
当然,也可以拥有某个订单的一个文档。文档以 JSON(Javascript Object Notation)格式来表示,而JSON是一个到处存在的互联网数据交互格式。
在一个index/type里面,只要你想,你可以存储任意多的文档。注意,尽管一个文档,物理上存在于一个索引之中,文档必须被索引/赋予一个索引的type。

7、分片和复制(shards & replicas)
  • 一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢。    为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。 当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。
  • 分片之所以重要,主要有两方面的原因:

- 允许你水平分割/扩展你的内容容量

- 允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量

  •  至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由Elasticsearch管理的,对于作为用户的你来说,这些都是透明的
  • 在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非 常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。
  • 复制之所以重要,有两个主要原因:

- 在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的。

- 扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行

  • 总之,每个索引可以被分成多个分片。一个索引也可以被复制0次(意思是没有复制)或多次。一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和 复制分片(主分片的拷贝)之别。分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量,但是你事后不能改变 分片的数量。
  • 默认情况下,Elasticsearch中的每个索引被分片5个主分片和1个复制,这意味着,如果你的集群中至少有两个节点,你的索引将会有5个主分片和另外5个复制分片(1个完全拷贝),这样的话每个索引总共就有10个分片。

安装

1、环境:JDK使用java 1.8(安装省略),elasticsearch 2.3.4,window系统

2、下载解压后,在bin目录中运行elasticsearch.bat

此处的节点名称:Vermin

我们可以覆盖集群或者节点的名字。我们可以在启动Elasticsearch的时候通过命令行来指定,如下:
elasticsearch.bat --cluster.name my_cluster_name --node.name my_node_name

3、提供了有关HTTP地址(192.168.8.112)和端口(9200)的信息,通过这个地址和端口我们就可以 访问我们的节点了。
默认情况下,Elasticsearch使用9200来提供对其REST API的访问。如果有必要,这个端口是可以配置的

访问地址:http://127.0.0.1:9200/

利用rest api通信

  • 检查你的集群、节点和索引的健康状态、和各种统计信息
  • 管理你的集群、节点、索引数据和元数据
  • 对你的索引进行CRUD(创建、读取、更新和删除)和搜索操作
  • 执行高级的查询操作,像是分页、排序、过滤、脚本编写(scripting)、小平面刻画(faceting)、聚合(aggregations)和许多其它操作

为了方便在对elasticsearch通信,安装插件sense,由于google被墙,无法访问Google 商店,因此建议使用360浏览器安装sense插件,安装方法(略)

1、集群健康

访问的URL: [html]  view plain  copy
  1. http://127.0.0.1:9200/_cat/health?v  
响应: [html]  view plain  copy
  1. epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent   
  2. 1512722087 16:34:47  elasticsearch green           1         1      0   0    0    0        0             0                  -                100.0%   
  • 集群的名字是“elasticsearch”,正常运行,并且状态是绿色。

(1)当我们询问集群状态的时候,我们要么得到绿色、黄色或红色

绿色代表一切正常(集群功能齐全)

黄色意味着所有的数据都是可用的,但是某些复制没有被分配 (集群功能齐全)

红色则代表因为某些原因,某些数据不可用。注意,即使是集群状态是红色的,集群仍然是部分可用的(它仍然会利用可用的分片来响应搜索请 求),但是可能你需要尽快修复它,因为你有丢失的数据。

(2)从上面的响应中,我们可以看到,一共有一个节点,由于里面没有数据,我们有0个分片。注意,由于我们使用默认的集群名字 (elasticsearch),并且由于Elasticsearch默认使用网络多播(multicast)发现其它节点,如果你在你的网络中启动了多 个节点,你就已经把她们加入到一个集群中了。在这种情形下,你可能在上面的响应中看到多个节点。

2、列出节点列表

访问的URL(GET请求):

[html] view plain copy

  1. http://127.0.0.1:9200/_cat/nodes?v  
响应:

[html] view plain copy

  1. host      ip        heap.percent ram.percent  load node.role master name        
  2. 127.0.0.1 127.0.0.1            7          63 -1.00 d         *      Mary Zero   
从中可以看到节点是Mary Zero

3、列出所有的索引

访问的URL(GET请求):

[html] view plain copy

  1. http://127.0.0.1:9200/_cat/indices?v  
响应:

[html] view plain copy

  1. health status index pri rep docs.count docs.deleted store.size pri.store.size   
从中可以看到还没有索引

4、创建一个索引

创建一个叫“customer”的索引

访问地址,使用sense插件请求(PUT请求):

[html] view plain copy

  1. 127.0.0.1:9200/customer?pretty  
响应:

[html] view plain copy

  1. {  
  2.    "acknowledged": true  
  3. }  
由于加入了pretty在尾部,会打印出美观的json形式

列出所有索引(参考 3)

[html] view plain copy

  1. health status index    pri rep docs.count docs.deleted store.size pri.store.size   
  2. yellow open   customer   5   1          0            0       650b           650b   

这个customer索引有一个黄色健康标签。回顾我们之前的讨论,黄色意味着某些复制没有(或者还未)被分配。这个索引之所以这样,是 因为Elasticsearch默认为这个索引创建一份复制。由于现在我们只有一个节点在运行,那一份复制就分配不了了(为了高可用),直到当另外一个节 点加入到这个集群后,才能分配。一旦那份复制在第二个节点上被复制,这个节点的健康状态就会变成绿色。

索引并查询文档

利用之前创建的customer索引进行相关文档操作,使用sense插件,server:127.0.0.1:9200

首先要知道的是,为了索引一个文档,我们必须告诉Elasticsearch这个文档要到这个索引的哪个类型(type)下。

1、将一个简单的客户文档索引到customer索引、“external”类型中,这个文档的ID是1

[html]  view plain  copy
  1. PUT /customer/external/1?pretty  
  2. {  
  3.     "name":"Johe Done"  
  4. }  
响应: [html]  view plain  copy
  1. {  
  2.    "_index": "customer",  
  3.    "_type": "external",  
  4.    "_id": "1",  
  5.    "_version": 1,  
  6.    "_shards": {  
  7.       "total": 2,  
  8.       "successful": 1,  
  9.       "failed": 0  
  10.    },  
  11.    "created": true  
  12. }  
从上面的响应中,我们可以看到,一个新的客户文档在customer索引和external类型中被成功创建。文档也有一个内部id 1, 这个id是我们在索引的时候指定的。
有一个关键点需要注意,Elasticsearch在你想将文档索引到某个索引的时候,并不强制要求这个索引被显式地创建。
在前面这个例子中,如果customer索引不存在,Elasticsearch将会自动地创建这个索引。

2、取出创建的文档

[html]  view plain  copy
  1. GET /customer/external/1?pretty  

响应: [html]  view plain  copy
  1. {  
  2.    "_index": "customer",  
  3.    "_type": "external",  
  4.    "_id": "1",  
  5.    "_version": 2,  
  6.    "found": true,  
  7.    "_source": {  
  8.       "name": "Johe Done"  
  9.    }  
  10. }  

3、删除创建的索引 [html]  view plain  copy
  1. DELETE /customer?pretty  
响应: [html]  view plain  copy
  1. {  
  2.    "acknowledged": true  
  3. }  
在查看索引(参考 :列出所有索引)

响应:

[html]  view plain  copy
  1. health status index pri rep docs.count docs.deleted store.size pri.store.size   
这表明我们成功地删除了这个索引,现在我们回到了集群中空无所有的状态

4、修改数据

Elasticsearch提供了近乎实时的数据操作和搜索功能。默认情况下,从你索引/更新/删除你的数据动作开始到它出现在你的搜索结果中,

大概会有1秒钟的延迟。这和其它类似SQL的平台不同,数据在一个事务完成之后就会立即可用。

先添加ID:1,name:Johe Done

[html]  view plain  copy
  1. PUT /customer/external/1?pretty  
  2. {  
  3.     "name":"Johe Done"  
  4. }  

在添加ID:2,name:Loren

[html]  view plain  copy
  1. PUT /customer/external/2?pretty  
  2. {  
  3.     "name":"Loren"  
  4. }  
在索引的时候,ID部分是可选的。如果不指定,Elasticsearch将产生一个随机的ID来索引这个文档。Elasticsearch生成的ID会作为索引API调用的一部分被返回。 [html]  view plain  copy
  1. POST /customer/external?pretty  
  2. {  
  3.     "name":"Karrt"  
  4. }  
在上面的情形中,由于我们没有指定一个ID,我们使用的是POST而不是PUT。


5、更新文档

除了可以索引、替换文档之外,我们也可以更新一个文档。但要注意,Elasticsearch底层并不支持原地更新。在我们想要做一次更新的时候,Elasticsearch先删除旧文档,然后在索引一个更新过的新文档。

将name的值修改为Lucene

[html]  view plain  copy
  1. POST /customer/external/1/_update?pretty  
  2. {  
  3.     "doc":{"name":"Lucene"}  
  4. }  

将name的值修改,并同时添加age字段 [html]  view plain  copy
  1. POST /customer/external/1/_update?pretty  
  2. {  
  3.     "doc":{"name":"Lucene", "age":50}  
  4. }  
使用脚本将age加10 [html]  view plain  copy
  1. POST /customer/external/1/_update?pretty  
  2. {  
  3.     "script":"ctx._source.age += 10"  
  4. }  

ctx._source指向当前要被更新的文档。

需要开启允许脚本运行

在elasticsearch.yml配置文件中添加

[html] view plain copy

  1. script.inline: on    
  2. script.indexed: on   
  3. script.engine.groovy.inline.aggs: on   

6、删除文档

删除ID:2的文档

[html] view plain copy

  1. DELETE /customer/external/2?pretty  
通过查询删除多个文档,例如:删除name包含Kerry的文档

[html] view plain copy

  1. DELETE /customer/external/_query?pretty  
  2. {  
  3.     "query":{"match": {  
  4.        "name": "Kerry"  
  5.     }}  
  6. }  
注意:在2.x以上版本默认的的Delete by Query API被移除,需要自己按照插件,原因: https://www.elastic.co/blog/core-delete-by-query-is-a-plugin
在bin目录运行:

[html] view plain copy

  1. plugin.bat install delete-by-query  
 集群环境下必须在每个结点上安装,安装之后要重启结点才能使这个插件生效

7、批处理

除了能够对单个的文档进行索引、更新和删除之外,Elasticsearch也提供了以上操作的批量处理功能,这是通过使用_bulk API实现的。

这个功能之所以重要,在于它提供了非常高效的机制来尽可能快的完成多个操作,与此同时使用尽可能少的网络往返。

批量修改ID:1,ID:2的数据

[html]  view plain  copy
  1. POST /customer/external/_bulk?pretty  
  2. {"index":{"_id":"2"}}  
  3. {"name":"kkkkk"}  
  4. {"index":{"_id":"1"}}  
  5. {"name":"enenen"}  

先修改(ID:1),在删除ID:3

[html]  view plain  copy
  1. POST /customer/external/_bulk?pretty  
  2. {"update":{"_id":"1"}}  
  3. {"doc":{"name":"zhangsan"}}  
  4. {"delete":{"_id":"3"}}  
注意上面的delete动作,由于删除动作只需要被删除文档的ID,所以并没有对应的源文档

bulk API按顺序执行这些动作。如果其中一个动作因为某些原因失败了,将会继续处理它后面的动作。当bulk API返回时,它将提供每个动作的状态(按照同样的顺序),所以你能够看到某个动作成功与否。

例子学习

1、样本数据集

以客户的银行账户信息的JSON文档的样本。文档具有以下的模式(schema)

[javascript]  view plain  copy
  1. {  
  2.     "account_number": 0,  
  3.     "balance": 16623,  
  4.     "firstname": "Bradshaw",  
  5.     "lastname": "Mckenzie",  
  6.     "age": 29,  
  7.     "gender": "F",  
  8.     "address": "244 Columbus Place",  
  9.     "employer": "Euron",  
  10.     "email": "[email protected]",  
  11.     "city": "Hobucken",  
  12.     "state": "CO"  
  13. }  

2、载入样本数据

样本数据获取:https://github.com/bly2k/files/blob/master/accounts.zip?raw=true

本人不知道如何使用sense读取本地json文件,因此使用GUI bash工具在windows使用curl命令

[javascript]  view plain  copy
  1. curl -XPOST '127.0.0.1:9200/bank/account/_bulk?pretty' --data-binary @accounts.json  
查看索引 [javascript]  view plain  copy
  1. GET /_cat/indices?v  
[html]  view plain  copy
  1. health status index    pri rep docs.count docs.deleted store.size pri.store.size   
  2. yellow open   bank       5   1       1000            0    442.1kb        442.1kb   
  3. yellow open   customer   5   1          3            0      9.4kb          9.4kb   
 

3、搜索API

通过‘_search’来查询调用,并且q=*参数指示Elasticsearch去匹配这个索引中所有的文档。pretty参数,和以前一样,仅仅是告诉Elasticsearch返回美观的JSON结果

[html]  view plain  copy
  1. GET /bank/_search?q=*&pretty  
部分响应: [html]  view plain  copy
  1. {  
  2.    "took": 58,  
  3.    "timed_out": false,  
  4.    "_shards": {  
  5.       "total": 5,  
  6.       "successful": 5,  
  7.       "failed": 0  
  8.    },  
  9.    "hits": {  
  10.       "total": 1000,  
  11.       "max_score": 1,  
  12.       "hits": [  
  13.          {  
  14.             "_index": "bank",  
  15.             "_type": "account",  
  16.             "_id": "25",  
  17.             "_score": 1,  
  18.             "_source": {  
  19.                "account_number": 25,  
  20.                "balance": 40540,  
  21.                "firstname": "Virginia",  
  22.                "lastname": "Ayala",  
  23.                "age": 39,  
  24.                "gender": "F",  
  25.                "address": "171 Putnam Avenue",  
  26.                "employer": "Filodyne",  
  27.                "email": "[email protected]",  
  28.                "city": "Nicholson",  
  29.                "state": "PA"  
  30.             }  
  31.          },  
  32.          {  
  33.             "_index": "bank",  
  34.             "_type": "account",  
  35.             "_id": "44",  
  36.             "_score": 1,  
  37.             "_source": {  
  38.                "account_number": 44,  
  39.                "balance": 34487,  
  40.                "firstname": "Aurelia",  
  41.                "lastname": "Harding",  
  42.                "age": 37,  
  43.                "gender": "M",  
  44.                "address": "502 Baycliff Terrace",  
  45.                "employer": "Orbalix",  
  46.                "email": "[email protected]",  
  47.                "city": "Yardville",  
  48.                "state": "DE"  
  49.             }  
  50.          },  
  51.          {  
  52.             "_index": "bank",  
  53.             "_type": "account",  
  54.             "_id": "99",  
  55.             "_score": 1,  
  56.             "_source": {  
  57.                "account_number": 99,  
  58.                "balance": 47159,  
  59.                "firstname": "Ratliff",  
  60.                "lastname": "Heath",  
  61.                "age": 39,  
  62.                "gender": "F",  
  63.                "address": "806 Rockwell Place",  
  64.                "employer": "Zappix",  
  65.                "email": "[email protected]",  
  66.                "city": "Shaft",  
  67.                "state": "ND"  
  68.             }  
  69.          },  
响应说明:

took:elasticsearch执行这个搜索的耗时,以毫秒为单位

timed_out:指明这个搜索是否超时

_shards:指出多少个分片被搜索,同时也指出成功/失败的被搜索的shareds数量

hits:搜索的结果

hits.total:能够匹配搜索出文档的总数

hits.hits:真正搜索的数据,默认显示10条

max_score和_score:先忽略

另一种等价的方式:

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match_all": {}}  
  4. }  

这里的不同之处在于,并不是向URI中传递q=*,取而代之的是,我们在_search API的请求体中POST了一个JSON格式请求体

有一点需要重点理解一下,一旦你取回了你的搜索结果,Elasticsearch就完成了使命,它不会维护任何服务器端的资源或者在你的结果中打开游标。 这是和其它类似SQL的平台的一个鲜明的对比, 在那些平台上,你可以在前面先获取你查询结果的一部分,然后如果你想获取结果的剩余部分,你必须继续返回服务端去取,这个过程使用一种有状态的服务器端游 标技术。

4、介绍查询语言

Elasticsearch提供一种JSON风格的特定领域语言,利用它你可以执行查询。这被称为查询DSL。

[html]  view plain  copy
  1. {  
  2.     "query": {"match_all": {}}  
  3. }  
分解上面的这个查询,其中的query部分告诉我查询的定义,match_all部分就是我们想要运行的查询的类型。match_all查询,就是简单地查询一个指定索引下的所有的文档。

除了这个query参数之外,我们也可以通过传递其它的参数来影响搜索结果

例如只返回一个文档

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match_all": {}},  
  4.     "size": 1  
  5. }  
size如果没有指定,默认值是10

返回第11到20的文档

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match_all": {}},  
  4.     "from": 11,   
  5.     "size": 10  
  6. }  
其中的from参数(0-based)从哪个文档开始,size参数指明从from参数开始,要返回多少个文档。这个特性对于搜索结果分页来说非常有帮助。注意,如果不指定from的值,它默认就是0

下面的例子:

搜索所有的账户并余额降序排序,返回前10个文档

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match_all": {}},  
  4.     "sort": [  
  5.        {  
  6.           "balance": {  
  7.              "order": "desc"  
  8.           }  
  9.        }  
  10.     ]  
  11. }  

5、执行搜索

现在我们已经知道了几个基本的参数,让我们进一步发掘查询语言吧。首先我们看一下返回文档的字段。默认情况下,是返回完整的JSON文档的。这可以通过 source来引用(搜索hits中的_source字段)。如果我们不想返回完整的源文档,我们可以指定返回的几个字段。

返回指定的account_number和balance两个 字段

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match_all": {}},  
  4.     "_source":["account_number", "balance"]  
  5. }  
上面的例子仅仅是简化了_source字段。它仍将会返回一个叫做_source的字段,但是仅仅包含account_number和balance来年改革字段

我们看到了match_all查询是怎样匹配到所有的文档的。现在我们介绍一种新的查询,叫做match查询,这可以看成是一个简单的字段搜索查询(比如对应于某个或某些特定字段的搜索)

查询account_number为44的文档

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match": {  
  4.        "account_number": 44  
  5.     }}  
  6. }  
搜索地址包含mill的文档 [html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match": {  
  4.        "address": "mill"  
  5.     }}  
  6. }  
搜索地址包含mill或者lane的文档 [html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {"match": {  
  4.        "address": "mill lane"  
  5.     }}  
  6. }  

下面这个例子是match的变体(match_phrase),它会去匹配短语“mill lane”: [html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {  
  4.         "match_phrase": {  
  5.            "address": "mill lane"  
  6.         }  
  7.     }  
  8. }  

介绍一下布尔查询。布尔查询允许我们利用布尔逻辑将较小的查询组合成较大的查询 [html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {  
  4.         "bool": {  
  5.             "must": [  
  6.                {"match": {  
  7.                   "address": "mill"  
  8.                }},  
  9.                {"match": {  
  10.                   "address": "lane"  
  11.                }}  
  12.             ]  
  13.         }  
  14.     }  
  15. }  

这个例子组合了两个match查询,这个组合查询返回必须包含“mill”和“lane”的所有的账户

bool must语句指明了,对于一个文档,所有的查询都必须为真,这个文档才能够匹配成功

相反的,下面的例子组合了两个match查询,它返回的是地址中包含“mill”或者“lane”的所有的账户

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {  
  4.         "bool": {  
  5.             "should": [  
  6.                {"match": {  
  7.                   "address": "mill"  
  8.                }},  
  9.                {"match": {  
  10.                   "address": "lane"  
  11.                }}  
  12.             ]  
  13.         }  
  14.     }  
  15. }  
bool should语句指明,对于一个文档,查询列表中,只要有一个查询匹配,那么这个文档就被看成是匹配的

下面这个例子组合了两个查询,它返回地址中既不包含“mill”,同时也不包含“lane”的所有的账户信息

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {  
  4.         "bool": {  
  5.             "must_not": [  
  6.                {"match": {  
  7.                   "address": "mill"  
  8.                }},  
  9.                {"match": {  
  10.                   "address": "lane"  
  11.                }}  
  12.             ]  
  13.         }  
  14.     }  
  15. }  
在上面的例子中, bool must_not语句指明,对于一个文档,查询列表中的的所有查询都必须都不为真,这个文档才被认为是匹配的。
我们可以在一个bool查询里一起使用must、should、must_not。此外,我们可以将bool查询放到这样的bool语句中来模拟复杂的、多等级的布尔逻辑。


下面的列子搜索出,age=40并且state不等于PA的文档

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {  
  4.         "bool": {  
  5.             "must": [  
  6.                {"match": {  
  7.                   "age": 40  
  8.                }}  
  9.             ],  
  10.             "must_not": [  
  11.                {"match": {  
  12.                   "state": "PA"  
  13.                }}  
  14.             ]  
  15.         }  
  16.     }  
  17. }  

6、执行过滤器

在先前的章节中,我们跳过了文档得分的细节(搜索结果中的_score字段)。这个得分是与我们指定的搜索查询匹配程度的一个相对度量。得分越高,文档越相关,得分越低文档的相关度越低。


Elasticsearch中的所有的查询都会触发相关度得分的计算。对于那些我们不需要相关度得分的场景下,Elasticsearch以过滤器的形式 提供了另一种查询功能。过滤器在概念上类似于查询,但是它们有非常快的执行速度,这种快的执行速度主要有以下两个原因

  • 过滤器不会计算相关度的得分,所以它们在计算上更快一些
  • 过滤器可以被缓存到内存中,这使得在重复的搜索查询上,其要比相应的查询快出许多

为了理解过滤器,我们先来介绍“被过滤”的查询,这使得你可以将一个查询(像是match_all,match,bool等)和一个过滤器结合起来。

作为一个例子,我们介绍一下范围过滤器,它允许我们通过一个区间的值来过滤文档。这通常被用在数字和日期的过滤上。

使用一个被过滤的查询,其返回值是越在20000到30000之间(闭区间)的账户。换句话说,我们想要找到越大于等于20000并且小于等于30000的账户

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "query": {  
  4.         "filtered": {  
  5.            "query": {"match_all": {}},  
  6.            "filter": {  
  7.                "range": {  
  8.                   "balance": {  
  9.                      "gte": 20000,  
  10.                      "lte": 30000  
  11.                   }  
  12.                }  
  13.            }  
  14.         }  
  15.     }  
  16. }  

分解上面的例子,被过滤的查询包含一个match_all查询(查询部分)和一个过滤器(filter部分)。我们可以在查询部分中放入其他查询,在 filter部分放入其它过滤器。在上面的应用场景中,由于所有的在这个范围之内的文档都是平等的(或者说相关度都是一样的),没有一个文档比另一个文档 更相关,所以这个时候使用范围过滤器就非常合适了。
通常情况下,要决定是使用过滤器还是使用查询,你就需要问自己是否需要相关度得分。如果相关度是不重要的,使用过滤器,否则使用查询。如果你有SQL背 景,查询和过滤器在概念上类似于SELECT WHERE语句, although more so for filters than queries
除了match_all, match, bool,filtered和range查询,还有很多其它类型的查uxn/过滤器,我们这里不会涉及。由于我们已经对它们的工作原理有了基本的理解,将其应用到其它类型的查询、过滤器上也不是件难事。
 

6、执行聚合

聚合提供了分组并统计数据的能力。理解聚合的最简单的方式是将其粗略地等同为SQL的GROUP BY和SQL聚合函数。

在Elasticsearch中,你可以在一个响应中同时返回命中的数据和聚合结果。

你可以使用简单的API同时运行查询和多个聚 合,并以一次返回,这避免了来回的网络通信,这是非常强大和高效的。

按照state分组,按照州名的计数倒序排序

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "size": 0,  
  4.     "aggs":{  
  5.         "group_by_state":{  
  6.             "terms":{  
  7.                 "field":"state"  
  8.             }  
  9.         }  
  10.     }  
  11. }  
在SQL中,上面的聚合在概念上类似于:
   SELECT COUNT(*) from bank GROUP BY state ORDER BY COUNT(*) DESC
 

注意我们将size设置成0,这样我们就可以只看到聚合结果了,而不会显示命中的结果

这个例子计算了每个州的账户的平均余额(还是按照账户数量倒序排序的前10个州)

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "size": 0,  
  4.     "aggs":{  
  5.         "group_by_state":{  
  6.             "terms":{  
  7.                 "field":"state"  
  8.             },  
  9.             "aggs":{  
  10.                 "average_balance": {  
  11.                     "avg": {  
  12.                         "field": "balance"  
  13.                     }  
  14.                 }  
  15.             }  
  16.         }  
  17.     }  
  18. }  
注意,我们把average_balance聚合嵌套在了group_by_state聚合之中。这是所有聚合的一个常用模式。你可以任意的聚合之中嵌套聚合,

这样你就可以从你的数据中抽取出想要的概述。

现在让我们按照平均余额进行排序:

[html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "size": 0,  
  4.     "aggs":{  
  5.         "group_by_state":{  
  6.             "terms":{  
  7.                 "field":"state",  
  8.                 "order": {  
  9.                     "average_balance":"desc"  
  10.                 }  
  11.             },  
  12.             "aggs": {  
  13.                 "average_balance": {  
  14.                     "avg": {  
  15.                         "field": "balance"  
  16.                     }  
  17.                 }  
  18.             }  
  19.         }  
  20.     }  
  21. }  

使用年龄段(20-29,30-39,40-49)分组,然后在用性别分组,然后为每一个年龄段的每一个性别计算平均账户余额 [html]  view plain  copy
  1. POST /bank/_search?pretty  
  2. {  
  3.     "size": 0,  
  4.     "aggs": {  
  5.         "group_by_age": {  
  6.             "range": {  
  7.                 "field": "age",  
  8.                 "ranges":[  
  9.                     {  
  10.                         "from":20, "to":30  
  11.                     },  
  12.                     {  
  13.                         "from":30, "to":40  
  14.                     },  
  15.                     {  
  16.                         "from":40, "to":50  
  17.                     }  
  18.                 ]  
  19.             },  
  20.              "aggs": {  
  21.                 "group_by_gender": {  
  22.                     "terms": {  
  23.                         "field": "gender"  
  24.                     },  
  25.                     "aggs": {  
  26.                         "average_balance": {  
  27.                             "avg": {  
  28.                                 "field": "balance"  
  29.                             }  
  30.                         }  
  31.                     }  
  32.                 }  
  33.             }  
  34.         }  
  35.     }  
  36. }  

很多关于聚合的细节,我们没有涉及。如果你想做更进一步的实验,

http://www.elasticsearch.org/guide/en /elasticsearch/reference/current/search-aggregations.html是一个非常好的起点。

 

猜你喜欢

转载自my.oschina.net/u/3803405/blog/1820994