本文集主要是总结自己在项目中使用ES 的经验教训,包括各种实战和调优。
当用于日期字段时,range 过滤器支持日期数学操作。例如,我们想找到所有最近一个小时的文档:
"range" : {
"timestamp" : {
"gt" : "now-1h"
}
}
这个过滤器将始终能找出所有时间戳大于当前时间减 1 小时的文档,让这个过滤器像移窗一样通过你的文档。
日期计算也能用于实际的日期,而不是仅仅是一个像 now 一样的占位符。只要在日期后加上双竖线 ||,就能使用日期数学表达式了。
"range" : {
"timestamp" : {
"gt" : "2014-01-01 00:00:00",
"lt" : "2014-01-01 00:00:00||+1M" <1>
}
}
<1> 早于 2014 年 1 月 1 号加一个月
日期计算是与日历相关的,所以它知道每个月的天数,每年的天数,等等。更详细的关于日期的信息可以在这里找到 日期格式手册
字符串范围
range 过滤器也可以用于字符串。字符串范围根据字典或字母顺序来计算。例如,这些值按照字典顺序排序:
- 5, 50, 6, B, C, a, ab, abb, abc, b
提示:倒排索引中的短语按照字典顺序排序,也是为什么字符串范围使用这个顺序。
假如我们想让范围从 a 开始而不包含 b,我们可以用类似的 range 过滤器语法:
"range" : {
"title" : {
"gte" : "a",
"lt" : "b"
}
}
当心基数:
数字和日期字段的索引方式让他们在计算范围时十分高效。但对于字符串来说却不是这样。为了在字符串上执行范围操作,Elasticsearch 会在这个范围内的每个短语执行 term 操作。这比日期或数字的范围操作慢得多。
字符串范围适用于一个基数较小的字段,一个唯一短语个数较少的字段。你的唯一短语数越多,搜索就越慢。
关于now的使用:
now并不是按照北京时间,而且使用utc时间,因此会相差八个小时。可以自己在now+8h就可以表示现在的时间。在一点在使用的时候需要额外注意。
在es的DateHistogramBuilder里面有几个比较重要的参数:
- field:指定按那个字段聚合
- interval:聚合的时间单位(年,季度,月,周,天,小时,分钟,秒)
- format:日期格式
- time_zone:时区指定
- offset:时间偏移量
例:
GET _search
{
"query": {
"range" : {
"timestamp" : {
"gte": "2015-01-01 00:00:00",
"lte": "now",
"time_zone": "+01:00"
}
}
}
}
This date will be converted to 2014-12-31T23:00:00 UTC.
now is not affected by the time_zone parameter (dates must be stored as UTC).
注意,默认不设置时区参数,es是按照UTC的时间进行查询的,所以分组的结果可能与预期不一样,所以我们要指定时区为Asia/Shanghai代表北京的时区,这样才能获取正确的聚合结果
curl方式如下:
GET my_index/_search?size=0
{
"aggs": {
"by_day": {
"date_histogram": {
"field": "ctime",
"interval": "day",
"time_zone": "Asia/Shanghai"
}
}
}
}
Java代码如下:
SearchRequestBuilder search = client.prepareSearch("search2017-02*").setTypes("log");
DateHistogramBuilder dateagg = AggregationBuilders.dateHistogram("dateagg");
dateagg.field("ctime");//聚合时间字段
dateagg.interval(DateHistogramInterval.DAY);//按天聚合第一天的0点到第二天的0点
dateagg.timeZone("Asia/Shanghai");//指定时区
// dateagg.offset("+8h");//默认都是从0点开始计算一天的,通过这个offset,我们可以把第一天的6点到第二天的6点当做一天来聚合
search.addAggregation(dateagg);
Histogram hs= search.get().getAggregations().get("dateagg");
List<Histogram.Bucket> buckets = (List<Histogram.Bucket>) hs.getBuckets();//获取结果
for(Histogram.Bucket bk:buckets){
//下面的转化,也是因为默认是UTC的时间,所以我们要获取时间戳,自己转化
System.out.println(new DateTime(Long.parseLong(bk.getKeyAsString()+"")).toString("yyyy-MM-dd HH:mm:ss") +" "+bk.getDocCount());
}
client.close();
上面的这个例子,基本涵盖了日期聚合核心功能,其中时区和偏移量时两个非常有用的而且需要特别注意的参数,不设置时区直接统计结果肯定是不准确的,offset偏移量这个参数,在某些时刻也是有用的,它可以自己定义一天的开始,比如设置从第一天的3点到第二天的3点为一天,默认都是从0点开始0点结束算做一天的,最后一点需要注意的是在输出打印时间的时候也要考虑转化因为默认也是UTC的时间,所以我们直接取出时间戳,自己格式化时间即可。
同时使用data math 可以表示很复杂的时间关系。这个后续根据需要在学习。
官网链接:
https://www.elastic.co/guide/en/elasticsearch/reference/5.3/query-dsl-range-query.html
网友评论