翻译自elasticsearch官方文档,整理了java api和rest api的文档部分,仅仅是整理翻译,不是逐字逐句,某些遗漏部分请移步官方文档。
目录
- Docment API
- Index API
- Get API
- Multi Get API
- Delete API
- Update API
- 批量操作
- Bulk API
- Bulk Processor
- Search API
- 使用Java scrolls
- Multisearch API
- Terminate After
- Count API
- Query DSL
- Match All Query
- Full text queries
- Match Query
- Multi Match Query
- Common Terms Query
- Query String Query
- Simple Query String Query
- Term level queries
- Term Query
- Terms Query
- Range Query
- Exists Query
- Prefix Query
- Wildcard Query
- Regexp Query
- Fuzzy Query
- Type Query
- Ids Query
- Compound queries
- Constant Score Query
- Bool Query
- Dis Max Query
- Function Score Query
- Boosting Query
- Indices Query
- Joining queries
- nested query
- has child query
- has parent query
- Geo queries
- geo shape query
- Geo Bounding Box Query
- Geo Distance Query
- Geo Distance Range Query
- Geo Polygon Query
- Geohash Cell Query
- Specialized queries
elasticsearch版本:2.4.5
Docment API
基础的CRUD API
https://www.elastic.co/guide/en/elasticsearch/client/java-api/2.4/java-docs.html
Index API
将结构化的json文档添加到指定的索引下,如果索引不存在,将自动创建
1 2 3 4
| Map<String, Object> json = new HashMap<String, Object>(); json.put("user","kimchy"); IndexResponse response = client.prepareIndex(index,type).setRefresh(true).setSource(params).get();
|
setFresh(true)使添加的文档能立即被搜索到,在添加文档后需要立即能被检索到时很有用,如果是很重的索引操作,不应该设置为true,默认false。
Get API
通过文档id获取结构化的json文档
1
| GetResponse response = client.prepareGet(index, type, id).get();
|
Multi Get API
通过多个id获取多个文档,可以是不同索引和类型
1 2 3 4 5 6 7 8 9 10 11 12
| MultiGetResponse multiGetItemResponses = client.prepareMultiGet() .add("twitter", "tweet", "1") .add("twitter", "tweet", "2", "3", "4") .add("another", "type", "foo") .get(); for (MultiGetItemResponse itemResponse : multiGetItemResponses) { GetResponse response = itemResponse.getResponse(); if (response.isExists()) { String json = response.getSourceAsString(); } }
|
Delete API
通过文档id删除索引下的文档
1
| DeleteResponse response = client.prepareDelete(index, type, id).setRefresh(true).get();
|
Update API
文档修改,实质是先删除再添加,指定的json文档时,可以指定部分内容,es会进行浅合并。
1 2
| Map params = new HashMap(); UpdateResponse response = client.prepareUpdate(index,type,id).setRefresh(true).setDoc(params).get();
|
如果指定的id不存在,会抛出以下错误
1
| Exception in thread "main" [radiott][[radiott][2]] DocumentMissingException[[artiststt][213]: document missing]
|
在进行修改操作时,需要判断记录是否存在,或者使用upsert,当文档不存在时,会使用indexRequest添加文档
1
| UpdateResponse response = client.prepareUpdate(index,type,id).setRefresh(true).setDoc(params).setDocAsUpsert(false).get();
|
setDocAsUpsert(true)是当文档不存在时,使用指定的文档(即params)添加。
同样还有setUpsert(Map upsertParam)及不同参方法,当文档不存在时使用upsertParam作为文档添加
批量操作
Bulk API
合并多个IndexRequest、UpdateRequest、DeleteRequest为一个请求
1 2 3 4 5 6 7 8 9 10
| BulkRequestBuilder bulkRequest = client.prepareBulk(); UpdateRequestBuilder updateBuilder = client.prepareUpdate(index, type, id).setSource(xxx).xxx; DeleteRequestBuilder deleteBuilder = client.prepareDelete(index, type, id).xxx; ... bulkRequest.add(updateBuilder); bulkRequest.add(deleteBuilder); BulkResponse bulkResponse = bulkRequest.get(); if (bulkResponse.hasFailures()) { }
|
Bulk Processor
BulkProcessor提供了一个简单的接口能够根据请求的数量或数据的大小或时间间隔自动处理批量操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| BulkProcessor bulkProcessor = BulkProcessor.builder( client, new BulkProcessor.Listener() { @Override public void beforeBulk(long executionId,BulkRequest request) { ... } @Override public void afterBulk(long executionId,BulkRequest request,BulkResponse response) { ... } @Override public void afterBulk(long executionId,BulkRequest request,Throwable failure) { ... } }) .setBulkActions(10000) .setBulkSize(new ByteSizeValue(1, ByteSizeUnit.GB)) .setFlushInterval(TimeValue.timeValueSeconds(5)) .setConcurrentRequests(10) .setBackoffPolicy(BackoffPolicy.exponentialBackoff(TimeValue.timeValueMillis(100), 3)) .build(); bulkProcessor.add(new IndexRequest("twitter", "tweet", "1").source()); bulkProcessor.add(new DeleteRequest("twitter", "tweet", "2")); bulkProcessor.awaitClose(10, TimeUnit.MINUTES);
|
上面注释中三个条件任意达到一个触发批量处理。
默认情况下,批量请求数1000,数据量阈值5M,无时间间隔,1个批量处理线程,补偿策略(重试8次,延时50ms)
Search API
search api执行一个查询操作并返回符合查询条件的命中数,可以跨多个索引和多个类型。
https://www.elastic.co/guide/en/elasticsearch/client/java-api/2.4/java-search.html
一个简单的例子
1 2 3 4 5 6 7 8
| SearchResponse response = client.prepareSearch("index1", "index2") .setTypes("type1", "type2") .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) .setQuery(QueryBuilders.termQuery("multi", "test")) .setPostFilter(QueryBuilders.rangeQuery("age").from(12).to(18)) .setFrom(0).setSize(60).setExplain(true) .execute() .actionGet();
|
上面的所有参数都是可选的。一个极简的查询如下:
1
| SearchResponse response = client.prepareSearch().execute().actionGet();
|
search查询请求返回的是一个单页的数据结果集,scroll API常用来通过查询请求获取大数据量的结果集。在传统的关系型数据库中我们通常使用cursor来处理类似需求。
scroll并不是用来处理用户的实时查询,而是用来处理大量的数据操作。例如:使用不同的配置为一个索引的内容建立一个新的索引。
注意:在scroll的初始请求中,将返回当前索引状态的结果集,类似于索引的一个快照,后续改变文档的操作如添加删除修改都不影响接下来的scroll查询。
在使用scroll中,初始查询需要指定scroll的查询参数(即快照时间),告诉elasticsearch快照的有效时间。在处理大量数据时,这个值不需要很大,只需要保证处理前一批数据时间足够就行,每一次scroll查询都会刷新快照的失效时间。
一般情况下,在查询操作中,后台会对索引进行合并操作优化,将多个小的片段合并成一个大的片段,然后删除小的片段。这种优化策略同样适用于scroll的初始查询,但是快照(快照就是一个最终合并的大的片段)一旦建立,在快照的有效时间内将会阻止旧片段的删除。这也是scroll初始查询一旦完成,无论怎样修改文档都不会影响scroll后续查询的原因。
注意:保持旧的片段(即快照)有效,意味着需要更多的文件句柄,必须确保elasticsearch节点配置了足够的文件句柄。相关配置
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| SearchResponse scrollResp = client.prepareSearch(test) .addSort(SortParseElement.DOC_FIELD_NAME, SortOrder.ASC) .setScroll(new TimeValue(60000)) .setQuery(QueryBuilders.termQuery("multi", "test")) .setSize(100) .execute().actionGet(); while (true) { for (SearchHit hit : scrollResp.getHits().getHits()) { } scrollResp = client.prepareSearchScroll(scrollResp.getScrollId()).setScroll(new TimeValue(60000)).execute().actionGet(); if (scrollResp.getHits().getHits().length == 0) { break; } }
|
注意:setSize(100)并不是指最终返回100条数据,而是指每个分片返回的最大数量是100.假如一个操作涉及到多个索引多个分片,最终返回的数量可能会超出你的预期(实际返回的数量是:0 ~ 分片数量*size)
Multisearch API
将多个查询请求合并成一个查询请求
1 2 3 4 5 6 7 8 9
| SearchRequestBuilder srb1 = client.prepareSearch().setQuery(QueryBuilders.queryStringQuery("elasticsearch")).setSize(1); SearchRequestBuilder srb2 = client.prepareSearch().setQuery(QueryBuilders.matchQuery("name","kimchy")).setSize(1); MultiSearchResponse sr = client.prepareMultiSearch().add(srb1).add(srb2).execute().actionGet(); long nbHits = 0; for (MultiSearchResponse.Item item : sr.getResponses()) { SearchResponse response = item.getResponse(); nbHits += response.getHits().getTotalHits(); }
|
Terminate After
在每个分片上收集大量文档,根据收集的数量可以提前关闭搜索操作,如果设置提前关闭,我们可以调用SearchRespnse
对象的 isTerminatedEarly
方法判断操作是否提前结束。
1 2 3 4 5 6 7
| SearchResponse sr = client.prepareSearch(INDEX) .setTerminateAfter(1000) .get(); if (sr.isTerminatedEarly()) { }
|
Count API
已废弃,使用search api代替并设置size为0;
Query DSL
官方文档
elasticsearch 提供了全功能的Java query dsl,同REST query dsl保持一致。构建查询的工厂类是QueryBuilders
,一旦查询器构建好后,配合search api一起使用。
使用QueryBuilders工厂构建好查询器QueryBuilder
实例后,可以直接调用toString()
方法打印出结构化的json格式字符串
1 2
| QueryBuilder builder = QueryBuilders.termQuery("name","test"); System.out.println(builder.toString());
|
打印结果
1 2 3 4 5
| { "term" : { "name" : "test" } }
|
QueryBuilder
实例可以使用在任何接受query查询的api中,如count
和seaarch
。
测试的时候查询都需要包含以下结构体中,使用POST请求。
使用GET请求查询时,head插件直接忽略了我的查询条件,直接返回索引类型下的所有数据
Match All Query
最简单的查询,会查询所有文档,每个文档的_score
为1.0
。
1 2 3
| { "match_all": {} } { "match_all": { "boost" : 1.2 }}
|
Java示例
1
| QueryBuilder qb = matchAllQuery();
|
Full text queries
高等级的全文本查询,常用来在全文本字段上如邮件的正文内容执行全文本查询
Match Query
match家族的查询支持文本\数字和日期类型数据
1 2 3 4 5
| { "match" : { "message" : "this is a test" } }
|
match查询包含三种类型:boolean,phrase和phrase_prefix。
boolean
默认的match查询类型,意思是分析进程根据提供的文本构建一个boolean查询,可以设置operator
参数(or
或and
,默认or
)控制boolean字句。
1 2 3 4 5 6 7 8
| { "match" : { "message" : { "query" : "this is a test", "operator" : "and" } } }
|
phrase
match_phrase
查询通过分析提供的文本创建phrase查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "match_phrase" : { "message" : "this is a test" } } { "match" : { "message" : { "query" : "this is a test", "type" : "phrase" } } }
|
phrase_prefix
1 2 3 4 5
| { "match_phrase_prefix" : { "message" : "quick brown f" } }
|
java使用:
1
| QueryBuilder qb = matchQuery("name","kimchy elasticsearch");
|
Multi Match Query
同match query,允许在不同字段上进行查询,字段名支持占位符
1 2 3 4 5 6 7
| { "multi_match" : { "query": "this is a test", "fields": [ "subject", "message" ] } }
|
Java示例
1
| QueryBuilder qb = multiMatchQuery("kimchy elasticsearch","user", "message" );
|
Common Terms Query
更多参数具体参考官方文档
1 2 3 4 5 6 7
| { "common": { "body": { "query": "this is bonsai cool" } } }
|
Java示例
1
| QueryBuilder qb = commonTermsQuery("name","kimchy");
|
Query String Query
专门的字符串查询.
默认示例
1 2 3 4 5 6 7
| { "query_string" : { "query" : "test", "default_field" : "name" } }
|
- query字段支持
?
和*
占位,前者代表任意一个字符,后者代表多个任意字符.
可以将字段名和查询内容写在一起,”query”:”name:test”.如果是占位查询,需要转义,”query”:”name:\\*test”
支持模糊查询,不同于关系型数据库的模糊查询,这里指的是允许错误额输入内容模糊查询,如搜索computer,输入时打错了computor,只要在查询的时候输入computor~ , 同样可以搜索到computer,默认允许的错误字符数是2,可以通过computor~3,就能允许错误三个字符.
其它的一些参数如下:
- default_operator:操作符,默认
OR
.如查询字符串是”cat dog”,两个短语以空格分开,如果为OR
,只要字段包含cat或dog之一就可以匹配否则,字段内必须同时包含cat和dog才可以匹配.
- minimum_should_match:最小匹配词条,假如query:”cat dog mouse”,配置项为2,那么只有字段中至少包含这三个中的任意两个才能匹配成功,这个配置项仅对default_operator为
OR
时生效.
更多参数参考官方文档
Java示例
1
| QueryBuilder qb = queryStringQuery("+kimchy -elasticsearch");
|
Simple Query String Query
基本同 Query String Query
1 2 3 4 5 6
| { "simple_query_string" : { "fields" : ["content", "name.*^5"], "query" : "foo bar baz" } }
|
Java示例
1
| QueryBuilder qb = simpleQueryStringQuery("+kimchy -elasticsearch");
|
Term level queries
不同于full text queries会在查询执行前分析查询字符串,term-level queries要求在倒排索引上有精确的term . 这些查询常常使用在结构化的数据上如数字,日期,非全文本的字符串(如name,gender)和枚举等,而不是==全文本==字段(如邮箱正文)上.
Term Query
1 2 3
| { "term" : { "user" : "Kimchy" } }
|
可以设置boost
参数指定当前term查询比其他查询有更高的关联分数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| { "query": { "bool": { "should": [ { "term": { "status": { "value": "urgent", "boost": 2.0 } } }, { "term": { "status": "normal" } } ] } } }
|
在上面的例子中,设置status字段中值为urgent的查询字句boost值为2,意思是urgent的重要性是normal的两倍(boost的默认值是1.0)
==为什么term query匹配不到任何文档?==
字符串字段可以被analyzed
(视为全文本字段,如邮件正文)或者not_analyzed
(视为精确的值如邮件地址),精确的值(如数字日期或not_analyzed
字符串)在在倒排索引的字段中被明确指定.
默认情况下,字符串字段都被analyzed
, 即字符串值在存储时第一步被传输到分析器分解成terms列表,再添加到倒排索引中.例如,字符串 “Quick Brown Fox!”被分解成 “ [quick, brown, fox]”三个term. 文本分析处理使在大字段文本中可以搜索单个单词.
term query在倒排索引的字段中搜索精确值,这在not_analyzed
字段,日期字段,数字字段中搜索精确值非常有用, 如果要搜索全文本字段,使用match query替代.
示例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| PUT my_index { "mappings": { "my_type": { "properties": { "full_text": { "type": "string" }, "exact_value": { "type": "string", "index": "not_analyzed" } } } } } PUT my_index/my_type/1 { "full_text": "Quick Foxes!", "exact_value": "Quick Foxes!" } PUT my_index/my_type/1 { "full_text": "Quick Foxes!", "exact_value": "Quick Foxes!" }
|
执行四个查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| { "query": { "term": { "exact_value": "Quick Foxes!" } } } { "query": { "term": { "full_text": "Quick Foxes!" } } } { "query": { "term": { "full_text": "foxes" } } } { "query": { "match": { "full_text": "Quick Foxes!" } } }
|
第一个查询能够匹配到,因为存在精确的term “Quick Foxes!”
第二个查询不能匹配到,因为full_text字段只存在quick和foxes两个term,不存在 “Quick Foxes!”的term.
第三个查询能够匹配到,full_text字段存在foxes的term.
第四个查询能够匹配到,match查询首先分析查询字符串,会在full_text字段查找quick或foxes或quick foxes的term.
Java示例
1
| QueryBuilder qb = termQuery("name","kimchy");
|
Terms Query
基本同term query,可以指定多个term
1 2 3 4 5 6 7
| { "constant_score" : { "filter" : { "terms" : { "user" : ["kimchy", "elasticsearch"]} } } }
|
Terms lookup mechanismedit 参考官方文档
Java示例
1
| QueryBuilder qb = termsQuery("tags", "blue", "pill");
|
Range Query
查询指定范围内的term.如果是字符串字段,使用TermRangeQuery
,如果是数字或日期字段,使用NumericRangeQuery
.
1 2 3 4 5 6 7 8 9
| { "range" : { "age" : { "gte" : 10, "lte" : 20, "boost" : 2.0 } } }
|
支持的参数如下:
参数 |
说明 |
gte |
>= |
gt |
> |
lte |
<= |
lt |
< |
boost |
查询的boost值,默认1.0 |
日期范围
日期示例, 格式参考 date math
1 2 3 4 5 6 7 8
| { "range" : { "date" : { "gte" : "now-1d/d", "lt" : "now/d" } } }
|
日期计算和取整
当使用date math取整日期到最近的日,月,小时等等,被取整的日期取决于范围的结束是否被包括或是不包括
- 向上取整:向上取整到取整域的最后一毫秒
- 向下取整:向下取整到取整域的第一毫秒
参数 |
示例 |
gt |
`2014-11-18 |
|
/M---> 2014-11-30T23:59:59.999` |
gte |
`2014-11-18 |
|
/M---> 2014-11-01` |
lt |
`2014-11-18 |
|
/M---> 2014-11-01` |
lte |
`2014-11-18 |
|
/M---> 2014-11-30T23:59:59.999` |
格式化日期
1 2 3 4 5 6 7 8 9
| { "range" : { "born" : { "gte": "01/01/2012", "lte": "2013", "format": "dd/MM/yyyy||yyyy" } } }
|
Java示例
1 2 3 4 5
| QueryBuilder qb = rangeQuery("price") .from(5) .to(10) .includeLower(true) .includeUpper(false);
|
默认情况下是包含下界不包含上界
Exists Query
判断字段中是否至少有一个非null的值
1 2 3
| { "exists" : { "field" : "user" } }
|
如返回文档中字段name中值为null的文档
1 2 3 4 5 6 7 8 9 10 11
| { "query": { "bool": { "must_not": { "exists": { "field": "name" } } } } }
|
Java示例
1
| QueryBuilder qb = existsQuery("name");
|
Prefix Query
查询指定字段中的term包含指定前缀的文档, 适用在not_analyzed
的字段中,如果使用在analyzed
字段中,分词后的term如果包含指定前缀,也同样会被查询出来.
假如包含以下数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| [ { "_index": "radiott", "_type": "artiststt", "_id": "AVx7JhglJRLJihedvp1b", "_score": 1, "_source": { "name": "abtest333", "id": "3" } }, { "_index": "radiott", "_type": "artiststt", "_id": "AVw0PAb_xJt8Zgd-WGNi", "_score": 1, "_source": { "id": "2", "name": "test222" } }, { "_index": "radiott", "_type": "artiststt", "_id": "AVy_JHcHj7fNaw5JbLcn", "_score": 1, "_source": { "name": "some test", "id": "7" } } ]
|
执行查询
1 2 3 4 5 6 7
| { "query": { "prefix": { "name": "te" } } }
|
结果中test222和some test都会被查询出来
java示例
1
| QueryBuilder qb = prefixQuery("name","heine" );
|
Wildcard Query
通配符查询.查询指定字段符合通配表达式的文档,支持的通配符如下:
*
:匹配任何字符串,包括空字符串
?
:匹配任何单个字符.
通配查询比较慢,它会比较很多的term,为了避免过慢的查询,通配表达式不应该以*
和?
开头.
1 2 3 4 5 6 7 8 9 10 11
| { "wildcard" : { "user" : "ki*y" } } { "wildcard" : { "user" : { "value" : "ki*y", "boost" : 2.0 } } } { "wildcard" : { "user" : { "wildcard" : "ki*y", "boost" : 2.0 } } }
|
Java示例
1
| QueryBuilder qb = wildcardQuery("user", "k?mc*");
|
Regexp Query
使用正则表达式查询term,支持的正则语法参考官方文档 . elasticsearch会对分词器生成的每一个term进行正则比较,而不是字段的原始文本 .
注意:正则查询的性能取决于正则表达式本身 , 使用*
会比较缓慢,如果有可能 , 在正则表达式前尽量添加足够长的前缀 . .*?+
这样的正则表达式会严重影响性能 .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| { "regexp":{ "name.first": "s.*y" } } { "regexp":{ "name.first":{ "value":"s.*y", "boost":1.2 } } } { "regexp":{ "name.first": { "value": "s.*y", "flags" : "INTERSECTION|COMPLEMENT|EMPTY" } } }
|
默认的flags是ALL
, 其它字段意思参考Lucene documentation
更多关于正则查询的说明参考官方文档 .
Java示例
1
| QueryBuilder qb = regexpQuery("name.first","s.*y");
|
Fuzzy Query
模糊查询应用于String,number和date类型字段,使用方式类似于Levenshtein编辑距离 .
注意 : 这和关系型数据库的模糊查询是不一样的
在最大编辑距离内尽可能多的生成term去匹配term字典然后找出在索引中实际存在的term,最大编辑距离由fuzziness
参数指定.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "fuzzy" : { "name" : "tesd" } } { "fuzzy" : { "name" : { "value" : "tesd", "boost" : 1.0, "fuzziness" : 2, "prefix_length" : 0, "max_expansions": 100 } } }
|
产生的结果可能如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| [ { "_index": "radiott", "_type": "artiststt", "_id": "AVy_JHcHj7fNaw5JbLcn", "_score": 0.8784157, "_source": { "name": "some test", "id": "7" } }, { "_index": "radiott", "_type": "artiststt", "_id": "AVyvrqDa6x0lhJNLATQv", "_score": 0.8465736, "_source": { "name": "there is a test", "id": "8" } } ]
|
上例中输入tesd , 能查询到term为test的文档 , 它允许用户输错指定数量的字符 .
支持参数如下
参数 |
说明 |
fuzziness |
最大编辑距离,默认AUTO ,具体参考fuzziness说明 |
prefix_length |
不会被模糊化的初始字符串长度,默认0,能够减少必须检查的terms数量 |
max_expansions |
模糊查询扩大terms的最大数量,默认50 |
注意:如果prefix_length指定为0并且max_expansions指定为一个很大的数会很耗费性能
使用指定的值执行Range查询,范围是+/-
, filedValue-fuzziness <= filedValue <= fuzziness+filedValue
1 2 3 4 5 6 7 8
| { "fuzzy" : { "price" : { "value" : 12, "fuzziness" : 2 } } }
|
最终查询的范围是10-14 . 日期字段支持 time value
1 2 3 4 5 6 7 8
| { "fuzzy" : { "created" : { "value" : "2010-02-05T12:05:07", "fuzziness" : "1d" } } }
|
模糊查询支持更多的值,详情参考 the section called “Fuzzinessedit”
Java示例
1
| QueryBuilder qb = fuzzyQuery("name","kimzhy");
|
Type Query
查询索引中符合指定类型的文档
1 2 3 4 5
| { "type" : { "value" : "my_type" } }
|
Java示例
1
| QueryBuilder qb = typeQuery("my_type");
|
Ids Query
根据id查询,匹配_uid字段
1 2 3 4 5 6
| { "ids" : { "type" : "my_type", "values" : ["1", "4", "100"] } }
|
Java示例
1 2 3
| QueryBuilder qb = idsQuery("my_type", "type2").addIds("1", "4", "100"); QueryBuilder qb = idsQuery().addIds("1", "4", "100");
|
Compound queries
符合查询可以包裹其它符合查询或者叶子查询 , 也可以组合它们的结果 , 改变它们的行为,或者从查询环境切换到过滤环境。
Constant Score Query
一个包裹其它查询的查询,执行在过滤环境中,所有匹配的文档都给一个相同的_score分数,默认1,也可以设置boost值作为 _score分数,并简单返回。常用于计算相关度,可参考一个博客例子。
1 2 3 4 5 6 7 8
| { "constant_score" : { "filter" : { "term" : { "user" : "kimchy"} }, "boost" : 1.2 } }
|
Java示例
1 2 3 4
| QueryBuilder qb = constantScoreQuery( termQuery("name","kimchy") ) .boost(2.0f);
|
Bool Query
常用于组合多个叶子查询或多个符合查询字句,有must
,should
,must_not
,filter
子句。其中must_not和filter子句执行在过滤环境中。Bool Query使用boolean条件组合其它查询,由一个或者多个boolean子句组成,每个子句都有一个资源类型
资源类型如下:
- must :必须有匹配的文档,类似关系数据库的AND
- filter : 必须有匹配的文档,不同于must,查询分数将被忽略
- should : 可以有匹配的文档,类似关系数据库的OR。 在一个没有must或filter的boolean字句中,必须有至少一个should字句匹配文档,可以通过设置minimum_should_match参数设置至少有多少个boolean字句必须匹配文档
- must_not:与must相反
注意:如果boolean查询使用在filter环境并且含有should
子句,则至少有一个should
子句能够匹配到文档。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| { "bool" : { "must" : { "term" : { "user" : "kimchy" } }, "filter": { "term" : { "tag" : "tech" } }, "must_not" : { "range" : { "age" : { "from" : 10, "to" : 20 } } }, "should" : [ { "term" : { "tag" : "wow" } }, { "term" : { "tag" : "elasticsearch" } } ], "minimum_should_match" : 1, "boost" : 1.0 } }
|
关于boolean查询中_score分数的计算参考官方文档。
Java示例
1 2 3 4 5
| QueryBuilder qb = boolQuery() .must(termQuery("content", "test1")) .must(termQuery("content", "test4")) .mustNot(termQuery("content", "test2")) .should(termQuery("content", "test3"));
|
Dis Max Query
类似于Boolean查询的should,组合多个子查询,取子查询中最高的分数作为最终文档的分数。详细说明和示例参考博客:[Elasticsearch] 多字段搜索 (二) - 最佳字段查询及其调优
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "dis_max" : { "tie_breaker" : 0.7, "boost" : 1.2, "queries" : [ { "term" : { "age" : 34 } }, { "term" : { "age" : 35 } } ] } }
|
java示例
1 2 3 4 5
| QueryBuilder qb = disMaxQuery() .add(termQuery("name", "kimchy")) .add(termQuery("name", "elasticsearch")) .boost(1.2f) .tieBreaker(0.7f);
|
Function Score Query
允许用户修改通过查询得到的文档的分数。,使用function_score
,用户可以定义一个或者多个functions,对每一个文档计算一个新的分数。多个function可以进行组合,更多详细内容参考官方文档或博客function_score查询中的filter,functions及random_score参数
基本格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| "function_score": { "query": {}, "boost": "boost for the whole query", "functions": [ { "filter": {}, "FUNCTION": {}, "weight": number }, { "FUNCTION": {} }, { "filter": {}, "weight": number } ], "max_boost": number, "score_mode": "(multiply|max|...)", "boost_mode": "(multiply|replace|...)", "min_score" : number }
|
测试数据中name字段有以下数据:[“test222”,”test333”,”this is test”,”张三”]。查询如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| { "query": { "function_score": { "functions": [ { "filter": { "term": { "name": "test" } }, "weight": 1.5 }, { "filter": { "term": { "name": "test222" } }, "weight": 2 } ] } } }
|
执行上面的查询,查询所有数据,得到的结果顺序如下:[“test222”,”this is test”,”test333”,”张三”],其分数分别是[2,1.5,1,1]。
如果不指定filter,默认查询全部"match_all": {}
。
定义的function将计算每一个内部filter匹配的文档的分数,通过制定score_mode可以决定分数的计算方式。score_mode 参数值如下:
参数名 |
含义 |
multiply |
分数相乘,默认值 |
sum |
分数求和 |
avg |
分数值取平均值 |
first |
应用第一个匹配到的function的分数 |
max |
取最大值 |
min |
取最小值 |
boost_mode 参数说明如下:
| 参数名 | 含义 |
| ——– | ———————- |
| multiply | 查询分数和和function分数相乘,默认值 |
| replace | 忽略查询分数,使用function分数 |
| sum | 查询分数和function分数相加 |
| avg | 取平均值 |
| max | 查询分数和function分数取最大值 |
| min | 查询分数和function分数取最小值 |
随机分值计算random_score
使用当前的查询,拥有相同的_score的结果每次的返回顺序都是相同的。此时引入一定程度的随机性会更好,来保证拥有相同分值的文档都能有同等的展示机会。
我们希望每个用户都能看到一个不同的随机顺序,但是对于相同的用户,当他点击第二页,第三页或者后续页面时,看到的顺序应该是相同的。这就是所谓的一致性随机(Consistently Random)。
random_score函数,它的输出是一个介于0到1之间的数字,当给它提供相同的seed值时,它能够产生一致性随机的结果,这个seed值可以是用户的会话(Session)ID。
注意:在博客中random_score是写在functions数组中,测试时会打乱所有文档的顺序而不是同分数文档的顺序,如果需要打乱同分数文档的顺序,将random_score写在function中
1 2 3 4 5 6 7 8 9 10 11 12 13
| "functions": [ { "filter": { "term": { "name": "test" } }, "weight": 1.5, "random_score": { "seed" : xxx } } ]
|
java示例
1 2 3 4 5 6 7 8
| QueryBuilder qb = functionScoreQuery() .add( matchQuery("name", "kimchy"), randomFunction("ABCDEF") ) .add( exponentialDecayFunction("age", 0L, 1L) );
|
Boosting Query
boosting 查询能够有效地对查询的文档降级,不同于boolean查询的not子句,该查询仍然包含“不良的”term,但是会降低它们的整体分数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "boosting" : { "positive" : { "term" : { "field1" : "value1" } }, "negative" : { "term" : { "field2" : "value2" } }, "negative_boost" : 0.2 } }
|
java示例
1 2 3 4
| QueryBuilder qb = boostingQuery() .positive(termQuery("name","kimchy")) .negative(termQuery("name","dadoonet")) .negativeBoost(0.2f);
|
Indices Query
可以跨多个索引查询,指定一个索引列表和内部的query结构,该query结构仅在指定的索引列表上查找,对于不在指定的列表中但是被查询到的其它索引,可以执行可选的no_match_query
参数。
如下,假设
1 2 3 4 5 6 7 8 9 10 11
| { "indices" : { "indices" : ["index1", "index2"], "query" : { "term" : { "tag" : "wow" } }, "no_match_query" : { "term" : { "tag" : "kow" } } } }
|
no_match_query可以指定为一个字符串值 “none”,表示不在其它索引上执行查询,默认值 “all”。
注意:执行indices查询时,不能在顶层指定索引。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| http://192.168.70.128:9200/radiott/artiststt/_search? -d { "query": { "indices": { "indices": [ "sys" ], "query": { "term": { "user_id": "1" } }, "no_match_query": { "term": { "name": "test" } } } } }
|
在上例中,指定了索引为radiott,类型为artiststt,是无法再在索引sys查找user_id为 “1”的文档的,只会执行not_match_query的查询。
Java示例如下:
1 2 3 4 5 6 7 8 9
| QueryBuilder builder = QueryBuilders.indicesQuery(QueryBuilders.termQuery("user_id","1"),"sys") .noMatchQuery(QueryBuilders.termQuery("name","test")); SearchResponse res = client.prepareSearch().setQuery(builder).execute().actionGet(); QueryBuilder qb = indicesQuery( termQuery("tag", "wow"), "index1", "index2" ).noMatchQuery("all");
|
Joining queries
详情见官方文档
在类似于elasticsearch这样的分布式系统中实现全功能的SQL是非常非常耗费性能的,作为替代,elasticsearch提供了两种形式的join,可以实现横向扩展。
文档包含类型是nested
的字段,这些字段索引了对象数组,每一个对象作为一个独立的文档都能被查询(使用nested query)。
- has_child和has_parent queries
在一个索引中两个文档类型可以是父子关系,has_child
查询返回父文档中符合匹配条件的子文档。has_parent
查询返回子文档中符合匹配条件的父文档。
nested query
nested查询可以查询嵌入的对象或文档(嵌入数据映射见nested mapping)。该查询执行在被嵌入的对象或文档上,就好像嵌入的文档或对象是被独立索引的(实际上还是内部的),最终查询结果展示在父文档的根下(或是父文档的嵌入结构)。
定义数据结构
1 2 3 4 5 6 7 8 9 10 11 12
| PUT my_index { "mappings": { "my_type": { "properties": { "user": { "type": "nested" } } } } }
|
插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| PUT my_index/my_type/1 { "group" : "fans", "user" : [ { "first" : "John", "last" : "Smith" }, { "first" : "Alice", "last" : "White" } ] }
|
查询语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| GET my_index/_search { "query": { "nested": { "path": "user", "score_mode" : "avg", "query": { "bool": { "must": [ { "match": { "user.first": "Alice" }}, { "match": { "user.last": "Smith" }} ] } }, "inner_hits": { "highlight": { "fields": { "user.first": {} } } } } } }
|
在上面的示例中,path
指出嵌入文档的路径,query
包含了将要执行在嵌入文档上的查询,然后和父文档连接。需要注意的是任何关联到内部查询的字段都必须使用全路径。
score_mode
允许用户定义匹配的嵌入文档怎样影响关联父文档的分数,默认avg
,也可以是sum
,min
,max
和none
。
支持多级嵌套和检索,内部嵌套的查询将会自动匹配对应的嵌入层级。
Java 示例
1 2 3 4 5 6 7
| QueryBuilder qb = nestedQuery( "obj1", boolQuery() .must(matchQuery("obj1.name", "blue")) .must(rangeQuery("obj1.count").gt(5)) ) .scoreMode("avg");
|
has child query
关于父子类型的映射关系参考官方文档 _parent field。
has_child
查询接受一个query参数和一个child type,然后将查询结果展示在父文档中。
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "has_child" : { "type" : "blog_tag", "score_mode" : "sum", "min_children": 2, "max_children": 10, "query" : { "term" : { "tag" : "something" } } } }
|
score_mode
包含min
,max
,avg
,none
,默认none
。
min_children
和max_children
可以指定父文档必须匹配子文档的最小/最大数量,可选值,可结合score_mode
一起使用
- 父文档不能直接使用子文档中的字段进行排序,如果需要根据子文档的字段排序,可以使用
function_score
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "query": { "has_child" : { "type" : "blog_tag", "score_mode" : "max", "query" : { "function_score" : { "script_score": { "script": "_score * doc['click_count'].value" } } } } } }
|
java 示例
1 2 3 4
| QueryBuilder qb = hasChildQuery( "blog_tag", termQuery("tag","something") );
|
has parent query
has_parent
查询接受一个query参数和一个parent type,query对应的查询执行在指定的父文档空间中,查询返回与父文档关联的子文档,has_parent
参数和工作方式与has_child
保持一致。
1 2 3 4 5 6 7 8 9 10
| { "has_parent" : { "parent_type" : "blog", "query" : { "term" : { "tag" : "something" } } } }
|
分数计算和排序参考has_child
。
Java示例
1 2 3 4
| QueryBuilder qb = hasParentQuery( "blog", termQuery("tag","something") );
|
Geo queries
elasticsearch支持两种geo类型数据,geo_point 和 geo_shape。geo_point支持lat/lon数据对,geo_shape支持点、线、圆、多边形、复合多边形等等。
geo shape query
通过指定的geo_shape 查找与文档中的 geo_shape 的交集、包含或是相离。关于geo_shape mapping参考官方文档
注意:geo_shape类型使用Spatial4J
和JTS
,两者都是可选的依赖,如果使用了geo_shape类型确保引入了以下依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependency> <groupId>com.spatial4j</groupId> <artifactId>spatial4j</artifactId> <version>0.4.1</version> </dependency> <dependency> <groupId>com.vividsolutions</groupId> <artifactId>jts</artifactId> <version>1.13</version> <exclusions> <exclusion> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> </exclusion> </exclusions> </dependency>
|
geo_shape查询使用坐标网格表示法定义字段映射检索文档。它也可以使用同样的的PrefixTree配置定义字段映射。该查询支持两种方式定义形状,一种是提供完整的形状定义,另一种是引用其它索引定义的形状的名称。
内联形状定义
使用GeoJSON表示形状。
1 2 3 4 5 6 7
| { "name": "Wind & Wetter, Berlin, Germany", "location": { "type": "Point", "coordinates": [13.400544, 52.530286] } }
|
下面的例子使用elasticsearch的envelope
GeoJSON扩展查找上面的文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| { "query":{ "bool": { "must": { "match_all": {} }, "filter": { "geo_shape": { "location": { "shape": { "type": "envelope", "coordinates" : [[13.0, 53.0], [14.0, 52.0]] }, "relation": "within" } } } } } }
|
预定义形状
geo_shape查询也支持预先被其它索引或类型定义的形状,在应用程序中,使用预先定义的形状名称(如新西兰地图)比每次都提供其坐标点集要方便得多。在这种情况下,需要提供以下参数:
- id:包含预定义形状的文档的id
- index:包含预定义形状的索引,默认是
shapes
- type:包含预定义形状的索引类型
- path:存储预定义形状的字段名称,默认是
shape
下面的例子是使用过滤器筛选形状
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "bool": { "must": { "match_all": {} }, "filter": { "geo_shape": { "location": { "indexed_shape": { "id": "DEU", "type": "countries", "index": "shapes", "path": "location" } } } } } }
|
空间关系
geo_shape策略映射参数定义了在查询时可能用到的空间关系操作
- INTERSECTS:默认值,返回所有与查询形状相交的文档
- DISJOINT :返回所有与查询形状相离的文档
- WITHIN:返回所有在查询形状之内的文档
- CONTAINS:返回所有包含查询形状的文档
Java示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| QueryBuilder qb = geoShapeQuery( "pin.location", ShapeBuilder.newMultiPoint() .point(0, 0) .point(0, 10) .point(10, 10) .point(10, 0) .point(0, 0), ShapeRelation.WITHIN); QueryBuilder qb = geoShapeQuery( "pin.location", "DEU", "countries", ShapeRelation.WITHIN) .indexedShapeIndex("shapes") .indexedShapePath("location");
|
Geo Bounding Box Query
查找所有指定字段的定位点在边界盒内的文档
假设一份文档数据如下:
1 2 3 4 5 6 7 8
| { "pin" : { "location" : { "lat" : 40.12, "lon" : -71.34 } } }
|
简单的查询示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| { "bool" : { "must" : { "match_all" : {} }, "filter" : { "geo_bounding_box" : { "pin.location" : { "top_left" : { "lat" : 40.73, "lon" : -74.1 }, "bottom_right" : { "lat" : 40.01, "lon" : -71.12 } } } } } }
|
可以接受的数据格式
lat/lon格式,推荐格式,最直观最安全
1 2 3 4
| "pin.location" : { "top_left" : {"lat" : 40.73,"lon" : -74.1}, "bottom_right":{"lat" : 40.01,"lon" : -71.12} }
|
[lon,lat]
数组格式
1 2 3 4
| "pin.location" : { "top_left" : [-74.1, 40.73], "bottom_right" : [-71.12, 40.01] }
|
lat,lon
字符串格式
1 2 3 4
| "pin.location" : { "top_left" : "40.73, -74.1", "bottom_right" : "40.01, -71.12" }
|
geohash格式
1 2 3 4
| "pin.location" : { "top_left" : "dr5r9ydj2y73", "bottom_right" : "drj7teegpus6" }
|
vertices 顶点格式
1 2 3 4 5 6
| "pin.location" : { "top" : -74.1, "left" : 40.73, "bottom" : -71.12, "right" : 40.01 }
|
java示例
1 2 3
| QueryBuilder qb = geoBoundingBoxQuery("pin.location") .topLeft(40.73, -74.1) .bottomRight(40.717, -73.99);
|
Geo Distance Query
查询文档中geo_point与指定geo_point间的距离在指定长度内的文档。
假设有以下数据
1 2 3 4 5 6 7 8
| { "pin" : { "location" : { "lat" : 40.12, "lon" : -71.34 } } }
|
测试查询如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| { "bool" : { "must" : { "match_all" : {} }, "filter" : { "geo_distance" : { "distance" : "200km", "pin.location" : { "lat" : 40, "lon" : -70 } } } } }
|
接受的定位数据格式与geo bounding box query一致。其它可选参数如下:
Java示例
1 2 3 4 5
| QueryBuilder qb = geoDistanceQuery("pin.location") .point(40, -70) .distance(200, DistanceUnit.KILOMETERS) .optimizeBbox("memory") .geoDistance(GeoDistance.ARC);
|
Geo Distance Range Query
查询与指定坐标点在指定范围内的文档,支持的参数同geo_distance查询,也支持范围查询的通用参数如lt
,lte
,gt
,gte
,from
,to
,include_upper
,include_lower
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { "bool" : { "must" : { "match_all" : {} }, "filter" : { "geo_distance_range" : { "from" : "200km", "to" : "400km", "pin.location" : { "lat" : 40, "lon" : -70 } } } } }
|
Java示例
1 2 3 4 5 6 7 8
| QueryBuilder qb = geoDistanceRangeQuery("pin.location") .point(40, -70) .from("200km") .to("400km") .includeLower(true) .includeUpper(false) .optimizeBbox("memory") .geoDistance(GeoDistance.ARC);
|
Geo Polygon Query
查询坐标点落在指定多边形内的文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { "bool" : { "query" : { "match_all" : {} }, "filter" : { "geo_polygon" : { "person.location" : { "points" : [ {"lat" : 40, "lon" : -70}, {"lat" : 30, "lon" : -80}, {"lat" : 20, "lon" : -90} ] } } } } }
|
支持的坐标数据格式同geo boundingbox query,也可参考官方文档
Java示例
1 2 3 4
| QueryBuilder qb = geoPolygonQuery("pin.location") .addPoint(40, -70) .addPoint(30, -80) .addPoint(20, -90);
|
Geohash Cell Query
参考官方文档。
Java示例参考官方文档。
Specialized queries