在常規數據庫中,我們都知道有一個sql就是group,分組。如果主表只有對應的一個列記錄的分組的ID,那么還好統計,比如說每本書book表,有一個分類catId,記錄是屬於哪一類的書,那么直接按照catId進行分組即可。可是在實際應用種,並非如此簡單。一本書往往屬於多個分類,比如:某本書既屬於科技類書,又屬於兒童類書,要求按照這兩種條件進行篩選,都能篩選出來,如果要求按照分類進行統計數量,數據庫怎么group?我們且拋開種種解決方案,來看看Elasticsearch里面對這種需求,是多么的容易統計。
首先,我們需要造些數據,需要用到一個模型,這個模型定義了一個type,就算類型吧,我們用這個屬性來演示常規的group。還有一個catIds的列表模型,這個來解決我們上面描述的一本書對應多個分類的需求。模型定義如下:
import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Random; import com.donlianli.es.ESUtils; /** * 這個是為分組定義的一個模型 * catIds通常為一對多的分類ID * @author donlian */ public class FacetTestModel implements Serializable { private static final long serialVersionUID = 3174577828007649745L; /** * 隨便編寫的一些值,type屬性只能取這里面的其中一個 */ private String[] types= new String[]{ "type1","type2","type3","type4","type5","type6","type7", "type11","type12","type13","type14","type15","type16","type17" }; //主ID private long id; //類型,為types之一 private String type; /** * 所屬分類,范圍為1-50 */ private List<Integer> catIds; public FacetTestModel(){ Random r = new Random(); int n = Math.abs(r.nextInt()); int index = n%14; this.type = types[index]; this.id = Math.abs(r.nextLong()); n = n%50; catIds = new ArrayList<Integer>(); catIds.add(n); int ys = n%3; if(ys!=0){ for(int i=1;i<ys+1;i++){ catIds.add(n+i); } } } public static void main(String[] argv){ for(int i=0;i<10;i++){ FacetTestModel f = new FacetTestModel(); System.out.println(ESUtils.toJson(f)); } } set,get方法,自己寫吧 }
接着就是初始化數據。
import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.client.Client; import com.donlianli.es.ESUtils; import com.donlianli.es.model.FacetTestModel; public class BulkIndexTest { public static void main(String[] args) { Client client = ESUtils.getClient(); BulkRequestBuilder bulkRequest = client.prepareBulk(); for(int i=0;i<10;i++){ String json = ESUtils.toJson(new FacetTestModel()); IndexRequestBuilder indexRequest = client.prepareIndex("test", "test") //指定不重復的ID .setSource(json).setId(String.valueOf(i)); //添加到builder中 bulkRequest.add(indexRequest); } BulkResponse bulkResponse = bulkRequest.execute().actionGet(); if (bulkResponse.hasFailures()) { System.out.println(bulkResponse.buildFailureMessage()); } } }
接下來,我們首先對type進行統計。在elasticsearch中,分組的功能叫facet,不知道為啥起這個名稱。總之,就是對type的每一個值的數量進行統計,注意,要設置里面的size條件,否則默認只返回10個。
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.search.facet.FacetBuilders; import org.elasticsearch.search.facet.Facets; import org.elasticsearch.search.facet.terms.TermsFacet; import org.elasticsearch.search.facet.terms.TermsFacetBuilder; import com.donlianli.es.ESUtils; public class GroupTest { public static void main(String[] argv){ Client client = ESUtils.getClient(); TermsFacetBuilder facetBuilder = 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.Entry tf :facet.entries()){ System.out.println(tf.getTerm()+"\t:\t" + tf.getCount()); } client.close(); } }
運行程序后,大概得到如下結果:
type3 : 4 type7 : 1 type6 : 1 type4 : 1 type13 : 1 type12 : 1 type11 : 1
正好10個。初始化代碼能對的上。
下面,我們就要對catIds進行統計了,再統計之前,我們先看看es里面都存儲的是那些數據。
{id=3683174899323317453, catIds=[4, 5], type=type3} {id=271209313870366004, catIds=[26, 27, 28], type=type3} {id=348654892174153835, catIds=[41, 42, 43], type=type4} {id=6826187683023110944, catIds=[46, 47], type=type7} {id=3437591661789488747, 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=3593797446463621044, catIds=[30], type=type12} {id=5824112111832084165, catIds=[1, 2], type=type6}
怎么對catIds進行統計呢,代碼跟上面進行單個統計一樣。
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.search.facet.FacetBuilders; import org.elasticsearch.search.facet.Facets; import org.elasticsearch.search.facet.terms.TermsFacet; import org.elasticsearch.search.facet.terms.TermsFacetBuilder; import com.donlianli.es.ESUtils; public class GroupTest2 { public static void main(String[] argv){ Client client = ESUtils.getClient(); TermsFacetBuilder facetBuilder = 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.Entry tf :facet.entries()){ System.out.println("鍵:"+tf.getTerm()+"\t;數量:\t" + 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等等,更多功能,大家還是自己多多驗證。
