preference
查詢選擇副本分片的傾向性(即在一個複製組中選擇副本的分片值。默認情況下,es以未指定的順序從可用的碎片副本中進行選擇,副本之間的路由將在集羣章節更加詳細的介紹 。可以通過該字段指定分片傾向與選擇哪個副本。preference可選值:
- _primary
只在節點上執行,在6.1.0版本後廢棄,將在7.x版本移除。 - _primary_first
優先在主節點上執行。在6.1.0版本後廢棄,將在7.x版本移除。 - _replica
操作只在副本分片上執行,如果有多個副本,其順序隨機。在6.1.0版本後廢棄,將在7.x版本移除。 - _replica_first
優先在副本分片上執行,如果有多個副本,其順序隨機。在6.1.0版本後廢棄,將在7.x版本移除。 - _only_local
操作將只在分配給本地節點的分片上執行。_only_local選項保證只在本地節點上使用碎片副本,這對於故障排除有時很有用。所有其他選項不能完全保證在搜索中使用任何特定的碎片副本,而且在索引更改時,這可能意味着如果在處於不同刷新狀態的不同碎片副本上執行重複搜索,則可能產生不同的結果。 - _local
優先在本地分片上執行。 - _prefer_nodes:abc,xyz
優先在指定節點ID的分片上執行,示例中的節點ID爲abc、xyz。 - shards:2,3
將操作限制到指定的分片上執行。(這裏是2和3)這個首選項可以與其他首選項組合,但必須首先出現
-shards:2,3|_local。 - _only_nodes:abc,xyz,…
根據節點ID進行限制。
Custom (string) value
自定義字符串,其路由爲 hashcod-e(該值)%賦值組內節點數。例如在web應用中通常以sessionId爲傾向值。
explain
是否解釋各分數是如何計算的。
1GET /_search
2{
3 "explain": true,
4 "query" : {
5 "term" : { "user" : "kimchy" }
6 }
7}
version
如果設置爲true,則返回每個命中文檔的當前版本號。
1GET /_search
2{
3 "version": true,
4 "query" : {
5 "term" : { "user" : "kimchy" }
6 }
7}
Index Boost
當搜索多個索引時,允許爲每個索引配置不同的boost級別。當來自一個索引的點擊率比來自另一個索引的點擊率更重要時,該屬性則非常方便。
使用示例如下:
1GET /_search
2{
3 "indices_boost" : [
4 { "alias1" : 1.4 },
5 { "index*" : 1.3 }
6 ]
7}
min_score
指定返回文檔的最小評分,如果文檔的評分低於該值,則不返回。
1GET /_search
2{
3 "min_score": 0.5,
4 "query" : {
5 "term" : { "user" : "kimchy" }
6 }
7}
Named Queries
每個過濾器和查詢都可以在其頂級定義中接受_name。搜索響應中每個匹配文檔中會增加matched_queries結構體,記錄該文檔匹配的查詢名稱。查詢和篩選器的標記只對bool查詢有意義。
java示例如下:
1public static void testNamesQuery() {
2 RestHighLevelClient client = EsClient.getClient();
3 try {
4 SearchRequest searchRequest = new SearchRequest();
5 searchRequest.indices("esdemo");
6 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
7 sourceBuilder.query(
8 QueryBuilders.boolQuery()
9 .should(QueryBuilders.termQuery("context", "fox").queryName("q1"))
10 .should(QueryBuilders.termQuery("context", "brown").queryName("q2"))
11 );
12 searchRequest.source(sourceBuilder);
13 SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
14 System.out.println(result);
15 } catch (Throwable e) {
16 e.printStackTrace();
17 } finally {
18 EsClient.close(client);
19 }
20 }
返回結果如下:
1{
10 "hits":{
19 "_source":{
20 "context":"My quick brown as fox eats rabbits on a regular basis.",
21 "title":"Keeping pets healthy"
22 },
23 "matched_queries":[
24 "q1",
25 "q2"
26 ]
27 }
41 ]
42 }
43}
正如上面所說,每個匹配文檔中都包含matched_queries,表明該文檔匹配的是哪個查詢條件。
Inner hits
用於定義內部嵌套層的返回規則,其inner hits支持如下選項:
- from 用於內部匹配的分頁。
- size 用於內部匹配的分頁,size。
- sort 排序策略。
- name 爲內部嵌套層定義的名稱。
該部分示例將在下節重點闡述。
field collapsing(字段摺疊)
允許根據字段值摺疊搜索結果。摺疊是通過在每個摺疊鍵上只選擇排序最高的文檔來完成的。有點類似於聚合分組,其效果類似於按字段進行分組,默認命中的文檔列表第一層由該字段的第一條信息,也可以通過允許根據字段值摺疊搜索結果。摺疊是通過在每個摺疊鍵上只選擇排序最高的文檔來完成的。例如下面的查詢爲每個用戶檢索最佳twee-t,並按喜歡的數量對它們進行排序。
下面首先通過示例進行展示field colla-psing的使用。
1)首先查詢發的推特內容中包含elast-icsearch的推文:
1GET /twitter/_search
2{
3 "query": {
4 "match": {
5 "message": "elasticsearch"
6 }
7 },
8 "collapse" : {
9 "field" : "user"
10 },
11 "sort": ["likes"]
12}
返回結果:
1{
13 "hits":[
14 {
15 "_index":"mapping_field_collapsing_twitter",
16 "_type":"_doc",
17 "_id":"OYnecmcB-IBeb8B-bF2X",
18 "_score":null,
19 "_source":{
20 "message":"to be a elasticsearch",
21 "user":"user2",
22 "likes":3
23 },
24 "sort":[
25 3
26 ]
27 },
84 ]
85 }
86}
首先上述會列出所有用戶的推特,如果只想每個用戶只顯示一條推文,並且點贊率最高,或者每個用戶只顯示2條推文呢?這個時候,按字段摺疊就閃亮登場了。java demo如下:
1public static void search_field_collapsing() {
2 RestHighLevelClient client = EsClient.getClient();
3 try {
4 SearchRequest searchRequest = new SearchRequest();
5 searchRequest.indices("mapping_field_collapsing_twitter");
6 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
7 sourceBuilder.query(
8 QueryBuilders.matchQuery("message","elasticsearch")
9 );
10 sourceBuilder.sort("likes", SortOrder.DESC);
11 CollapseBuilder collapseBuilder = new CollapseBuilder("user");
12 sourceBuilder.collapse(collapseBuilder);
13 searchRequest.source(sourceBuilder);
14 SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
15 System.out.println(result);
16 } catch (Throwable e) {
17 e.printStackTrace();
18 } finally {
19 EsClient.close(client);
20 }
21 }
其結果如下:
1{
10 "hits":{
11
13 "hits":[
14 {
15 "_index":"mapping_field_collapsing_twitter",
16 "_type":"_doc",
17 "_id":"OYnecmcB-IBeb8B-bF2X",
18 "_score":null,
19 "_source":{
20 "message":"to be a elasticsearch",
21 "user":"user2",
22 "likes":3
23 },
24 "fields":{
25 "user":[
26 "user2"
27 ]
28 },
29 "sort":[
30 3
31 ]
32 },
33 {
34 "_index":"mapping_field_collapsing_twitter",
35 "_type":"_doc",
36 "_id":"OInecmcB-IBeb8B-bF2G",
37 "_score":null,
38 "_source":{
39 "message":"elasticsearch is very high",
40 "user":"user1",
41 "likes":3
42 },
43 "fields":{
44 "user":[
45 "user1"
46 ]
47 },
48 "sort":[
49 3
50 ]
51 }
52 ]
53 }
54}
上面的示例只返回了每個用戶的第一條數據,如果需要每個用戶返回2條數據呢?可以通過inner_hit來設置。
1public static void search_field_collapsing() {
2 RestHighLevelClient client = EsClient.getClient();
3 try {
4 SearchRequest searchRequest = new SearchRequest();
5 searchRequest.indices("mapping_field_collapsing_twitter");
6 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
7 sourceBuilder.query(
8 QueryBuilders.matchQuery("message","elasticsearch")
9 );
10 sourceBuilder.sort("likes", SortOrder.DESC);
11 CollapseBuilder collapseBuilder = new CollapseBuilder("user");
12
13 InnerHitBuilder collapseHitBuilder = new InnerHitBuilder("collapse_inner_hit");
14 collapseHitBuilder.setSize(2);
15 collapseBuilder.setInnerHits(collapseHitBuilder);
16 sourceBuilder.collapse(collapseBuilder);
17
18 searchRequest.source(sourceBuilder);
19 SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
20 System.out.println(result);
21 } catch (Throwable e) {
22 e.printStackTrace();
23 } finally {
24 EsClient.close(client);
25 }
26 }
返回結果如下:
1{
2 "took":42,
3 "timed_out":false,
4 "_shards":{
5 "total":5,
6 "successful":5,
7 "skipped":0,
8 "failed":0
9 },
10 "hits":{
11 "total":5,
12 "max_score":null,
13 "hits":[
14 {
15 "_index":"mapping_field_collapsing_twitter",
16 "_type":"_doc",
17 "_id":"OYnecmcB-IBeb8B-bF2X",
18 "_score":null,
19 "_source":{
20 "message":"to be a elasticsearch",
21 "user":"user2",
22 "likes":3
23 },
24 "fields":{
25 "user":[
26 "user2"
27 ]
28 },
29 "sort":[
30 3
31 ],
32 "inner_hits":{
33 "collapse_inner_hit":{
34 "hits":{
35 "total":2,
36 "max_score":0.19363807,
37 "hits":[
38 {
39 "_index":"mapping_field_collapsing_twitter",
40 "_type":"_doc",
41 "_id":"OonecmcB-IBeb8B-bF2q",
42 "_score":0.19363807,
43 "_source":{
44 "message":"to be elasticsearch",
45 "user":"user2",
46 "likes":3
47 }
48 },
49 {
50 "_index":"mapping_field_collapsing_twitter",
51 "_type":"_doc",
52 "_id":"OYnecmcB-IBeb8B-bF2X",
53 "_score":0.17225473,
54 "_source":{
55 "message":"to be a elasticsearch",
56 "user":"user2",
57 "likes":3
58 }
59 }
60 ]
61 }
62 }
63 }
64 },
65 {
66 "_index":"mapping_field_collapsing_twitter",
67 "_type":"_doc",
68 "_id":"OInecmcB-IBeb8B-bF2G",
69 "_score":null,
70 "_source":{
71 "message":"elasticsearch is very high",
72 "user":"user1",
73 "likes":3
74 },
75 "fields":{
76 "user":[
77 "user1"
78 ]
79 },
80 "sort":[
81 3
82 ],
83 "inner_hits":{
84 "collapse_inner_hit":{
85 "hits":{
86 "total":3,
87 "max_score":0.2876821,
88 "hits":[
89 {
90 "_index":"mapping_field_collapsing_twitter",
91 "_type":"_doc",
92 "_id":"O4njcmcB-IBeb8B-Rl2H",
93 "_score":0.2876821,
94 "_source":{
95 "message":"elasticsearch is high db",
96 "user":"user1",
97 "likes":1
98 }
99 },
100 {
101 "_index":"mapping_field_collapsing_twitter",
102 "_type":"_doc",
103 "_id":"N4necmcB-IBeb8B-bF0n",
104 "_score":0.2876821,
105 "_source":{
106 "message":"very likes elasticsearch",
107 "user":"user1",
108 "likes":1
109 }
110 }
111 ]
112 }
113 }
114 }
115 }
116 ]
117 }
118}
此時,返回結果是兩級,第一級,還是每個用戶第一條消息,然後再內部中嵌套inner_hits。
Search After
Elasticsearch支持的第三種分頁獲取方式,該方法不支持跳轉頁面。
es支持的分頁方式目前已知:
- 通過from和size,當時當達到深度分頁時,成本變的非常高昂,故es提供了索引參數:index.max_result_window來控制(from + size)的最大值,默認爲10000,超過該值後將報錯。
- 通過scroll滾動API,該方式類似於快照的工作方式,不具備實時性,並且滾動上下文的存儲需要耗費一定的性能。
本節將介紹第3種分頁方式,search after,基於上一頁查詢的結果進行下一頁數據的查詢。基本思想是選擇一組排序字段,能做到全局唯一。es的排序查詢響應結果中會返回sort數組,包含本排序字段的最大值,下一頁查詢將該組字段當成查詢條件,es在此數據的基礎下返回下一批合適的數據。
java示例如下:
1public static void search_search_after() {
2 RestHighLevelClient client = EsClient.getClient();
3 try {
4 SearchRequest searchRequest = new SearchRequest();
5 searchRequest.indices("mapping_search_after");
6 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
7 sourceBuilder.query(
8 QueryBuilders.termQuery("user","user2")
9 );
10 sourceBuilder.size(1);
11 sourceBuilder.sort("id", SortOrder.ASC);
12 searchRequest.source(sourceBuilder);
13 SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
14 System.out.println(result);
15 if(hasHit(result)) { // 如果本次匹配到數據
16 // 省略處理數據邏輯
17 // 繼續下一批查詢
18 // result.getHits().
19 int length = result.getHits().getHits().length;
20 SearchHit aLastHit = result.getHits().getHits()[length - 1];
21 //開始下一輪查詢
22 sourceBuilder.searchAfter(aLastHit.getSortValues());
23 result = client.search(searchRequest, RequestOptions.DEFAULT);
24 System.out.println(result);
25 }
26 } catch (Throwable e) {
27 e.printStackTrace();
28 } finally {
29 EsClient.close(client);
30 }
31 }
32 private static boolean hasHit(SearchResponse result) {
33 return !( result.getHits() == null ||
34 result.getHits().getHits() == null ||
35 result.getHits().getHits().length < 1 );
36 }
更多文章請關注微信公衆號:
廣告:作者新書《RocketMQ技術內幕》已上市
《RocketMQ技術內幕》已出版上市,目前可在主流購物平臺(京東、天貓等)購買,本書從源碼角度深度分析了RocketMQ NameServer、消息發送、消息存儲、消息消費、消息過濾、主從同步HA、事務消息;在實戰篇重點介紹了RocketMQ運維管理界面與當前支持的39個運維命令;並在附錄部分羅列了RocketMQ幾乎所有的配置參數。本書得到了RocketMQ創始人、阿里巴巴Messaging開源技術負責人、Linux OpenMessaging 主席的高度認可並作序推薦。目前是國內第一本成體系剖析RocketMQ的書籍。