引言
DSL,叫“特定领域语言”,是针对某一领域,具有受限表达性的一种计算机程序设计语言。elasticsearch的 query DSL即是针对elasticsearch检索的一种特定语言。
es的DSL在使用java API访问es时候也特别好用,本文我们详细了解下es的DSL java API的使用。
在使用java api检索es时候,我们使用的方式是:
QueryBuilder builders = null;// 重点讲这个的构造
SearchResponse response = client.prepareSearch(index)
.setFrom(0).setSize(100)
.setTimeout(TimeValue.timeValueMillis(300))
.setFetchSource(retFields, null)
.setQuery(builders)
.setTypes(type)
.execute().actionGet();
上面查询API中的setQuery就是指定查询的DSL,也是本文主要讲的。
es版本:5.1.x
query和filter
我们知道,es的查询分为两种query和filter,两种不同的上下文环境用于不同的目的。
query
使用上下文
query字句解决的问题是“文档和查询字句匹配程度”,并通过一个score分数来具体表示匹配程度。
query子句查询生效条件
直接在search API中指定query参数(通过setQuery),比如设置如下:
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(QueryBuilders.termQuery("val", "field")); // 指定query,使用的是query上下文
System.out.println(requestBuilder.toString()); // 打印查询语句
SearchResponse response = requestBuilder.execute().actionGet(); // 执行query
在上例中,我们通过设置query参数,使用的就是query上下文查询。如果通过toString方法打印出来,如下:
"query" : {
"term" : {
"field" : {
"value" : "val",
"boost" : 1.0
}
}
}
filter
使用上下文
filter字句解决的问题是“文档和查询字句是否匹配”,只有两种情况YES和NO,不会计算分数。filter字句常用来过滤结构型数据,比如:
是否时间字段timestamp处于2015和2016之间
是否status字段被设置为true
另外,filter查询会被es缓存到内存以提高性能。
filter字句查询生效条件
触发filter上下文有三种情况:
在bool查询中指定filter参数或者must_not参数;
在 constant_score查询中指定filter参数;
filter aggregation,即filter聚合查询;
示例
如下一段查询语句:
GET /_search
{
"query": { (1)
"bool": { (2)
"must": [
{ "match": { "title": "Search" }}, (3)
{ "match": { "content": "Elasticsearch" }} (4)
],
"filter": [ (5)
{ "term": { "status": "published" }}, (6)
{ "range": { "publish_date": { "gte": "2015-01-01" }}} (7)
]
}
}
}
说明如下:
(1) query参数表明是query上下文;
(2)(3)(4)bool和两个match查询字句处于query上下文,用score表示匹配度;
(5) filter参数表明是filter上下文;
(6) (7) term和range子句用于filter上下文,将会过滤掉不匹配的文档,这些查询语句不会影响match查询的匹配分数;
term级别query
全文档匹配
用于匹配所有的文档,java API使用如下:
MatchAllQueryBuilder allQueryBuilder = QueryBuilders.matchAllQuery(); // match_all匹配
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(allQueryBuilder);
SearchResponse response = requestBuilder.execute().actionGet(); // 执行query
上面用于匹配index下面所有文档。
term query
说明:term级别检索是在es的倒排索引中排序的term准确的匹配。
场景:
结构化数据的查询,比如数字,日期,enum,而不是全文本字段;
低级别查询,忽略分词处理;
API使用:
TermQueryBuilder builder = QueryBuilders.termQuery("field", "val"); // 在field字段中查询val
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
terms query
说明:在指定字段中查询包含任意一个term的文档。
API使用:
TermsQueryBuilder builder = QueryBuilders.termsQuery("field", "val1", "val2"); // field中包含val1或者val2
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
rank query
说明:在指定的field中是否包含固定的值(一个范围值),比如数字,日期,字符串。
API使用:
RangeQueryBuilder builder = QueryBuilders.rangeQuery("age").from(20).to(40) // 从20到40
.includeLower(true) // 是否包含下界,即>=20
.includeUpper(true); // 是否包含上界,即<=40
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
exist query
说明:查询指定的field是否包含任意非null的值。
API使用:
ExistsQueryBuilder builder = QueryBuilders.existsQuery("field"); // 字段field是否非null
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
prefix query
说明:查询指定的filed是否包含指定的前缀开头的term。
API使用:
PrefixQueryBuilder builder = QueryBuilders.prefixQuery("field", "prefix"); // 字段field是否包含prefix开头的term
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
wildcard query
说明:查询指定的field是否包含符合通配符匹配的term,通配符支持?和*。
API使用:
WildcardQueryBuilder builder = QueryBuilders.wildcardQuery("field", "a?pre*"); // 字段field是否匹配a?pre*
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
其他query
其他还包括ids query,type query,regexp query,类似上面一样。
全文query
即full text query,一般用户类似邮件内容、文章内容这样的全文检索,每个field对应一个analyzer,即将field内容分词并构建倒排索引。通常也会在执行检索前对query字符串进行分词(每个field对应一个analyzer或search_analyzer。
全文query相关API使用参考官网。
DSL的组合
bool query
我们经常遇到如果我的查询条件是(A || B ) && (C || D)这样情况时,该如何组织我们的API,这其实就是一个bool组合查询。
bool查询是将多种查询组合在一起,并且每个都对应一个“事件”(即must,should,must_not和filter),四种事件说明如下:
事件 描述
must 查询子句must出现在文档中,并且会影响文档得分score
filter 查询子句must出现在文档中,但是不会影响文档得分,并且会缓存
should 查询子句should出现在文档中,即并列的should子句必须有一个或者多个出现在文档中,可以通过设置参数 minimum_should_match来指定最少匹配的查询子句
must_not 查询子句一定不能出现在文档中,处于filter上下文,不影响文档得分,会被缓存
因此,bool组合查询条件就像构建一棵树一样,按照我们的逻辑构造bool查询即可,比如上述的(A || B ) && (C || D)组合构造结构如下图:
使用java api编写代码如下:
BoolQueryBuilder builder = QueryBuilders.boolQuery();
BoolQueryBuilder shoud1 = QueryBuilders.boolQuery();
TermQueryBuilder tqbA = QueryBuilders.termQuery("fieldA", "A");
TermQueryBuilder tqbB = QueryBuilders.termQuery("fieldB", "B");
shoud1.should(tqbA);
shoud1.should(tqbB);
BoolQueryBuilder shoud2 = QueryBuilders.boolQuery();
TermQueryBuilder tqbC = QueryBuilders.termQuery("fieldC", "C");
TermQueryBuilder tqbD = QueryBuilders.termQuery("fieldD", "D");
shoud2.should(tqbC);
shoud2.should(tqbD);
builder.must(shoud1);
builder.must(shoud2);
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet(); boosting query
在实际使用过程中,我们经常用到的是,对于不同关键词的查询,我们希望不同term词对查询结果得分score的影响不同,即不同查询词的权重不同;还有一种情况就是,我们希望包含某些查询词的文档降权(而不是直接过滤掉),这时候就可以用到我们的booting查询了。
指定boost值得java示例如下:
BoolQueryBuilder builder = QueryBuilders.boolQuery();
TermQueryBuilder tqbA = QueryBuilders.termQuery("fieldA", "A").boost(5.0f);
TermQueryBuilder tqbB = QueryBuilders.termQuery("fieldB", "B").boost(2.0f);
TermQueryBuilder tqbC = QueryBuilders.termQuery("fieldC", "C").boost(0.2f);
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
同样,也可以直接使用boost query,示例如下:
TermQueryBuilder tqbA = QueryBuilders.termQuery("fieldA", "A");
TermQueryBuilder tqbB = QueryBuilders.termQuery("fieldB", "B");
BoostingQueryBuilder builder = QueryBuilders.boostingQuery(tqbA, tqbB).negativeBoost(0.2f); // tqbA提权,tqbB降权
SearchRequestBuilder requestBuilder = client.prepareSearch(index);
requestBuilder.setQuery(builder);
SearchResponse response = requestBuilder.execute().actionGet();
其他组合
除了常用的bool query和boost query,还有其他几种compound query方式。比如function score query,可以指定计算文档分数的函数;constant score query,指定每个文档的分数相同;dis max query,用于指定不同的子查询分数不同,从而文档取子查询中分数最高的一个。 这些可以从官网来去学习了解