天天看點

ElasticSearch5.X搜尋條件的聚合(六)

範圍限定的聚合

(測試資料:http://blog.csdn.net/wwd0501/article/details/78501842)所有聚合的例子到目前為止,你可能已經注意到,我們的搜尋請求省略了一個 

query

 。 整個請求隻不過是一個聚合。

聚合可以與搜尋請求同時執行,但是我們需要了解一個新概念: 範圍 。 預設情況下,聚合與查詢是對同一範圍進行操作的,也就是說,聚合是基于我們查詢比對的文檔集合進行計算的。

讓我們看看第一個聚合的示例:

GET /cars/transactions/_search
{
    "size" : 0,
    "aggs" : {
        "colors" : {
            "terms" : {
              "field" : "color"
            }
        }
    }
}
           

我們可以看到聚合是隔離的。現實中,Elasticsearch 認為 "沒有指定查詢" 和 "查詢所有文檔" 是等價的。前面這個查詢内部會轉化成下面的這個請求:

GET /cars/transactions/_search
{
    "size" : 0,
    "query" : {
        "match_all" : {}
    },
    "aggs" : {
        "colors" : {
            "terms" : {
              "field" : "color"
            }
        }
    }
}
           

因為聚合總是對查詢範圍内的結果進行操作的,是以一個隔離的聚合實際上是在對 

match_all

 的結果範圍操作,即所有的文檔。

一旦有了範圍的概念,我們就能更進一步對聚合進行自定義。我們前面所有的示例都是對 所有 資料計算統計資訊的:銷量最高的汽車,所有汽車的平均售價,最佳銷售月份等等。

利用範圍,我們可以問“福特在售車有多少種顔色?”諸如此類的問題。可以簡單的在請求中加上一個查詢(本例中為 

match

 查詢):

GET /cars/transactions/_search
{
    "query" : {
        "match" : {
            "make" : "ford"
        }
    },
    "aggs" : {
        "colors" : {
            "terms" : {
              "field" : "color"
            }
        }
    }
}
           

因為我們沒有指定 

"size" : 0

 ,是以搜尋結果和聚合結果都被傳回了:

{
...
   "hits": {
      "total": 2,
      "max_score": 1.6931472,
      "hits": [
         {
            "_source": {
               "price": 25000,
               "color": "blue",
               "make": "ford",
               "sold": "2014-02-12"
            }
         },
         {
            "_source": {
               "price": 30000,
               "color": "green",
               "make": "ford",
               "sold": "2014-05-18"
            }
         }
      ]
   },
   "aggregations": {
      "colors": {
         "buckets": [
            {
               "key": "blue",
               "doc_count": 1
            },
            {
               "key": "green",
               "doc_count": 1
            }
         ]
      }
   }
}
           

看上去這并沒有什麼,但卻對高大上的儀表盤來說至關重要。 加入一個搜尋欄可以将任何靜态的儀表闆變成一個實時資料搜尋裝置。 這讓使用者可以搜尋資料,檢視所有實時更新的圖形(由于聚合的支援以及對查詢範圍的限定)。 這是 Hadoop 無法做到的!

Java代碼實作:

/**
  	  * Description:帶有搜尋條件的聚合查詢
  	  * 例:bmw(寶馬)在售車有多少種顔色
  	  * 
  	  * @author wangweidong
  	  * CreateTime: 2017年11月10日 下午4:03:30
  	  *
  	 */
  	@Test
  	public void searchBucketsAggregation() {
  		String index = "cars";
  		String type = "transactions";
  		SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index).setTypes(type);
  		
  		//搜尋條件
  		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
  		queryBuilder.must(QueryBuilders.matchQuery("make", "bmw")); 
  		
  		//聚合
	    TermsAggregationBuilder field = AggregationBuilders.terms("popular_colors").field("color.keyword");
	    searchRequestBuilder.addAggregation(field);
//	    searchRequestBuilder.setSize(0);
	    searchRequestBuilder.setQuery(queryBuilder);
	    SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
	     
	    System.out.println(searchResponse.toString());
	    
	    Terms genders = searchResponse.getAggregations().get("popular_colors");
	    for (Terms.Bucket entry : genders.getBuckets()) {
	    	Object key = entry.getKey();      // Term
	        Long count = entry.getDocCount(); // Doc count
	        
	        System.out.println(key);
	        System.out.println(count);
	    }
  	}
           

