网站首页 > 博客文章 正文
样本数据集
现在我们已经学习了一些基本知识,让我们尝试一下更加真实的数据集。 我准备了一个关于银行账户信息的虚构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" }
这些数据是使用www.json-generator.com/生成的,所以请忽略数据的实际值和语义,因为这些数据都是随机生成的。
加载样本数据集
你可以从这里下载样本数据集(accounts.json).将其解压到我们的当前目录,并将其加载到我们的集群中,如下所示:
curl -H "Content-Type: application/json" -XPOST 'localhost:9200/bank/account/_bulk?pretty&refresh' --data-binary "@accounts.json" curl 'localhost:9200/_cat/indices?v'
其响应为:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open bank l7sSYV2cQXmu6_4rJWVIww 5 1 1000 0 128.6kb 128.6kb
上述响应表明:我们刚刚成功地将1000个文档批量索引到了bank索引的account类型中.
Search API
现在我们来做一些简单搜索.有两种基本方式可执行搜索:一种是通过REST请求URI发送搜索参数,另一种是通过REST请求主体发送搜索参数。请求主体方法允许你使用更具表现力,更具可读性的JSON格式来定义搜索. 我们先尝试一个使用请求URI方法的示例,但在本教程的剩余部分中,我们将专注使用请求主体方法。
用于搜索的REST API可从_search端点访问。 下面的示例展示了如何返回bank索引中的所有文档:
GET /bank/_search?q=*&sort=account_number:asc&pretty
我们首先解析搜索调用。 我们在bank索引中搜索(_search端点),并且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": "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 – 实际的搜索结果数组(默认为前10个文档)
- hits.sort - 结果排序键(若按分数(score)进行排序则会丢失)
- hits._score和max_score - 现在可忽略这些字段
下面是使用请求主体方法进行相同搜索的示例:
GET /bank/_search { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] }
这里的区别是,我们在_search API中POST了一个JSON风格的请求主体来代替URL中的q=*参数。
在下一节中,我们将讨论这种JSON查询.
需要重点理解的是,一旦你得到了搜索结果,Elasticsearch就完成了请求,它不会维护维护任何形式的服务端资源或针对结果的打开游标.这与许多其他平台(如SQL)形成了鲜明对比,在其它平台中,您最初可能会先获取查询结果的部分子集,然后再通过某种有状态的服务端游标来获取结果集的剩余部分。
Query语言介绍
Elasticsearch提供了一种可用来执行查询的JSON风格语言(称为Query 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查询,并返回文档11到20之间的文档:
GET /bank/_search { "query": { "match_all": {} }, "from": 10, "size": 10 }
from参数(从0开始)用于指定从哪个文档开始检索,size参数指定从from参数开始返回多少个文档。
在实现分页搜索结果时,此功能非常有用。 请注意,如果from不指定,则默认为0。
下面这个示例会执行match_all查询,并按帐户余额进行降序排序,并返回前10个(默认大小)文档。
GET /bank/_search { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } }
执行搜索
现在我们已经看到了一些基本的搜索参数,让我们再深入查询DSL。我们先来看看返回的文档字段。 默认情况下,完整的JSON文档作为所有搜索的一部分返回。 这被称为源(搜索匹配中的_source字段)。 如果我们不希望返回整个源文档,我们可以只返回源内的几个字段。
下面这个示例显示如何从搜索中返回两个字段account_number和balance(在_source之内):
GET /bank/_search { "query": { "match_all": {} }, "_source": ["account_number", "balance"] }
注意,上面的例子只是简单地减少了_source字段. 它仍然只会返回一个名为_source的字段,但在其中只包含字段account_number和balance。
如果您有SQL背景,则上述内容在概念上与SQL SELECT FROM字段列表有些相似。
现在我们来看看query部分.在这之前,我们已经看到了如何使用match_all来查询所有文档.
现在我们将介绍一种新的查询-match query, 可将其视为基本的字段搜索查询(即针对特定字段或字段集合进行的搜索).
下面这个例子会返回帐号为20的文档:
GET /bank/_search { "query": { "match": { "account_number": 20 } } }
下面这个示例会返回address中包含term为“mill”的所有帐户:
GET /bank/_search { "query": { "match": { "address": "mill" } } }
下面这个示例会返回address中包含术语"mill" 或"lane"的所有帐户:
GET /bank/_search { "query": { "match": { "address": "mill lane" } } }
这个例子是match(match_phrase)的一个变体,它会返回address中包含短语“mill lane”的所有账号:
GET /bank/_search { "query": { "match_phrase": { "address": "mill lane" } } }
接下来我们介绍bool query.bool query允许我们使用布尔逻辑将较小的查询组合成更大的查询。
下面这个示例由两个match查询所组成,它会返回address中包含“mill”和“lane”的所有帐户:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中, bool must 条件指明了所有查询条件必须为true时才算文档匹配.
相反,下面的示例也由两个match查询所组成,但只要adress中任意包一个 "mill"或"lane",就可视文档匹配:
GET /bank/_search { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,bool should 条件指明了只要文档满足任意一个条件,就可以视其为匹配文档.
下面的示例也由两个match查询所组成,但它会返回address中既不包含"mill"也不包含"lane"的所有文档:
GET /bank/_search { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的例子中,must_not 条件指明了任意一个查询条件均不为true时,就视文档为匹配.
我们可以在一个bool查询中同时结合must,should和must_not子句。此外,我们可以在这些bool子句中编写的布尔查询来模拟任何复杂的多级布尔逻辑。
下面这个例子会返回岁数为40,但不住在爱达荷州的所有帐户:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } }
执行过滤器
在上一节中,我们跳过了一个称为文档分数(document score,搜索结果中的_score字段)的相关细节.
得分是一个数字值,它代表的是文档与搜索词的匹配度。 分数越高,文档越相关,分数越低,文档就越不相关.但查询并非总是需要分数,尤其是用于过滤文档集时. Elasticsearch检测到这种情况后会自动对查询进行优化,以便不计算这些无用分数.
在上一节中我们介绍bool query时,还提到它可支持过滤条件,它允许使用查询来限制将被其他子句匹配的文档,而不改变计算得分的方式. 作为示例,我们将介绍一下range query,该查询允许我们通过范围值来过滤文档. 通常我们会使用数字或日期来过滤文档.
下面的示例会使用bool查询来返回余额介于20000到30000的帐户.
换句话说,我们想查询余额大于等于20000且小于等于30000的所有帐户.
GET /bank/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } }
解析一下上述查询, bool 查询包含了一个match_all查询(query部分) 和一个range查询(filter部分).
虽然我们也可以使用其它查询来替换query和filter部分,但在上面的示例中,range查询是非常有意义的,因为可通过范围来缩小文档匹配范围。
除了match_all, match, bool, 和range查询外,还有其它类型查询。 由于我们已经对其工作原理有了基本的了解,所以将这些知识应用于其他查询类型的学习和实验并不难。
执行聚合
聚合提供了对数据进行分组和统计的能力。 理解聚合的最简单方法是将其视为SQL中的GROUP BY和聚合函数。 在Elasticsearch中,您可以执行搜索来返回命中文档,还可以在一个响应中同时返回命中文档的聚合结果。 这是非常强大和高效的,因为您可以运行多个查询和多个聚合,并使用一个简洁的API来避免网络往返,从而一次性获得两个(或二者之一)操作的结果。
下面的例子首先会按state对帐户进行分组, 然后再按计数大小降序返回前10个洲(默认按降序排列):
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" } } } }
在SQL中,上面的聚合在概念上类似于:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC
其响应如下(部分显示):
{ "took": 29, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1000, "max_score": 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个账户,其次是德克萨斯州的27个账户,其次是阿拉巴马州的25个账户,等等。请注意,我们将size设置为0来不显示命中文档,因为我们只想看到响应中的聚合结果。
在前面的聚合基础上,下面的示例会按state计算平均账户余额:
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }
注意我们是如何在group_by_state 聚合中嵌入average_balance聚合的.这是所有聚合的通用模式。 你可以在聚合中任意地嵌套聚合, 以便从数据中提取所要的聚合结果.
基于前面的聚合,下面我们将按平均余额降序来排序结果:
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" } } } } } } } }
还有很多其它的聚合功能,在这里就不详细介绍了。
如果你想进一步地实验, 聚合参考手册则是很好的起点。
总结
Elasticsearch是一个既简单又复杂的产品。现在我们学习了其基础知识,并学会了如何使用REST APIs来进行相关操作.
希望本教程能够让您更好地理解Elasticsearch是什么,更重要的是:启发您进一步尝试其它的伟大功能!
猜你喜欢
- 2024-10-24 持久层框架JPA与Mybatis该如何选型
- 2024-10-24 hibatis--mybatis,自动生成SQL, 通用Mapper, 通用Dao
- 2024-10-24 Elasticsearch 6.0.0官方参考指南翻译
- 2024-10-24 推荐一个高效美观易用的服务器运维工具
- 2024-10-24 Java 操作之RestHighLevelClient查询详解
- 2024-10-24 如何在 Elasticsearch 上应用机器学习排序插件
- 2024-10-24 「开源资讯」Apache Solr 8.6.0 发布,Java 全文搜索服务器
- 2024-10-24 最轻量级的Kubernetes云原生日志框架Loki
- 2024-10-24 ElasticSearch学习系列 - (3) Python操作es
- 2024-10-24 全功能orm工具sorms 1.0.10 发布,合使用Spring,Spring boot用户
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)