ElasticSearch(九) Search API


一、termQuery   精確匹配  相當於=
二、rangeQuery 范圍 相當於SQL between and
SearchResponse response = client.prepareSearch("index1", "index2")
        .setTypes("type1", "type2")
        .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
        .setQuery(QueryBuilders.termQuery("multi", "test"))                 // Query
        .setPostFilter(QueryBuilders.rangeQuery("age").from(12).to(18))     // Filter
        .setFrom(0).setSize(60).setExplain(true)
        .get();

三、循環執行 Using scrolls in Java

QueryBuilder qb = termQuery("multi", "test");

SearchResponse scrollResp = client.prepareSearch(test)
        .addSort(FieldSortBuilder.DOC_FIELD_NAME, SortOrder.ASC)
        .setScroll(new TimeValue(60000))
        .setQuery(qb)
        .setSize(100).get(); //max of 100 hits will be returned for each scroll
//Scroll until no hits are returned
do {
    for (SearchHit hit : scrollResp.getHits().getHits()) {
        //Handle the hit...
    }

    scrollResp = client.prepareSearchScroll(scrollResp.getScrollId()).setScroll(new TimeValue(60000)).execute().actionGet();
} while(scrollResp.getHits().getHits().length != 0); // Zero hits mark the end of the scroll and the while loop.

四、MultiSearch API
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)
        .get();

// You will get all individual responses from MultiSearchResponse#getResponses()
long nbHits = 0;
for (MultiSearchResponse.Item item : sr.getResponses()) {
    SearchResponse response = item.getResponse();
    nbHits += response.getHits().getTotalHits();
}
五、Using Aggregations 聚合相當於 group by

GET /index/type/_search  

