日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Elasticsearch过滤与聚合的先后顺序java实现

發(fā)布時(shí)間:2024/1/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Elasticsearch过滤与聚合的先后顺序java实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、Elasticsearch的聚合

ES的聚合相當(dāng)于關(guān)系型數(shù)據(jù)庫里面的group by,例如查找在性別字段男女人數(shù)的多少并且按照人數(shù)的多少進(jìn)行排序,在使用mysql的時(shí)候,可以使用如下的句子

?

select sex,count(*) from table_name group by sex order by count(*)

在ES里面想要實(shí)現(xiàn)這種的語句,就叫做聚合,比如這種的聚合使用DSL語句的話如下所示:

?

  • GET /index/type/_search

  • {

  • "size" : 0,

  • "aggs" : {

  • "agg_sex" : {

  • "terms" : {

  • "field" : "sex"

  • }

  • }

  • }

  • }

  • 這樣就可以實(shí)現(xiàn)最以上例子中的group by的功能,當(dāng)然這只是最簡單的聚合的使用,在ES里面的聚合有多重多樣的,比如說有度量聚合,可以用來計(jì)算某一個(gè)字段的平均值最大值等,在此給出一個(gè)簡單的度量聚合的例子

    ?

  • GET /index/type/_search

  • {

  • "size" : 0,

  • "aggs": {

  • "agg_sex": {

  • "terms": {

  • "field": "sex"

  • },

  • "agg_age": {

  • "avg_age": {

  • "avg": {

  • "field": "age"

  • }

  • }

  • }

  • }

  • }

  • }

  • 這個(gè)DSL語句就是將先按照性別進(jìn)行聚合,并且對不同的性別給出一個(gè)平均的年齡,使用之后ES的給出結(jié)果如下所示:

    ?

  • {

  • ...

  • "aggregations": {

  • "agg_sex": {

  • "buckets": [

  • {

  • "key": "male",

  • "doc_count": 4,

  • "avg_age": {

  • "value": 25

  • }

  • },

  • {

  • "key": "female",

  • "doc_count": 2,

  • "avg_age": {

  • "value": 23

  • }

  • }

  • ]

  • }

  • }

  • ...

  • }

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

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

    二、桶聚合

    桶聚合是在實(shí)際使用時(shí)候用處比較多的一種聚合,簡單的桶聚合包括term聚合,range聚合,date聚合,IPV4聚合等聚合,因?yàn)樽约菏褂玫膬H僅是其中的三個(gè),在此就簡單的介紹三個(gè),分別是term聚合,range聚合,以及date聚合

    1、term聚合

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

    2、range聚合

    range聚合為按照自定義的范圍來創(chuàng)造桶,將每一個(gè)范圍的數(shù)據(jù)進(jìn)行聚合,并且這個(gè)聚合一般適用于字段類型為long或者int,double的字段,可以進(jìn)行直接的聚合,例如,我們想統(tǒng)計(jì)不同年齡段的人的個(gè)數(shù),DSL如下所示:

    ?

  • GET /index/type/_search

  • {

  • "aggs" : {

  • "agg_age" : {

  • "field":"age"

  • "ranges" : [

  • { "to" : 18},

  • { "from" : 19,"to" : 50},

  • {"from" : 51}

  • ]

  • }

  • }

  • }

  • 3、daterange聚合

    ?

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

    ?

  • GET /index/type/_search

  • {

  • "aggs" : {

  • "agg_year" : {

  • "field":"date"

  • "ranges" : [

  • { "to" : "2008-08-08"},

  • { "from" : "2008-08-09","to" : "2012-09-01"},

  • {"from" : "2012-09-02"}

  • ]

  • }

  • }

  • }

  • 上面的DSL是簡單的按照時(shí)間格式進(jìn)行區(qū)間的聚合,但是有些時(shí)候我們可能想要一些按照年份聚合或者月份聚合的情況,這個(gè)時(shí)候應(yīng)該怎么辦呢?在date range里面可以指定日期的格式,例如下面給出一個(gè)按照年份進(jìn)行聚合的例子:

    ?

  • GET /index/type/_search

  • {

  • "aggs" : {

  • "agg_year" : {

  • "field":"date"

  • "format":"YYYY",

  • "ranges" : [

  • { "to" : "1970"},

  • { "from" : "1971","to" : "2012"},

  • {"from" : "2013"}

  • ]

  • }

  • }

  • }

  • 我們可以指定格式來進(jìn)行聚合

    三、對于上述三種聚合java的實(shí)現(xiàn)

    首先先給出一個(gè)具體的使用ES java api實(shí)現(xiàn)搜索并且聚合的完整例子,例子中使用的是terms聚合,按照分類id,將所有的分類進(jìn)行聚合

    ?

  • public void aggsearch() {

  • init();

  • SearchResponse response = null;

  • ?
  • SearchRequestBuilder responsebuilder = client.prepareSearch("iktest")

  • .setTypes("iktest").setFrom(0).setSize(250);

  • AggregationBuilder aggregation = AggregationBuilders

  • .terms("agg")

  • .field("category_id")

  • .subAggregation(

  • AggregationBuilders.topHits("top").setFrom(0)

  • .setSize(10)).size(100);

  • response = responsebuilder.setQuery(QueryBuilders.boolQuery()

  • ?
  • .must(QueryBuilders.matchPhraseQuery("name", "中學(xué)歷史")))

  • .addSort("category_id", SortOrder.ASC)

  • .addAggregation(aggregation)// .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)

  • .setExplain(true).execute().actionGet();

  • ?
  • SearchHits hits = response.getHits();

  • ?
  • Terms agg = response.getAggregations().get("agg");

  • System.out.println(agg.getBuckets().size());

  • for (Terms.Bucket entry : agg.getBuckets()) {

  • String key = (String) entry.getKey(); // bucket key

  • long docCount = entry.getDocCount(); // Doc count

  • System.out.println("key " + key + " doc_count " + docCount);

  • ?
  • // We ask for top_hits for each bucket

  • TopHits topHits = entry.getAggregations().get("top");

  • for (SearchHit hit : topHits.getHits().getHits()) {

  • System.out.println(" -> id " + hit.getId() + " _source [{}]"

  • + hit.getSource().get("category_name"));

  • ;

  • }

  • }

  • System.out.println(hits.getTotalHits());

  • int temp = 0;

  • for (int i = 0; i < hits.getHits().length; i++) {

  • // System.out.println(hits.getHits()[i].getSourceAsString());

  • System.out.print(hits.getHits()[i].getSource().get("product_id"));

  • // if(orderfield!=null&&(!orderfield.isEmpty()))

  • // System.out.print("\t"+hits.getHits()[i].getSource().get(orderfield));

  • System.out.print("\t"

  • + hits.getHits()[i].getSource().get("category_id"));

  • System.out.print("\t"

  • + hits.getHits()[i].getSource().get("category_name"));

  • System.out.println("\t"

  • + hits.getHits()[i].getSource().get("name"));

  • }

  • }

  • }

  • 以上的例子實(shí)現(xiàn)的是按照category_id字段進(jìn)行分類的聚合,并且將在name字段查找包含“中學(xué)歷史”的這個(gè)詞,并且按照category_id進(jìn)行排序,在此給出的只是一個(gè)搜索實(shí)現(xiàn)的函數(shù),里面的字段名字,以及index,type等很多字段均為自己定義的index里面的名字,上面給出的是terms聚合時(shí)候的代碼,如果使用的是range聚合或者date range聚合,只需要改變aggregation就可以

    使用range聚合的時(shí)候:

    ?

  • aggregation = AggregationBuilders.range("agg")

  • .field("price").addUnboundedTo(50)

  • .addRange(51, 100).addRange(101, 1000)

  • .addUnboundedFrom(1001);

  • 使用date range聚合的時(shí)候:

    ?

  • aggregation = AggregationBuilders.dateRange("agg")

  • .field("date").format("yyyy")

  • .addUnboundedTo("1970").addRange("1970", "2000")

  • .addRange("2000", "2010").addUnboundedFrom("2009");

  • 以上所有的聚合均是先過濾搜索,然后對于召回得到的結(jié)果進(jìn)行一個(gè)聚合,例如我們在name字段搜索中學(xué)歷史這個(gè)詞,最終得到四個(gè)分類分別為1,2,3,4那么聚合的時(shí)候就是這四個(gè)分類,但是有時(shí)候我們可能會需要對于搜索的結(jié)果進(jìn)行一個(gè)過濾,但是我們不想對聚合的結(jié)果進(jìn)行過濾,那么我們就要使用一下的部分了

    四、先聚合再過濾

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

    點(diǎn)美食之后出現(xiàn)全部的分類,包括各種的菜系,下面我們點(diǎn)一個(gè)具體的菜系

    ?

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

    postfilter解決了僅僅過濾搜索結(jié)果,但是并不影響聚合結(jié)果,下面給出一個(gè)java使用時(shí)候的例子以及比較

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

    函數(shù)二的改變只是對于一的

    ?

  • response = responsebuilder.setQuery(QueryBuilders.boolQuery()

  • ?
  • .must(QueryBuilders.matchPhraseQuery("name", "中學(xué)歷史")))

  • .addSort("category_id", SortOrder.ASC)

  • .addAggregation(aggregation)

  • .setPostFilter(QueryBuilders.rangeQuery("price").gt(1000).lt(5000))

  • .setExplain(true).execute().actionGet();

  • 添加了按照price進(jìn)行過濾,最后結(jié)果顯示,聚合的結(jié)果兩次完全一樣,但是函數(shù)二召回的結(jié)果為函數(shù)一結(jié)果的子集。

    五、后續(xù)學(xué)習(xí)

    如何多次的過濾以及召回,比如先過濾后聚合再過濾再次聚合然后再次過濾這種的應(yīng)該如何實(shí)現(xiàn),需要學(xué)習(xí)。

    總結(jié)

    以上是生活随笔為你收集整理的Elasticsearch过滤与聚合的先后顺序java实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。