全局桶

通常我們希望聚合是在查詢範圍内的,但有時我們也想要搜尋它的子集,而聚合的對象卻是 所有 資料。

例如,比方說我們想知道福特汽車與 所有 汽車平均售價的比較。我們可以用普通的聚合(查詢範圍内的)得到第一個資訊,然後用 

全局

 桶獲得第二個資訊。

全局

 桶包含 所有 的文檔,它無視查詢的範圍。因為它還是一個桶,我們可以像平常一樣将聚合嵌套在内:

GET /cars/transactions/_search
{
    "size" : 0,
    "query" : {
        "match" : {
            "make" : "ford"
        }
    },
    "aggs" : {
        "single_avg_price": {
            "avg" : { "field" : "price" } 
        },
        "all": {
            "global" : {}, 
            "aggs" : {
                "avg_price": {
                    "avg" : { "field" : "price" } 
                }

            }
        }
    }
}
        },
        "all": {
            "global" : {}, 
            "aggs" : {
                "avg_price": {
                    "avg" : { "field" : "price" } 
                }

            }
        }
    }
}
           
聚合操作在查詢範圍内(例如:所有文檔比對 

ford

 )

global

 全局桶沒有參數。
聚合操作針對所有文檔,忽略汽車品牌。

single_avg_price

 度量計算是基于查詢範圍内所有文檔,即所有 

福特

 汽車。

avg_price

 度量是嵌套在 

全局

 桶下的,這意味着它完全忽略了範圍并對所有文檔進行計算。聚合傳回的平均值是所有汽車的平均售價。

如果能一直堅持讀到這裡,應該知道我們有個真言:盡可能的使用過濾器。它同樣可以應用于聚合,在下一章中,我們會展示如何對聚合結果進行過濾而不是僅對查詢範圍做限定。

Java代碼實作:

/**
  	 * Description:全局桶
  	 * 例:想知道福特(ford)汽車與 所有 汽車平均售價的比較
  	 * 
  	 * @author wangweidong
  	 * CreateTime: 2017年11月10日 下午4:03:30
  	 *
  	 */
  	@Test
  	public void globalBucketsAggregation() {
  		try {
  			String index = "cars";
  	  		String type = "transactions";
  	  		SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index).setTypes(type);
  	  		
  	  		//搜尋條件
  	  		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
  	  		queryBuilder.must(QueryBuilders.matchQuery("make", "ford")); 
  	  		
  	  		//平均價格
  	  		AvgAggregationBuilder singleAvgPrice = AggregationBuilders.avg("single_avg_price").field("price");
  	  		
  	  		//聚合
  	  		GlobalAggregationBuilder field = AggregationBuilders.global("all");
  	        AvgAggregationBuilder avgPrice = AggregationBuilders.avg("avg_price").field("price");
  	        field.subAggregation(avgPrice);
  	  		
  	        searchRequestBuilder.addAggregation(singleAvgPrice);
  	  		searchRequestBuilder.addAggregation(field);
  		    searchRequestBuilder.setSize(0);
  	  		searchRequestBuilder.setQuery(queryBuilder);
  	  		SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
  	  		
  	  		System.out.println(searchResponse.toString());
  	  		
  	  		Avg avg = searchResponse.getAggregations().get("single_avg_price");
  	  		System.out.println("福特(ford)汽車平均價格:" + avg.getValue());
  	  		
  	  		Global global = searchResponse.getAggregations().get("all");
  	  		Avg allAvg = global.getAggregations().get("avg_price");
  	  		System.out.println("所有汽車平均價格:" + allAvg.getValue());
		} catch (Exception e) {
			e.printStackTrace();
		}
  	}
           

繼續閱讀