{  "size" : 0,  

  "aggs": {  

    "agg_sex": {  

      "terms": {  

        "field": "sex"  

                 },  

      "agg_age": {   

        "avg_age": {   

            "avg": {  

              "field": "age"   

                         }  

                      }  

               }  

      }  

   }  

  1. }  
  2. 返回:
  3. {  
  4. ...  
  5.    "aggregations": {  
  6.       "agg_sex": {  
  7.          "buckets": [  
  8.             {  
  9.                "key""male",  
  10.                "doc_count"4,  
  11.                "avg_age": {   
  12.                   "value"25  
  13.                }  
  14.             },  
  15.             {  
  16.                "key""female",  
  17.                "doc_count"2,  
  18.                "avg_age": {  
  19.                   "value"23  
  20.                }  
  21.             }  
  22.          ]  
  23.       }  
  24.    }  
SearchResponse sr = client.prepareSearch()
    .setQuery(QueryBuilders.matchAllQuery())
    .addAggregation(
            AggregationBuilders.terms("agg1").field("field")
    )
    .addAggregation(
            AggregationBuilders.dateHistogram("agg2")
                    .field("birth")
                    .dateHistogramInterval(DateHistogramInterval.YEAR)
    )
    .get();

// Get your facet results
Terms agg1 = sr.getAggregations().get("agg1");
Histogram agg2 = sr.getAggregations().get("agg2");
 


  1. {  
  2. ...  
  3.    "aggregations": {  
  4.       "agg_sex": {  
  5.          "buckets": [  
  6.             {  
  7.                "key""male",  
  8.                "doc_count"4,  
  9.                "avg_age": {   
  10.                   "value"25  
  11.                }  
  12.             },  
  13.             {  
  14.                "key""female",  
  15.                "doc_count"2,  
  16.                "avg_age": {  
  17.                   "value"23  
  18.                }  
  19.             }  
  20.          ]  
  21.       }  
  22.    }  
  23. ...  
  24. }  

在度量聚合里面有min,max,sum,avg聚合等,還有stats,extern_stats等聚合,其中stats的聚合給出的信息會包括min,max,count等基本的信息,更多詳細的細節請參考ES官網給出的指導https://www.elastic.co/guide/en/elasticsearch/guide/current/aggregations.html

以上只是給出的度量聚合,但是在實際中我們經常使用的是桶聚合,什么是桶聚合呢,個人理解就是將符合某一類條件的文檔選出來,所有的某一類的聚合就稱為桶,例如你可以按照某一個分類將所有的商品聚合起來,這種情況下就可以認為某一個分類的商品稱為一個桶,下面將詳細介紹幾個常用的桶聚合,並且會給出java使用時候的代碼

二、桶聚合

桶聚合是在實際使用時候用處比較多的一種聚合,簡單的桶聚合包括term聚合,range聚合,date聚合,IPV4聚合等聚合,因為自己使用的僅僅是其中的三個,在此就簡單的介紹三個,分別是term聚合,range聚合,以及date聚合

1、term聚合

term聚合就是第一部分給出的簡單的例子,按照不同的字段進行聚合

2、range聚合

range聚合為按照自定義的范圍來創造桶,將每一個范圍的數據進行聚合,並且這個聚合一般適用於字段類型為long或者int,double的字段,可以進行直接的聚合,例如,我們想統計不同年齡段的人的個數,DSL如下所示:

 

[java]  view plain  copy
 
  1. GET /index/type/_search  
  2. {  
  3.     "aggs" : {   
  4.         "agg_age" : {   
  5.          "field":"age"  
  6.             "ranges" : [  
  7.              { "to" : 18},  
  8.              { "from" : 19,"to" : 50},  
  9.              {"from" : 51}  
  10.             ]  
  11.         }  
  12.     }  
  13. }  

3、daterange聚合

 

date range聚合和range聚合類似,但是所使用的類型是datetime這種類型,使用的時候與range有些區別,給出一個簡單的使用date range聚合的DSL例子,如下所示:

 

[java]  view plain  copy
 
  1. GET /index/type/_search  
  2. {  
  3.     "aggs" : {   
  4.         "agg_year" : {   
  5.          "field":"date"  
  6.             "ranges" : [  
  7.              { "to" : "2008-08-08"},  
  8.              { "from" : "2008-08-09","to" : "2012-09-01"},  
  9.              {"from" : "2012-09-02"}  
  10.             ]  
  11.         }  
  12.     }  
  13. }  
上面的DSL是簡單的按照時間格式進行區間的聚合,但是有些時候我們可能想要一些按照年份聚合或者月份聚合的情況,這個時候應該怎么辦呢?在date range里面可以指定日期的格式,例如下面給出一個按照年份進行聚合的例子:

 

[java]  view plain  copy
 
  1. GET /index/type/_search  
  2. {  
  3.     "aggs" : {   
  4.         "agg_year" : {   
  5.          "field":"date"  
  6.          "format":"YYYY",  
  7.             "ranges" : [  
  8.              { "to" : "1970"},  
  9.              { "from" : "1971","to" : "2012"},  
  10.              {"from" : "2013"}  
  11.             ]  
  12.         }  
  13.     }  
  14. }  
我們可以指定格式來進行聚合

三、對於上述三種聚合java的實現

首先先給出一個具體的使用ES java api實現搜索並且聚合的完整例子,例子中使用的是terms聚合,按照分類id,將所有的分類進行聚合

 

[java]  view plain  copy
 
  1.     public void aggsearch() {  
  2.         init();  
  3.         SearchResponse response = null;  
  4.   
  5.         SearchRequestBuilder responsebuilder = client.prepareSearch("iktest")  
  6.                 .setTypes("iktest").setFrom(0).setSize(250);  
  7.         AggregationBuilder aggregation = AggregationBuilders  
  8.                 .terms("agg")  
  9.                 .field("category_id")  
  10.                 .subAggregation(  
  11.                         AggregationBuilders.topHits("top").setFrom(0)  
  12.                                 .setSize(10)).size(100);  
  13.         response = responsebuilder.setQuery(QueryBuilders.boolQuery()  
  14.   
  15.         .must(QueryBuilders.matchPhraseQuery("name""中學歷史")))  
  16.                 .addSort("category_id", SortOrder.ASC)  
  17.                 .addAggregation(aggregation)// .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)  
  18.                 .setExplain(true).execute().actionGet();  
  19.   
  20.         SearchHits hits = response.getHits();  
  21.   
  22.         Terms agg = response.getAggregations().get("agg");  
  23.         System.out.println(agg.getBuckets().size());  
  24.         for (Terms.Bucket entry : agg.getBuckets()) {  
  25.             String key = (String) entry.getKey(); // bucket key  
  26.             long docCount = entry.getDocCount(); // Doc count  
  27.             System.out.println("key " + key + " doc_count " + docCount);  
  28.   
  29.             // We ask for top_hits for each bucket  
  30.             TopHits topHits = entry.getAggregations().get("top");  
  31.             for (SearchHit hit : topHits.getHits().getHits()) {  
  32.                 System.out.println(" -> id " + hit.getId() + " _source [{}]"  
  33.                         + hit.getSource().get("category_name"));  
  34.                 ;  
  35.             }  
  36.         }  
  37.         System.out.println(hits.getTotalHits());  
  38.         int temp = 0;  
  39.         for (int i = 0; i < hits.getHits().length; i++) {  
  40.             // System.out.println(hits.getHits()[i].getSourceAsString());  
  41.             System.out.print(hits.getHits()[i].getSource().get("product_id"));  
  42.             // if(orderfield!=null&&(!orderfield.isEmpty()))  
  43.             // System.out.print("\t"+hits.getHits()[i].getSource().get(orderfield));  
  44.             System.out.print("\t"  
  45.                     + hits.getHits()[i].getSource().get("category_id"));  
  46.             System.out.print("\t"  
  47.                     + hits.getHits()[i].getSource().get("category_name"));  
  48.             System.out.println("\t"  
  49.                     + hits.getHits()[i].getSource().get("name"));  
  50.         }  
  51.     }  
  52. }  
