ElasticSearch初始化数据
既然我们已经对基础有了一定了解,就让我们在更真实的数据集上做一些练习吧。我准备了一些客户银行账户信息的JSON格式的假数据。每个文档都有如下所示的结构:
{"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"}
数据可以在http://www.json-generator.com/ 上生成
加载样本数据集
你可以在这里下载这个样本数据集(accounts.json)。 解压到当前目录,然后使用下面的命令把这些数据载入咱们的集群中:curl -H "Content-Type: application/json" -XPOST '192.168.1.171:9200/bank/account/_bulk?pretty&refresh' --data-binary "@accounts.json"curl '192.168.1.171:9200/_cat/indices?v'
返回:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.sizeblue open bank -_IHCJWAQ06D7E-wPpL19A 5 1 1000 0 640.1kb 640.1kbblue open customer V0DAWcw3TGSeaefMqvGOIg 5 1 3 0 10.4kb 10.4kb
上面的结果意味着我们刚刚成功批量为1000个文档在bank索引下建立了account类型的索引。
数据文件如下:
ElasticSearch 搜索类型
有两种运行搜索的基本方法:一是通过REST request URI发送搜索参数,另一个是通过REST request body发送。(即:一个是地址连参数,一个是在BODY中走POST)
通过request body的方式你将有更大表达空间,可以以可读性更强的JSON格式类定义你的查询条件。我们只会用request URI的方式做一个简单的示例,然后在剩余的教程过程中我们将使用request body方式。
用于搜索的REST API可以通过_search 服务来访问。下面的示例返回所有在bank索引下的文档:
GET /bank/_search?q=*&sort=account_number:asc&pretty
{"took" : 63,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"failed" : 0},"hits" : {"total" : 1000,"max_score" : null,"hits" : [ {"_index" : "bank","_type" : "account","_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" : "account","_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 - 实际搜索结果(默认前十条文档记录) >hits.sort - 结果的排序键值(不指定排序的话则默认按照score排序) >hits._score和max_score - 暂时忽略这些字段下面是使用request body方式的同等搜索的替代方式:
POST /bank/_search{"query": { "match_all": {} },"sort": [{ "account_number": "asc" }]}
差别在于我们没有在URI中使用参数q=*,取而代之的是在请求_search API的body中以post方式提交JSON格式的检索条件。我们会在下一章节讨论这个JSON查询的细节。
理解这一点很重要,那就是一旦搜索结果返回,Elasticearch便完成了请求且不再维护(占用)服务器端资源或者搜索结果的指针。相对于其他的平台例如SQL这是个完全的差异,你可能会先获取结果的一部分子集显示在前端,然后使用某种服务器端有状态的指针的东西从服务器端获取剩余的结果。
ElasticSearch POST 查询语句
Elasticsearch提供了一种JSON样式的领域特定语言,你可以用他来执行查询。被称为:。该查询语言极其的复杂并且第一样看上去会令人心生敬畏。然而学习它最好的方式是现在一些简单的例子开始。
回到我们之前的案例,我们执行下面的查询:
POST /bank/_search{"query": { "match_all": {} }}
或者:
curl -XPOST 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d'{"query": { "match_all": {} }}
分析上面命令,query告知我们查询定义,match_all指定我们打算运行的查询类型。match_all就是在指定的索引中查询所有的文档。
除了query参数,我们也可以传递其他的参数来影响查询结果。在之前的额例子中我们使用过sort参数,现在我们传入size参数:
POST /bank/_search{"query": { "match_all": {} },"size": 1}
或者
curl -XPOST 'localhost:9200/bank/_search?pretty' -H 'Content-Type: application/json' -d'{"query": { "match_all": {} },"size": 1}
如果不指定size,则默认值为10.
下面的例子将在bank索引中匹配所有的文档,并返回第11到第20条文档。
POST /bank/_search{"query": { "match_all": {} },"from": 10,"size": 10}
from参数(从0开始)指定分页的起始序号,size参数指定了从from开始返回多少条文档。这个功能对于实现结果的分页是很有帮助的。注意如果没有指定from的值,那么默认是0.
下面的例子从按照account字段降序排列所有的文档中查询10条文档。
POST /bank/_search{"query": { "match_all": {} },"sort": { "balance":"desc" } }
ElasticSearch match bool(ean)
现在我们已经看过了基础搜索参数,让我们深入挖掘一下Query DSL。首先让我们看一下返回文档的字段。默认情况下,搜索返回的是全量JSON文档。这称之为source(搜索返回值中hits的_source字段)。如果我们不想返回整个source文档,我们是可以请求只返回source中的某些字段的。
下面的例子展示了如何返回两个字段account_number和 balance(_source中的两个字段):POST /bank/_search{"query": { "match_all": {} },"_source": ["account_number", "balance"]}
match查询,这是一个用于基础字段检索的条件关键字。(例如从指定的一个或一组字段中检索)。
下面的例子查询account的值等于20的文档:POST /bank/_search{"query": { "match": { "account_number": 20 } }}
下面的例子返回所有的address包含mill或lane的account记录:
POST /bank/_search{"query": { "match": { "address": "mill lane" } }}
下面的例子是match的变种(match_phrase), 其将返回所有的address字段包含“mill lane”的accounts记录:
POST /bank/_search{"query": { "match_phrase": { "address": "mill lane" } }}
现在我们来介绍bool(ean)布尔查询。bool查询允许我们使用布尔逻辑将小的查询组成大的(复杂的)的查询。
下面的例子组合了两个match查询,用于返回address字段包含mill和lane的accounts记录:POST /bank/_search{ "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } }}
上面例子中的bool must表示所有的match条件必须都满足才被认为匹配了查询。
相比之下,下面的例子组合了两个match查询并返回address中包含mill或者lane的account记录:POST /bank/_search{ "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } }}
上面的例子中,bool should语句指定一组查询条件,任何一个条件满足,对应文档机会被作为匹配记录返回。
下面的例子组合了两个match查询,用于返回不包含mill并且不包含lane的account记录:
POST /bank/_search{ "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } }}
上面的例子中,bool must_not所指定的条件中,任何一个都必须为false,对应的文档才被认为是匹配的记录。
我们可以在一个bool查询内部同时组合must,should以及must_not语句。而且我们可以在任何bool语句中包含bool查询来模拟复杂了多层级bool逻辑。
下面的例子返回所有年龄为40岁并且state不为ID的account记录:
POST /bank/_search{ "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } }}
ElasticSearch Filter过滤查询
在前边的部分,我们跳过了一个叫做document score(搜索结果中的_score字段)。分数(score)是一个数值,它是文档与我们指定的搜索查询匹配程度的相对度量值。分数越高,文档越相关,得分越低,文档的相关性越小。
但查询并不总是需要产生分数,特别是当它们只用于“过滤”文档集时。Elasticsearch检测这些情况并自动优化查询执行为了不计算无用的分数。
我们在上一节中介绍的布尔查询也支持筛选字句,允许在不适用分数计算的情况下,使用查询限制被其他字句匹配的文档。作为一个例子,让我们引入范围查询,这使我们能过根据值得范围来过滤文档。通常用于过滤数字或者日期。
这个例子使用布尔查询返回所有balances介于20000和30000之间的accounts。换句话讲,我们想要找出所有balance大于等于20000并且小于等于30000的accounts记录。
POST /bank/_search{ "query": { "bool": { "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } }, "must": { "match_all": {} } } }}
分析以上,布尔查询包含一个match_all查询(查询部分)和一个范围查询(过滤部分)。我们可以用任何其他查询替换查询(query)和过滤(filter)部分。在上述情况下,范围查询是完全有道理的,因为所有的文档都在范围内,没有哪个文档比其他更相关(结果内的文档的相关度是等同的)。
除了match_all, match, bool以及range查询,还有其他的可用的查询类型,我们在此不再详解。因为我们已经对于他们的工作原理有了基础的了解,那么将这些知识应用于其他类型的查询并非难事。