Elasticsearch 入门教程 – 范围搜索

范围编辑

    本章到目前为止,对于数字,只介绍如何处理精确值查询。 实际上,对数字范围进行过滤有时会更有用。例如,我们可能想要查找所有价格大于 $20 且小于 $40 美元的产品。

在 SQL 中,范围查询可以表示为:

SELECT document
FROM   products
WHERE  price BETWEEN 20 AND 40

Elasticsearch 有 range 查询, 不出所料地,可以用它来查找处于某个范围内的文档:

"range" : {
   "price" : {
       "gte" : 20,
       "lte" : 40
   }
}

range 查询可同时提供包含(inclusive)和不包含(exclusive)这两种范围表达式,可供组合的选项如下:

gt> 大于(greater than)

lt< 小于(less than)

gte>= 大于或等于(greater than or equal to)

lte<= 小于或等于(less than or equal to)

下面是一个范围查询的例子:. 

GET /my_store/products/_search
{
   "query" : {
       "constant_score" : {
           "filter" : {
               "range" : {
                   "price" : {
                       "gte" : 20,
                       "lt"  : 40
                   }
               }
           }
       }
   }
}

如果想要范围无界(比方说 >20 ),只须省略其中一边的限制:

"range" : {
   "price" : {
       "gt" : 20
   }
}

Elasticsearch 范围查询 Java API:

QueryBuilders.rangeQuery("price").gt(1).lt(10)

日期范围编辑

range 查询同样可以应用在日期字段上:

"range" : {
   "timestamp" : {
       "gt" : "2014-01-01 00:00:00",
       "lt" : "2014-01-07 00:00:00"
   }
}

当使用它处理日期字段时, range 查询支持对 日期计算(date math) 进行操作,比方说,如果我们想查找时间戳在过去一小时内的所有文档:

"range" : {
   "timestamp" : {
       "gt" : "now-1h"
   }
}

这个过滤器会一直查找时间戳在过去一个小时内的所有文档,让过滤器作为一个时间 滑动窗口(sliding window) 来过滤文档。

日期计算还可以被应用到某个具体的时间,并非只能是一个像 now 这样的占位符。只要在某个日期后加上一个双管符号 (||) 并紧跟一个日期数学表达式就能做到:

"range" : {
   "timestamp" : {
       "gt" : "2014-01-01 00:00:00",
       "lt" : "2014-01-01 00:00:00||+1M"
   }
}

早于 2014 年 1 月 1 日加 1 月(2014 年 2 月 1 日 零时)

日期计算是 日历相关(calendar aware) 的,所以它不仅知道每月的具体天数,还知道某年的总天数(闰年)等信息。

日期格式说明

Elasticsearch 日期查询 Java API:

//取到当天凌晨时间
    	Calendar cal = Calendar.getInstance();
    	cal.set(Calendar.HOUR_OF_DAY,0);
    	cal.set(Calendar.MINUTE, 0);
    	cal.set(Calendar.SECOND, 0);
    	cal.add(Calendar.DATE,0);
    	//查询今天的的数据,查询createDate 字段,大于当天0点的时间
    	srb.setQuery(QueryBuilders.rangeQuery("createDate").gt(cal.getTime()));

按Date类型查询

查询大于一个给定的时间。

//查询大于一个给定的时间 QueryBuilders.rangeQuery("date").gt(date);

查询一个区间时间

//查询区间时间  QueryBuilders.rangeQuery("date").lt(beginDate).gt(endDate);

查询小于给定的时间

//查询小于给定时间的数据 QueryBuilders.rangeQuery("date").lt(beginDate);

Date 以格式化字符串方式查询。

查询小于给定时间的数据

//查询小于给定时间的数据 QueryBuilders.rangeQuery("date").format("yyyyMMdd").lt("20170505");

查询区间时间

//查询区间时间  srb.setQuery(QueryBuilders.rangeQuery("date").format("yyyyMMdd").gt("20170505").lt("20170530"));

查询等于给定时间

//查询等于给定时间 srb.setQuery(QueryBuilders.rangeQuery("date").format("yyyyMMdd").gte("20170505"));

小插曲:这里我建议格式化话的时候,中间别带“-” ,只是建议。因为我测试把“20170505” 写成 int 类型的 20170505  查询是OK 的。

有问题加群继续沟通。另外上次记得深入了一次Elasticsearch date 类型存储的博客。

另外存储Date,请认准java.util.Date  , 切勿用 java.sql.Date 

字符串范围

range 查询同样可以处理字符串字段, 字符串范围可采用 字典顺序(lexicographically) 或字母顺序(alphabetically)。例如,下面这些字符串是采用字典序(lexicographically)排序的:

  • 5, 50, 6, B, C, a, ab, abb, abc, b

在倒排索引中的词项就是采取字典顺序(lexicographically)排列的,这也是字符串范围可以使用这个顺序来确定的原因。

如果我们想查找从 a 到 b (不包含)的字符串,同样可以使用 range 查询语法:

"range" : {
   "title" : {
       "gte" : "a",
       "lt" :  "b"
   }
}

注意基数

数字和日期字段的索引方式使高效地范围计算成为可能。 但字符串却并非如此,要想对其使用范围过滤,Elasticsearch 实际上是在为范围内的每个词项都执行 term 过滤器,这会比日期或数字的范围过滤慢许多。

字符串范围在过滤 低基数(low cardinality) 字段(即只有少量唯一词项)时可以正常工作,但是唯一词项越多,字符串范围的计算会越慢。

RangeFilter实现时间范围过滤:

 需求:我们搜索文章范围在近一年之内。这时候我们就用到elasticsearch RangeFilter了

long current=System.currentTimeMillis()/1000l;
Calendar ca = Calendar.getInstance();//得到一个Calendar的实例  
ca.setTime(new Date());   //设置时间为当前时间  
ca.add(Calendar.YEAR, -1); //年份减1  
Date lastYear = ca.getTime(); 
QueryStringQueryBuilder queryBuilder = new QueryStringQueryBuilder("中国经济");
queryBuilder.analyzer("ik").field("title");  
FilteredQueryBuilder query = QueryBuilders.filteredQuery(queryBuilder, 
FilterBuilders.boolFilter().must(FilterBuilders.rangeFilter("updatetime").from(otherDate).to(new Date())));

发表评论