以上的例子實現的是按照category_id字段進行分類的聚合,並且將在name字段查找包含“中學歷史”的這個詞,並且按照category_id進行排序,在此給出的只是一個搜索實現的函數,里面的字段名字,以及index,type等很多字段均為自己定義的index里面的名字,上面給出的是terms聚合時候的代碼,如果使用的是range聚合或者date range聚合,只需要改變 aggregation就可以

使用range聚合的時候:

 

[java]  view plain  copy
 
  1. aggregation = AggregationBuilders.range("agg")  
  2.                     .field("price").addUnboundedTo(50)  
  3.                     .addRange(51100).addRange(1011000)  
  4.                     .addUnboundedFrom(1001);  
使用date range聚合的時候:

 

[java]  view plain  copy
 
  1. aggregation = AggregationBuilders.dateRange("agg")  
  2.                     .field("date").format("yyyy")  
  3.                     .addUnboundedTo("1970").addRange("1970""2000")  
  4.                     .addRange("2000""2010").addUnboundedFrom("2009");  
以上所有的聚合均是先過濾搜索,然后對於召回得到的結果進行一個聚合,例如我們在name字段搜索中學歷史這個詞,最終得到四個分類分別為1,2,3,4那么聚合的時候就是這四個分類,但是有時候我們可能會需要對於搜索的結果進行一個過濾,但是我們不想對聚合的結果進行過濾,那么我們就要使用一下的部分了

四、先聚合再過濾

以上將的簡單的聚合都是先過濾或者搜索,然后對結果進行聚合,但是有時候我們需要先進行聚合,然后再對結果進行一次過濾,但是我們不希望這個時候聚合會發生變化,什么時候會遇到這種情況呢,我們以美團為例做一個說明,在主頁我們直接點解美食,得到如下所示的圖

點美食之后出現全部的分類,包括各種的菜系,下面我們點一個具體的菜系

 

從程序上來說,我們點第二次菜系的時候,出現的所有的菜品均是烤串之類的菜品了,但是在分類里面還是所有的分類都會有,如果按照之前的ES的聚合,會將所有搜索出來的品的分類進行一個聚合,但是點完烤串之后,所有的分類都是烤串了,那么就應該所有的分類只有一個烤串了,不應該有其他的,這樣的話肯定是不可以的,那么如何才能實現這種聚合的,這個時候我們就需要先聚合,然后進行再次的過濾,但是過濾的時候並不影響之前的聚合結果,這就是先聚合再過濾,在ES里面也有這種情況的考慮,這個時候使用的是postfilter

postfilter解決了僅僅過濾搜索結果,但是並不影響聚合結果,下面給出一個java使用時候的例子以及比較

函數一為第三部分給出的完整的搜索函數,按照分類聚合

函數二的改變只是對於一的

 

[java]  view plain  copy
 
  1. response = responsebuilder.setQuery(QueryBuilders.boolQuery()  
  2.   
  3.         .must(QueryBuilders.matchPhraseQuery("name""中學歷史")))  
  4.                 .addSort("category_id", SortOrder.ASC)  
  5.                 .addAggregation(aggregation)  
  6.                                 .setPostFilter(QueryBuilders.rangeQuery("price").gt(1000).lt(5000))  
  7.                 .setExplain(true).execute().actionGet();  
添加了按照price進行過濾,最后結果顯示,聚合的結果兩次完全一樣,但是函數二召回的結果為函數一結果的子集。

五、后續學習

如何多次的過濾以及召回,比如先過濾后聚合再過濾再次聚合然后再次過濾這種的應該如何實現,需要學習。








 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM