视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Elasticsearch强大的聚合功能Facet
2025-10-03 15:16:51 责编:小OO
文档
在常规数据库中,我们都知道有一个sql就是group,分组。如果主表只有对应的一个列记录的分组的ID,那么还好统计,比如说每本书book 表,有一个分类catId,记录是属于哪一类的书,那么直接按照catId进行分组即可。可是在实际应用种,并非如此简单。一本书往往属于多个分类,比 如:某本书既属于科技类书,又属于儿童类书,要求按照这两种条件进行筛选,都能筛选出来,如果要求按照分类进行统计数量,数据库怎么group?我们且抛 开种种解决方案,来看看Elasticsearch里面对这种需求,是多么的容易统计。

 

首先,我们需要造些数据,需要用到一个模型,这个模型定义了一个type,就算类型吧,我们用这个属性来演示常规的group。还有一个catIds的列表模型,这个来解决我们上面描述的一本书对应多个分类的需求。模型定义如下:

importjava.io.Serializable;  

importjava.util.ArrayList;  

importjava.util.List;  

importjava.util.Random;  

importcom.donlianli.es.ESUtils;  

/** 

 * 这个是为分组定义的一个模型 

 * catIds通常为一对多的分类ID 

 * @authordonlian

 */

publicclassFacetTestModelimplementsSerializable {  

privatestaticfinallongserialVersionUID = 31745778280079745L;  

/** 

     * 随便编写的一些值,type属性只能取这里面的其中一个 

     */

private String[] types= new String[]{  

"type1,  

"type11

    };  

//主ID  

privatelong id;  

//类型,为types之一  

private String type;  

/** 

     * 所属分类,范围为1-50 

     */

private ListcatIds;  

publicFacetTestModel(){  

        Random r = newRandom();  

int n =Math.abs(r.nextInt());  

int index = n%14;  

this.type =types[index];  

this.id =Math.abs(r.nextLong());  

        n = n%50;  

catIds= newArrayList();  

catIds.add(n);  

intys = n%3;  

if(ys!=0){  

for(int i=1;icatIds.add(n+i);  

            }  

        }  

    }  

publicstaticvoid main(String[] argv){  

for(int i=0;i<10;i++){  

FacetTestModel f = newFacetTestModel();  

System.out.println(ESUtils.toJson(f));  

        }  

    }  

set,get方法,自己写吧

}  

 接着就是初始化数据。

importorg.elasticsearch.action.bulk.BulkRequestBuilder;  

importorg.elasticsearch.action.bulk.BulkResponse;  

importorg.elasticsearch.action.index.IndexRequestBuilder;  

importorg.elasticsearch.client.Client;  

importcom.donlianli.es.ESUtils;  

importcom.donlianli.es.model.FacetTestModel;  

publicclassBulkIndexTest {  

publicstaticvoid main(String[] args) {  

        Client client=ESUtils.getClient();  

BulkRequestBuilderbulkRequest=client.prepareBulk();  

for(int i=0;i<10;i++){  

            String json= ESUtils.toJson(newFacetTestModel());  

IndexRequestBuilderindexRequest= client.prepareIndex("test", "test")  

//指定不重复的ID        

            .setSource(json).setId(String.valueOf(i));  

//添加到builder中  

bulkRequest.add(indexRequest);  

        }  

BulkResponsebulkResponse=bulkRequest.execute().actionGet();  

if (bulkResponse.hasFailures()) {  

System.out.println(bulkResponse.buildFailureMessage());  

        }  

    }  

}  

接下来,我们首先对type进行统计。在elasticsearch中,分组的功能叫facet,不知道为啥起这个名称。总之,就是对type的每一个值的数量进行统计,注意,要设置里面的size条件,否则默认只返回10个。

importorg.elasticsearch.action.search.SearchResponse;  

importorg.elasticsearch.client.Client;  

importorg.elasticsearch.index.query.FilterBuilders;  

importorg.elasticsearch.search.facet.FacetBuilders;  

importorg.elasticsearch.search.facet.Facets;  

importorg.elasticsearch.search.facet.terms.TermsFacet;  

importorg.elasticsearch.search.facet.terms.TermsFacetBuilder;  

importcom.donlianli.es.ESUtils;  

