聚合aggregations
ElasticSearch中的有一个功能叫做聚合aggregations
它允许你在数据上生成复杂的分析统计。它很像SQL中的GROUP BY但是功能更强大。
简单的高亮显示
rpt_indicates_v1/day/_search
{
"query":{
"match_phrase":{
"gcname":"豪爽客中西餐厅"
}
},
"highlight":{
"fields":{
"gcname":{}
}
}
}
统计所有的市别名称
{
"aggs":{
"all_shift_name":{
"terms":{"field":"settle_shift_name"}
}
}
}
部分结果
{
"aggregations":{
"all_shift_name":{
"doc_count_error_upper_bound":12807,
"sum_other_doc_count":749897,
"buckets":[
{
"key":"晚市",
"doc_count":13935863
},
{
"key":"午市",
"doc_count":12694628
},
{
"key":"早市",
"doc_count":2954679
},
{
"key":"通市",
"doc_count":677642
},
{
"key":"夜宵",
"doc_count":354366
},
{
"key":"夜市",
"doc_count":342317
},
{
"key":"午餐",
"doc_count":286941
},
{
"key":"晚餐",
"doc_count":282559
},
{
"key":"下午茶",
"doc_count":255826
},
{
"key":"早餐",
"doc_count":170144
}
]
}
}
}
可以指定和查询语句相匹配的文档了
{
"query":{
"match":{
"gcname":"豪爽客中西餐厅"
}
},
"aggs":{
"all_shift_name":{
"terms":{"field":"settle_shift_name"}
}
}
}
查询结果,这样我们查到的就是match条件下的市别名称了
{
"aggregations":{
"all_shift_name":{
"doc_count_error_upper_bound":0,
"sum_other_doc_count":0,
"buckets":[
{
"key":"晚餐",
"doc_count":80398
},
{
"key":"午餐",
"doc_count":76918
},
{
"key":"下午茶",
"doc_count":75138
},
{
"key":"晚市",
"doc_count":585
}
]
}
}
}
可以指定和查询语句相匹配的文档了,
并且在此基础上再次指定一个平均值聚集函数
{
"query":{
"match":{
"gcname":"豪爽客中西餐厅"
}
},
"aggs":{
"all_shift_name":{
"terms":{
"field":"settle_shift_name"
},
"aggs":{
"avg_amount":{
"avg":{
"field":"amount"
}
}
}
}
}
}
查询的结果
{
"aggregations":{
"all_shift_name":{
"doc_count_error_upper_bound":0,
"sum_other_doc_count":0,
"buckets":[
{
"key":"晚餐",
"doc_count":80398,
"avg_amount":{
"value":408.714127846584
}
},
{
"key":"午餐",
"doc_count":76918,
"avg_amount":{
"value":179.26644833313318
}
},
{
"key":"下午茶",
"doc_count":75138,
"avg_amount":{
"value":179.1789019303361
}
},
{
"key":"晚市",
"doc_count":585,
"avg_amount":{
"value":0
}
}
]
}
}
}
检索文档
rpt_indicates_v1/day/27_2394_239200000000000002_20161011?pretty
输出结果
{
"_index": "rpt_indicates_v1",
"_type": "day",
"_id": "27_2394_239200000000000002_20161011",
"_version": 1,
"found": true,
"_source": {
"biz_date": "2016-10-11",
"mcid": 2394,
"amount": null,
"type_id": 27,
"mcname": "豪爽客中西餐厅(青阳中天花园店)",
"gcid": 2392,
"count": null,
"remark": "",
"detail_id": "27_2394_239200000000000002_20161011",
"gcname": "豪爽客中西餐厅",
"settle_shift_id": 239200000000000000,
"settle_shift_name": "下午茶",
"value": "",
"timestamp": "2017-11-05 03:18:12"
}
}
检索部分文档
rpt_indicates_v1/day/27_2394_239200000000000002_20161011?_source=gcname,gcid
输出结果
{
"_index": "rpt_indicates_v1",
"_type": "day",
"_id": "27_2394_239200000000000002_20161011",
"_version": 1,
"found": true,
"_source": {
"gcid": 2392,
"gcname": "豪爽客中西餐厅"
}
}
得到包含source的全部文档
rpt_indicates_v1/day/27_2394_239200000000000002_20161011?_source
输出结果
{
"_index": "rpt_indicates_v1",
"_type": "day",
"_id": "27_2394_239200000000000002_20161011",
"_version": 1,
"found": true,
"_source": {
"biz_date": "2016-10-11",
"mcid": 2394,
"amount": null,
"type_id": 27,
"mcname": "豪爽客中西餐厅(青阳中天花园店)",
"gcid": 2392,
"count": null,
"remark": "",
"detail_id": "27_2394_239200000000000002_20161011",
"gcname": "豪爽客中西餐厅",
"settle_shift_id": 239200000000000000,
"settle_shift_name": "下午茶",
"value": "",
"timestamp": "2017-11-05 03:18:12"
}
}
只返回source的全部文档
rpt_indicates_v1/day/27_2394_239200000000000002_20161011/_source
输出结果
{
"biz_date": "2016-10-11",
"mcid": 2394,
"amount": null,
"type_id": 27,
"mcname": "豪爽客中西餐厅(青阳中天花园店)",
"gcid": 2392,
"count": null,
"remark": "",
"detail_id": "27_2394_239200000000000002_20161011",
"gcname": "豪爽客中西餐厅",
"settle_shift_id": 239200000000000000,
"settle_shift_name": "下午茶",
"value": "",
"timestamp": "2017-11-05 03:18:12"
}
在ES中文档不能被修改,只能是重新创建,
重新创建的文档会将旧的文档覆盖掉
2018/03/19 ES 权威指南(中文版)
#搜索
##空搜索
2018/03/19 ES 权威指南(中文版)
#mapping
##映射
rpt_indicates_v1/_mapping
rpt_indicates_v1/_mapping/month
2018/03/21 ES 权威指南(中文版)
#结构化查询
##合并多子句
must: AND
must_not:NOT
should:OR
{
"query":{
"bool":{
"must":{
"match":{
"mcid":"3826"
}
},
"must_not":{
"match":{
"amount":0
}
},
"should":{
"match":{
"amount":4278
}
}
}
}
}
#复合子句能合并
#任意其他查询子句,
#包括其他的复合子句。
#这就意味着复合子句可以相互嵌套,
#从而实现非常复杂的逻辑。
2018/03/22 ES 权威指南(中文版)
#验证查询
#查询方式
rpt_dishcount/day/_validate/query
#查询条件
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
#返回结果
{
"valid": false
}
#要想理解错误的信息,需要加上explain参数
rpt_dishcount/day/_validate/query?explain
#查询条件
{
"query": {
"tweet" : {
"match" : "really powerful"
}
}
}
#返回结果
{
"valid": false,
"error": "org.elasticsearch.common.ParsingException: no [query] registered for [tweet]"
}
2018/03/22 ES 权威指南(中文版)
#相关性排序
#在ES中搜索的结果会按照相关性排序,默认以_score这个字段的浮点数进行排序,
#默认情况下,结果集以_score进行倒序排列
#过滤语句不走相关性排序,只有查询时按照相关性排序
#在隐含所有查询条件的match_all为所有文档的_score设值为1
#也就是相当于所有文档的相关性是相同的
#查询的时候带过滤
{
"query":{
"bool":{
"filter":{
"range":{
"amount":{
"gte":10000
}
}
},
"must":{
"match":{
"mcid":"6360"
}
}
}
}
}
#加上排序
{
"size":1,
"query":{
"bool":{
"filter":{
"range":{
"amount":{
"gte":10000
}
}
},
"must":{
"match":{
"mcid":"6360"
}
}
}
},
"sort":{
"biz_date":{
"order":"desc"
}
}
}
#我们可以看到结果集
{
"took": 2018,
"timed_out": false,
"_shards": {
"total": 30,
"successful": 30,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1365,
"max_score": null, ==> 此字段为null
"hits": [
{
"_index": "rpt_indicates_v5",
"_type": "day",
"_id": "61_6360_624400000000000001_20180313",
"_score": null, ==> 此字段为null,score是比较消耗性能的,而且主要的作用是用来排序,
"_routing": "6244",
"_source": {
"biz_date": "2018-03-13",
"mcid": "6360",
"amount": 15940,
"type_id": "61",
"mcname": "大吉利(石家庄店)",
"gcid": "6244",
"count": "0",
"remark": "",
"detail_id": "61_6360_624400000000000001_20180313",
"gcname": "大吉利潮汕牛肉火锅",
"settle_shift_id": "624400000000000001",
"settle_shift_name": "午市",
"value": "",
"timestamp": "2018-03-15 20:22:10"
},
"sort": [
1520899200000 ==> 'date' 字段在内部被转为毫秒
]
}
]
}
}
#理解评分标准
rpt_indicates/day/_search?explain
#查询条件
{
"size":1,
"query":{
"bool":{
"filter":{
"range":{
"amount":{
"gte":10000
}
}
},
"must":{
"match":{
"mcid":"6360"
}
}
}
},
"sort":{
"biz_date":{
"order":"desc"
}
}
}
#查询结果
{
"took": 407,
"timed_out": false,
"_shards": {
"total": 30,
"successful": 30,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1365,
"max_score": null,
"hits": [
{
"_shard": "[rpt_indicates_v5][0]",
"_node": "n_aLLGgbQWqcHHxHWWtI8A",
"_index": "rpt_indicates_v5",
"_type": "day",
"_id": "61_6360_624400000000000001_20180313",
"_score": null,
"_routing": "6244",
"_source": {
"biz_date": "2018-03-13",
"mcid": "6360",
"amount": 15940,
"type_id": "61",
"mcname": "大吉利(石家庄店)",
"gcid": "6244",
"count": "0",
"remark": "",
"detail_id": "61_6360_624400000000000001_20180313",
"gcname": "大吉利潮汕牛肉火锅",
"settle_shift_id": "624400000000000001",
"settle_shift_name": "午市",
"value": "",
"timestamp": "2018-03-15 20:22:10"
},
"sort": [
1520899200000
],
"_explanation": {
"value": 5.626495,
"description": "sum of:",
"details": [
{
"value": 5.626495,
"description": "weight(mcid:6360 in 126401) [PerFieldSimilarity], result of:",
"details": [
{
"value": 5.626495,
"description": "score(doc=126401,freq=1.0 = termFreq=1.0
), product of:",
"details": [
{
"value": 5.626495,
"description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
"details": [
{
"value": 5838,
"description": "docFreq",
"details": [
]
},
{
"value": 1621275,
"description": "docCount",
"details": [
]
}
]
},
{
"value": 1,
"description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1) from:",
"details": [
{
"value": 1,
"description": "termFreq=1.0",
"details": [
]
},
{
"value": 1.2,
"description": "parameter k1",
"details": [
]
},
{
"value": 0,
"description": "parameter b (norms omitted for field)",
"details": [
]
}
]
}
]
}
]
},
{
"value": 0,
"description": "match on required clause, product of:",
"details": [
{
"value": 0,
"description": "# clause",
"details": [
]
},
{
"value": 1,
"description": "amount:[10000.0 TO Infinity], product of:",
"details": [
{
"value": 1,
"description": "boost",
"details": [
]
},
{
"value": 1,
"description": "queryNorm",
"details": [
]
}
]
}
]
}
]
}
}
]
}
}
#数据字段
为了提高排序效率,ElasticSearch 会将所有字段的值加载到内存中,这就叫做"数据字段"。
ElasticSearch中的字段数据常被应用到以下场景:
对一个字段进行排序
对一个字段进行聚合
某些过滤,比如地理位置过滤
某些与字段相关的脚本计算
#分布式搜索
#分布式搜索执行方式
#文档的唯一性由_index,_type和routing-value的三位组合来确定。这意味着我们可以准确的知道集群中哪个分片持有这个文档。
#但是找到匹配的文档只完成了这件事的一半,在搜索返回结果前,来自多个分片的结果必须被放到一个有序的列表中.因此,搜索
#的执行过程分为两个阶段.称为查询然后取回(query then fetch)
#查询阶段(query phase)
#在初始化查询阶段,查询被向索引中的每个分片副本(原本或副本)广播.每个分片在本地执行搜索并建立了匹配document的优先队列(priority queue)
#优先队列
#一个优先队列是一个存有前n个(top-n)匹配document的有序列表.这个优先队列的大小由分页参数from和size决定.例如,下面这个搜索请求要求优先队列
#要能够容纳100个document
#GET /_search
#{
# "from":90,
# "size":10
#}
#询阶段包含以下三步:
#.客户端发送一个search(搜索)请求给Node 3,Node 3创建了一个长度为from+size的空优先级队列。
#2.Node 3 转发这个搜索请求到索引中每个分片的原本或副本。每个分片在本地执行这个查询并且结果将结果到一个大小为from+size的有序本地优先队列里去。
#3.每个分片返回document的ID和它优先队列里的所有document的排序值给协调节点Node 3。Node 3把这些值合并到自己的优先队列里产生全局排序结果。
#整个过程类似于归并排序算法,先分组排序再归并到一起,对于这种分布式场景非常适用。
#取回阶段
#原理类似查询阶段
#深分页
#查询然后取回过程虽然支持通过使用from和size参数进行分页,但是要在有限范围内(within limited)。
#还记得每个分片必须构造一个长度为from+size的优先队列吧,所有这些都要传回协调节点。
#这意味着协调节点要通过对分片数量 * (from + size)个document进行排序来找到正确的size个document。
#根据document的数量,分片的数量以及所使用的硬件,对10,000到50,000条结果(1,000到5,000页)深分页是可行的。
#但是对于足够大的from值,排序过程将会变得非常繁重,会使用巨大量的CPU,内存和带宽。
#因此,强烈不建议使用深分页。
#搜索选项
#preference(偏爱)
#preference参数允许你控制使用哪个分片或节点来处理搜索请求。
#她接受如下一些参数 _primary, _primary_first, _local, _only_node:xyz, _prefer_node:xyz和_shards:2,3
#然而通常最有用的值是一些随机字符串,它们可以避免结果震荡问题(the bouncing results problem)。
#结果震荡(Bouncing Results)
#想像一下,你正在按照timestamp字段来对你的结果排序,并且有两个document有相同的timestamp。
#由于搜索请求是在所有有效的分片副本间轮询的,这两个document可能在原始分片里是一种顺序,在副本分片里是另一种顺序。
#这就是被称为_结果震荡(bouncing results)_的问题:用户每次刷新页面,结果顺序会发生变化。
#避免这个问题方法是对于同一个用户总是使用同一个分片。
#方法就是使用一个随机字符串例如用户的会话ID(session ID)来设置preference参数。
#search_type(搜索类型)
#默认query_then_fetch
#count(计数)搜索类型只有一个query(查询)的阶段
2018/03/26 ES 权威指南(中文版)
#扫描和滚屏
#scan 不排序 只要分片里有结果就可以返回,就返回一批结果
#scroll 会持续批量的从elasticserarch中拉取结果直到没有结果剩下,类似传统数据库里的游标
#并在截取数据的过程中建立快照,也不会对index做任何的修改.
2018/03/27 ES 权威指南(中文版)
请求体
rpt_indicates/day/_search?scroll=1m
{
"size":0
}
请求结果
{
"_scroll_id": "DnF1ZXJ5VGhlbkZldGNoHgAAAAAAFkD2Fm5fYUxMR2diUVdxY0hIeEhXV3RJOEEAAAAAABiRJRZhTFFJSW5TbVQ2aVRVYUF6bjd1ZmtnAAAAAAAZlYwWMUNuWUpYV3BUbE83a1VZRXRtRDFfQQAAAAAAGZWLFjFDbllKWFdwVGxPN2tVWUV0bUQxX0EAAAAAABZA-BZuX2FMTEdnYlFXcWNISHhIV1d0SThBAAAAAAAZlY0WMUNuWUpYV3BUbE83a1VZRXRtRDFfQQAAAAAAGJEpFmFMUUlJblNtVDZpVFVhQXpuN3Vma2cAAAAAABiRLxZhTFFJSW5TbVQ2aVRVYUF6bjd1ZmtnAAAAAAAZlY4WMUNuWUpYV3BUbE83a1VZRXRtRDFfQQAAAAAAGJEmFmFMUUlJblNtVDZpVFVhQXpuN3Vma2cAAAAAABiRKBZhTFFJSW5TbVQ2aVRVYUF6bjd1ZmtnAAAAAAAYkSoWYUxRSUluU21UNmlUVWFBem43dWZrZwAAAAAAGZWPFjFDbllKWFdwVGxPN2tVWUV0bUQxX0EAAAAAABZA9xZuX2FMTEdnYlFXcWNISHhIV1d0SThBAAAAAAAYkScWYUxRSUluU21UNmlUVWFBem43dWZrZwAAAAAAFkD7Fm5fYUxMR2diUVdxY0hIeEhXV3RJOEEAAAAAABZA-RZuX2FMTEdnYlFXcWNISHhIV1d0SThBAAAAAAAZlZAWMUNuWUpYV3BUbE83a1VZRXRtRDFfQQAAAAAAGZWRFjFDbllKWFdwVGxPN2tVWUV0bUQxX0EAAAAAABmVkxYxQ25ZSlhXcFRsTzdrVVlFdG1EMV9BAAAAAAAZlZIWMUNuWUpYV3BUbE83a1VZRXRtRDFfQQAAAAAAGJEtFmFMUUlJblNtVDZpVFVhQXpuN3Vma2cAAAAAABiRLBZhTFFJSW5TbVQ2aVRVYUF6bjd1ZmtnAAAAAAAZlZQWMUNuWUpYV3BUbE83a1VZRXRtRDFfQQAAAAAAGJErFmFMUUlJblNtVDZpVFVhQXpuN3Vma2cAAAAAABZA-hZuX2FMTEdnYlFXcWNISHhIV1d0SThBAAAAAAAZlZcWMUNuWUpYV3BUbE83a1VZRXRtRDFfQQAAAAAAGZWVFjFDbllKWFdwVGxPN2tVWUV0bUQxX0EAAAAAABiRLhZhTFFJSW5TbVQ2aVRVYUF6bjd1ZmtnAAAAAAAZlZYWMUNuWUpYV3BUbE83a1VZRXRtRDFfQQ==",
"took": 3,
"timed_out": false,
"_shards": {
"total": 30,
"successful": 30,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 22813669,
"max_score": 0,
"hits": [
]
}
}
请求的结果会返回一个_scroll_id
#索引管理
#创建索引
PUT /my_index
{
"settings": { ... any settings ... },
"mappings": {
"type_one": { ... any mappings ... },
"type_two": { ... any mappings ... },
...
}
#可以通过在config/elasticsearch.yml添加下面的配置来防止自动创建索引
action.auto_create_index: false
#索引模板可以预先自动配置索引
#删除索引
DELETE /my_index
#也可以一次性删除多个索引
DELETE /index_one,index_two
DELETE /index_*
#也可以删除所有索引
DELETE /_all
#索引设置
#索引的两个重要设置
#number_of_shards
#定义一个索引的主分片个数,默认值是 `5`。这个配置在索引创建后不能修改。
#number_of_replicas
#每个主分片的复制分片个数,默认是 `1`。这个配置可以随时在活跃的索引上修改。
#类型和映射
#索引别名
PUT /my_index_v1
PUT /my_index_v1/_alias/my_index
#分片
#查找准确值
在关系型数据库中,我们找出特定价格的产品
select document from products where price = 20;
在ES中使用term过滤器来实现同样的事
{
"query":{
"term":{
"amount":50000
}
}
}
#bool查询单值匹配
{
"query":{
"bool":{
"filter":{
"range":{
"amount":{
"gte":20000
}
}
},
"must":{
"match":{
"mcid":"6521"
}
}
}
}
}
#bool查询多值匹配
{
"query":{
"bool":{
"filter":{
"range":{
"amount":{
"gte":40000
}
}
},
"must":{
"terms":{
"mcid":[
"6521",
"6522"
]
}
}
}
}
}
#range 过滤器也可以用于日期字段:
"range" : {
"timestamp" : {
"gt" : "2014-01-01 00:00:00",
"lt" : "2014-01-07 00:00:00"
}
}
#当用于日期字段时,range 过滤器支持_日期数学_操作。例如,我们想找到所有最近一个小时的文档:
"range" : {
"timestamp" : {
"gt" : "now-1h"
}
}
#日期计算也能用于实际的日期,而不是仅仅是一个像 now 一样的占位符。只要在日期后加上双竖线 ||,就能使用日期数学表达式了。
"range" : {
"timestamp" : {
"gt" : "2014-01-01 00:00:00",
"lt" : "2014-01-01 00:00:00||+1M"
}
}
#使用must组合条件查询
{
"query":{
"bool":{
"filter":{
"range":{
"amount":{
"gte":40000
}
}
},
"must":[
{
"terms":{
"mcid":[
"6521",
"6522"
]
}
},
{
"range":{
"timesatmp":{
"lte":"now-1h"
}
}
}
]
}
}
}
#嵌套