publicclassGroupTest {  

publicstaticvoid  main(String[] argv){  

        Client client=ESUtils.getClient();  

TermsFacetBuilderfacetBuilder= FacetBuilders.termsFacet("typeFacetName");  

facetBuilder.field("type").size(Integer.MAX_VALUE);  

facetBuilder.facetFilter(FilterBuilders.matchAllFilter());  

SearchResponse response = client.prepareSearch("test")  

                .setTypes("test")  

                .addFacet(facetBuilder)  

                .setFilter(FilterBuilders.matchAllFilter())  

                .execute()

                .actionGet();  

        Facets f =response.facets();  

//跟上面的名称一样  

TermsFacet facet = (TermsFacet)f.getFacets().get("typeFacetName");  

for(TermsFacet.Entrytf :facet.entries()){  

System.out.println(tf.getTerm()+"\:\" +tf.getCount());  

        }  

client.close();  

    }  

}  

运行程序后,大概得到如下结果:

type3   :   4

type7   :   1

type6   :   1

type4   :   1

type13  :1

type12  :1

type11  :1  

正好10个。初始化代码能对的上。

下面,我们就要对catIds进行统计了,再统计之前,我们先看看es里面都存储的是那些数据。

{id=36831749323317453, catIds=[4, 5], type=type3}  

{id=271209313870366004, catIds=[26, 27, 28], type=type3}  

{id=3486542174153835, catIds=[41, 42, 43], type=type4}  

{id=6826187683023110944, catIds=[46, 47], type=type7}  

{id=34375916617488747, catIds=[22, 23], type=type3}  

{id=6365837443081614150, catIds=[37, 38], type=type11}  

{id=2387331048448677498, catIds=[20, 21, 22], type=type3}  

{id=5595404824923951817, catIds=[31, 32], type=type13}  

{id=35937974463621044, catIds=[30], type=type12}  

{id=5824112111832084165, catIds=[1, 2], type=type6}  

怎么对catIds进行统计呢,代码跟上面进行单个统计一样。

importorg.elasticsearch.action.search.SearchResponse;  

importorg.elasticsearch.client.Client;  

importorg.elasticsearch.index.query.FilterBuilders;  

importorg.elasticsearch.search.facet.FacetBuilders;  

importorg.elasticsearch.search.facet.Facets;  

importorg.elasticsearch.search.facet.terms.TermsFacet;  

importorg.elasticsearch.search.facet.terms.TermsFacetBuilder;  

importcom.donlianli.es.ESUtils;  

publicclass GroupTest2 {  

publicstaticvoid  main(String[] argv){  

        Client client=ESUtils.getClient();  

TermsFacetBuilderfacetBuilder= FacetBuilders.termsFacet("catIdName");  

facetBuilder.field("catIds").size(Integer.MAX_VALUE);  

facetBuilder.facetFilter(FilterBuilders.matchAllFilter());  

SearchResponse response = client.prepareSearch("test")  

                .setTypes("test")  

                .addFacet(facetBuilder)  

                .setFilter(FilterBuilders.matchAllFilter())  

                .execute()

                .actionGet();  

        Facets f =response.facets();  

//跟上面的名称一样  

TermsFacet facet = (TermsFacet)f.getFacets().get("catIdName");  

for(TermsFacet.Entrytf :facet.entries()){  

System.out.println("键:"+tf.getTerm()+"\;数量:\" +tf.getCount());  

        }  

client.close();  

    }  

}  

运行结果:

键:22    ;数量:    2

键:47    ;数量:    1

键:46    ;数量:    1

键:43    ;数量:    1

键:42    ;数量:    1

键:41    ;数量:    1

键:38    ;数量:    1

键:37    ;数量:    1

键:32    ;数量:    1

键:31    ;数量:    1

键:30    ;数量:    1

键:28    ;数量:    1

键:27    ;数量:    1

键:26    ;数量:    1

键:23    ;数量:    1

键:21    ;数量:    1

键:20    ;数量:    1

键:5 ;数量:    1

键:4 ;数量:    1

键:2 ;数量:    1

键:1 ;数量:    1  

再和上面的数据对对,是不是除了22,其他的都是一个?

 

在分组这方面,ES真的很强大,除了上面的支持列表分组外,还支持范围分组rangeFacet,多个分组可以一次全部发送给ES等等,更多功能,大家还是自己多多验证。下载本文

显示全文
专题