Browse Source

添加实体类字段,组装顶层聚合。

zhangjinjun 6 years ago
parent
commit
fe8bac85e7

+ 43 - 3
src/main/java/com/yihu/quota/model/view/ViewDimension.java

@ -21,14 +21,18 @@ public class ViewDimension extends BaseAssignedEntity {
    public String viewId; // 视图Id
    public String dimensionCode; // 维度编码
    public String dimensionName; // 维度名称
    public String cubeCode; // 多维度数据集编码
    public String esIndex; // 多维数据集的 ES index
    public String esType; //  多维数据集的 ES type
    public String dimensionType; // 维度类型,row:行维度,col:列维度,drill:钻取维度,默认行维度。
    public Integer groupRow; // 行维度组
    public Integer groupRowOrder; // 组内行维度顺序
    public Integer columnDrillOrder; //列维度/钻取维度的顺序
    public String rowMemberOrderType; //行维度成员排序方式,name:按名称排序,sub_agg:按子聚合统计值排序,默认按名称排序。
    public Integer rowMemberCount; //行维度成员返回个数
    public Integer columnDrillOrder; // 列维度/钻取维度的顺序
    public String rowMemberOrderType; // 行维度成员排序方式,name:按名称排序,sub_agg:按子聚合统计值排序,默认按名称排序。
    public String rowMemberOrderStrategy; // 行维度成员排序策略,asc:升序,desc:降序。
    public Integer rowMemberCount; // 行维度成员返回个数
    public String rowMemberDateFormat; // 行维度成员日期格式
    public String rowMemberDateInterval; // 行维度日期间隔
    @Column(name = "view_id")
    public String getViewId() {
@ -57,6 +61,15 @@ public class ViewDimension extends BaseAssignedEntity {
        this.dimensionName = dimensionName;
    }
    @Column(name = "cube_code")
    public String getCubeCode() {
        return cubeCode;
    }
    public void setCubeCode(String cubeCode) {
        this.cubeCode = cubeCode;
    }
    @Column(name = "es_index")
    public String getEsIndex() {
        return esIndex;
@ -120,6 +133,15 @@ public class ViewDimension extends BaseAssignedEntity {
        this.rowMemberOrderType = rowMemberOrderType;
    }
    @Column(name = "row_member_order_strategy")
    public String getRowMemberOrderStrategy() {
        return rowMemberOrderStrategy;
    }
    public void setRowMemberOrderStrategy(String rowMemberOrderStrategy) {
        this.rowMemberOrderStrategy = rowMemberOrderStrategy;
    }
    @Column(name = "row_member_count")
    public Integer getRowMemberCount() {
        return rowMemberCount;
@ -128,4 +150,22 @@ public class ViewDimension extends BaseAssignedEntity {
    public void setRowMemberCount(Integer rowMemberCount) {
        this.rowMemberCount = rowMemberCount;
    }
    @Column(name = "row_member_date_format")
    public String getRowMemberDateFormat() {
        return rowMemberDateFormat;
    }
    public void setRowMemberDateFormat(String rowMemberDateFormat) {
        this.rowMemberDateFormat = rowMemberDateFormat;
    }
    @Column(name = "row_member_date_interval")
    public String getRowMemberDateInterval() {
        return rowMemberDateInterval;
    }
    public void setRowMemberDateInterval(String rowMemberDateInterval) {
        this.rowMemberDateInterval = rowMemberDateInterval;
    }
}

+ 13 - 3
src/main/java/com/yihu/quota/model/view/ViewQuotaFilter.java

@ -21,12 +21,13 @@ public class ViewQuotaFilter extends BaseAssignedEntity {
    public Integer relationId; // 视图/指标主键
    public String dimensionCode; // 维度编码
    public String dimensionName; // 维度名称
    public String cubeCode; // 多维度数据集编码
    public String esIndex; // 多维数据集的 ES index
    public String esType; //  多维数据集的 ES type
    public String filterType; // 过滤条件类型,view:视图,quota:指标。
    public String relationType; // 关系类型,关系类型,and:且,or:或
    public String compareType; //比较类型,belong:属于,not_belong:不属于,null:为空,not_null:非空,gt:大于,gte:大于等于,小于:lt,小于等于:lte,contain:包含,not_contain:不包含
    public String filterValue; //过滤的值
    public Object filterValue; //过滤的值
    @Column(name = "relation_id")
    public Integer getRelationId() {
@ -55,6 +56,15 @@ public class ViewQuotaFilter extends BaseAssignedEntity {
        this.dimensionName = dimensionName;
    }
    @Column(name = "cube_code")
    public String getCubeCode() {
        return cubeCode;
    }
    public void setCubeCode(String cubeCode) {
        this.cubeCode = cubeCode;
    }
    @Column(name = "es_index")
    public String getEsIndex() {
        return esIndex;
@ -101,11 +111,11 @@ public class ViewQuotaFilter extends BaseAssignedEntity {
    }
    @Column(name = "filter_value")
    public String getFilterValue() {
    public Object getFilterValue() {
        return filterValue;
    }
    public void setFilterValue(String filterValue) {
    public void setFilterValue(Object filterValue) {
        this.filterValue = filterValue;
    }
}

+ 165 - 15
src/main/java/com/yihu/quota/service/view/ViewService.java

@ -3,6 +3,7 @@ package com.yihu.quota.service.view;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yihu.ehr.elasticsearch.ElasticSearchPool;
import com.yihu.ehr.exception.ApiException;
import com.yihu.ehr.query.BaseJpaService;
import com.yihu.quota.dao.view.ViewDao;
import com.yihu.quota.dao.view.ViewDimensionDao;
@ -13,7 +14,11 @@ import com.yihu.quota.model.view.View;
import com.yihu.quota.model.view.ViewDimension;
import com.yihu.quota.model.view.ViewQuota;
import com.yihu.quota.model.view.ViewQuotaFilter;
import com.yihu.quota.service.cube.CubeService;
import com.yihu.quota.vo.ViewQuotaFilterModel;
import io.searchbox.core.search.aggregation.DateHistogramAggregation;
import io.searchbox.core.search.aggregation.FilterAggregation;
import io.searchbox.core.search.aggregation.ValueCountAggregation;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.elasticsearch.action.search.SearchRequestBuilder;
@ -22,9 +27,25 @@ import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
import org.elasticsearch.search.aggregations.metrics.MetricsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.avg.AvgBuilder;
import org.elasticsearch.search.aggregations.metrics.cardinality.Cardinality;
import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityBuilder;
import org.elasticsearch.search.aggregations.metrics.max.MaxBuilder;
import org.elasticsearch.search.aggregations.metrics.min.MinBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.SumBuilder;
import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCountBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -56,6 +77,8 @@ public class ViewService extends BaseJpaService<View, ViewDao> {
    private ElasticSearchPool elasticSearchPool;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private CubeService cubeService;
    public View findOne(String id) {
        return viewDao.findOne(id);
@ -98,9 +121,9 @@ public class ViewService extends BaseJpaService<View, ViewDao> {
     * @param viewCode
     * @param filterModelList
     */
    public Aggregations statViewResult(String viewCode, List<ViewQuotaFilterModel> filterModelList) throws IOException {
    public Aggregations statViewResult(String viewCode, List<ViewQuotaFilterModel> filterModelList) throws Exception {
        // 获取视图规则
        View view = viewDao.findByCode(viewCode);
        View view = this.getViewRule(viewCode);
        String[] esIndices = view.getEsIndex().split(",");
        String[] esTypes = view.getEsType().split(",");
@ -109,22 +132,111 @@ public class ViewService extends BaseJpaService<View, ViewDao> {
        // 组装页面传入的过滤条件
        BoolQueryBuilder viewBoolQueryBuilder = QueryBuilders.boolQuery();
        joinViewFilters(viewBoolQueryBuilder, filterModelList);
        this.joinFilters(viewBoolQueryBuilder, filterModelList);
        // 组装视图默认过滤条件
        String viewFilterStr = objectMapper.writeValueAsString(view.getViewFilterList());
        List<ViewQuotaFilterModel> viewFilterModelList = objectMapper.readValue(viewFilterStr, new TypeReference<List<ViewQuotaFilterModel>>() {
        });
        joinViewFilters(viewBoolQueryBuilder, viewFilterModelList);
        this.joinFilters(viewBoolQueryBuilder, viewFilterModelList);
        searchRequestBuilder.setQuery(viewBoolQueryBuilder);
        // 组装聚合
        List<ViewDimension> groupTopRowDimensionList = view.getGroupTopRowDimensionList();
        if (groupTopRowDimensionList.size() != 0) {
        if (groupTopRowDimensionList.size() == 0) {
            if (!"1".equals(view.getDisplayType())) {
                throw new ApiException("请为视图至少配置一个行维度。");
            } else {
                // 视图为数值展示类型的场合,没有行/列维度
                List<ViewQuota> viewQuotaList = view.getViewQuotaList();
                for (ViewQuota quota : viewQuotaList) {
                    if ("basic".equals(quota.getFormulaMode())) {
                        if ("sum".equals(quota.getBasicFormulaType())) {
                            // 求和
                            String aggName = "sum-result";
                            SumBuilder sumAgg = AggregationBuilders.sum(aggName).field(quota.getDimensionCode());
                            this.joinNumericalAgg(searchRequestBuilder, sumAgg, quota);
                        } else if ("count".equals(quota.getBasicFormulaType())) {
                            // 计数
                            String aggName = "count-result";
                            ValueCountBuilder countAgg = AggregationBuilders.count(aggName).field(quota.getDimensionCode());
                            this.joinNumericalAgg(searchRequestBuilder, countAgg, quota);
                        } else if ("avg".equals(quota.getBasicFormulaType())) {
                            // 均值
                            String aggName = "avg-result";
                            AvgBuilder avgAgg = AggregationBuilders.avg(aggName).field(quota.getDimensionCode());
                            this.joinNumericalAgg(searchRequestBuilder, avgAgg, quota);
                        } else if ("max".equals(quota.getBasicFormulaType())) {
                            // 最大值
                            String aggName = "max-result";
                            MaxBuilder maxAgg = AggregationBuilders.max(aggName).field(quota.getDimensionCode());
                            this.joinNumericalAgg(searchRequestBuilder, maxAgg, quota);
                        } else if ("min".equals(quota.getBasicFormulaType())) {
                            // 最小值
                            String aggName = "min-result";
                            MinBuilder minAgg = AggregationBuilders.min(aggName).field(quota.getDimensionCode());
                            this.joinNumericalAgg(searchRequestBuilder, minAgg, quota);
                        } else if ("cardinality".equals(quota.getBasicFormulaType())) {
                            // 去重计数
                            String aggName = "cardinality-result";
                            CardinalityBuilder cardinalityAgg = AggregationBuilders.cardinality(aggName).field(quota.getDimensionCode());
                            this.joinNumericalAgg(searchRequestBuilder, cardinalityAgg, quota);
                        }
                    }
                }
            }
        } else {
            int indexCount = esIndices.length;
            for (ViewDimension viewDimension : groupTopRowDimensionList) {
            for (ViewDimension topRowDimension : groupTopRowDimensionList) {
                String topRowDimensionCode = topRowDimension.getDimensionCode();
                String topOrderType = topRowDimension.getRowMemberOrderType();
                boolean isTopAsc = "asc".equals(topRowDimension.getRowMemberOrderStrategy()) ? true : false;
                // 有多个多维度数据集时,设定顶层聚合的数据来自哪个多维数据集
                FilterAggregationBuilder topFilterAgg = null;
                if (indexCount > 1) {
                    AggregationBuilders.filter(viewDimension.getDimensionCode());
                    String aggName = topRowDimension.getDimensionCode() + "-index-filter";
                    QueryBuilder queryBuilder = QueryBuilders.termQuery("_index", topRowDimension.getEsIndex());
                    topFilterAgg = AggregationBuilders.filter(aggName).filter(queryBuilder);
                }
                // 顶层行维度
                AggregationBuilder topAgg = null;
                String fieldType = cubeService.findDimensionDataType(topRowDimension.getCubeCode(), topRowDimension.getDimensionCode());
                if (!"date".equals(fieldType)) {
                    String aggName = topRowDimensionCode + "-terms";
                    topAgg = AggregationBuilders.terms(aggName).field(topRowDimensionCode);
                    // 设置排序
                    if (StringUtils.isEmpty(topOrderType) || "name".equals(topOrderType)) {
                        ((TermsBuilder) topAgg).order(Terms.Order.term(isTopAsc));
                    } else {
                        // TODO 按子聚合结果排序,需拼接子聚合名称
                        ((TermsBuilder) topAgg).order(Terms.Order.aggregation("", isTopAsc));
                    }
                    // 设置分组个数
                    int memberCount = topRowDimension.getRowMemberCount();
                    if (memberCount > 0) {
                        ((TermsBuilder) topAgg).size(memberCount);
                    } else {
                        // TODO 动态获取全部的分组个数进行设置
                        ((TermsBuilder) topAgg).size(1000);
                    }
                } else {
                    String aggName = topRowDimensionCode + "-date_histogram";
                    topAgg = AggregationBuilders.dateHistogram(aggName).field(topRowDimensionCode);
                    ((DateHistogramBuilder) topAgg).minDocCount(0);
                    ((DateHistogramBuilder) topAgg).format(topRowDimension.getRowMemberDateFormat());
                    ((DateHistogramBuilder) topAgg).interval(new DateHistogramInterval(topRowDimension.getRowMemberDateInterval()));
                    if (StringUtils.isEmpty(topOrderType) || "name".equals(topOrderType)) {
                        Histogram.Order order = isTopAsc ? Histogram.Order.KEY_ASC : Histogram.Order.KEY_DESC;
                        ((DateHistogramBuilder) topAgg).order(order);
                    } else {
                        // TODO 按子聚合结果排序,需拼接子聚合名称
                        ((DateHistogramBuilder) topAgg).order(Histogram.Order.aggregation("", isTopAsc));
                    }
                    // TODO 需动态根据条件范围设置,或设置默认值
                    ((DateHistogramBuilder) topAgg).extendedBounds("", "");
                }
            }
        }
@ -137,17 +249,18 @@ public class ViewService extends BaseJpaService<View, ViewDao> {
    }
    /**
     * 拼接视图的过滤条件
     * 拼接视图、指标的过滤条件
     *
     * @param boolQueryBuilder
     * @param viewFilterModelList
     * @param filterModelList
     */
    private void joinViewFilters(BoolQueryBuilder boolQueryBuilder, List<ViewQuotaFilterModel> viewFilterModelList) {
        if (viewFilterModelList.size() != 0) {
            for (ViewQuotaFilterModel filterModel : viewFilterModelList) {
    private void joinFilters(BoolQueryBuilder boolQueryBuilder, List<ViewQuotaFilterModel> filterModelList) {
        if (filterModelList.size() != 0) {
            for (ViewQuotaFilterModel filterModel : filterModelList) {
                String field = filterModel.getDimensionCode();
                String value = filterModel.getFilterValue();
                // 属于与否,TODO 需要判断字段类型,转换 value 的类型。
                Object value = filterModel.getFilterValue();
                // 属于与否
                if ("belong".equals(filterModel.getCompareType())) {
                    QueryBuilder queryBuilder = QueryBuilders.termQuery(field, value);
                    joinBoolQuery(boolQueryBuilder, queryBuilder, filterModel.getRelationType());
@ -171,7 +284,7 @@ public class ViewService extends BaseJpaService<View, ViewDao> {
                    QueryBuilder queryBuilder = QueryBuilders.matchQuery(field, value);
                    joinBoolQuery(boolQueryBuilder, queryBuilder, "not");
                }
                // 范围,TODO 需要判断字段类型,转换 value 的类型。
                // 范围
                else if ("gt".equals(filterModel.getCompareType())) {
                    QueryBuilder queryBuilder = QueryBuilders.rangeQuery(field).gt(value);
                    joinBoolQuery(boolQueryBuilder, queryBuilder, filterModel.getRelationType());
@ -189,6 +302,13 @@ public class ViewService extends BaseJpaService<View, ViewDao> {
        }
    }
    /**
     * 组装且/或/否查询
     *
     * @param boolQueryBuilder
     * @param queryBuilder
     * @param relationType
     */
    private void joinBoolQuery(BoolQueryBuilder boolQueryBuilder, QueryBuilder queryBuilder, String relationType) {
        if ("and".equals(relationType)) {
            boolQueryBuilder.must(queryBuilder);
@ -199,4 +319,34 @@ public class ViewService extends BaseJpaService<View, ViewDao> {
        }
    }
    /**
     * 组装视图为数值展示类型的聚合
     *
     * @param searchRequestBuilder
     * @param numericalAgg
     * @param quota
     * @throws IOException
     */
    private void joinNumericalAgg(SearchRequestBuilder searchRequestBuilder, MetricsAggregationBuilder numericalAgg, ViewQuota quota) throws IOException {
        List<ViewQuotaFilter> quotaFilterList = quota.getQuotaFilterList();
        FilterAggregationBuilder filterAgg = null;
        if (quotaFilterList != null && quotaFilterList.size() > 0) {
            String quotaFilterStr = objectMapper.writeValueAsString(quotaFilterList);
            List<ViewQuotaFilterModel> quotaFilterModelList = objectMapper.readValue(quotaFilterStr, new TypeReference<List<ViewQuotaFilterModel>>() {
            });
            BoolQueryBuilder quotaBoolQueryBuilder = QueryBuilders.boolQuery();
            joinFilters(quotaBoolQueryBuilder, quotaFilterModelList);
            String aggName = quota.getDimensionCode() + "-filter";
            filterAgg = AggregationBuilders.filter(aggName).filter(quotaBoolQueryBuilder);
            filterAgg.subAggregation(numericalAgg);
        }
        if (filterAgg == null) {
            searchRequestBuilder.addAggregation(numericalAgg);
        } else {
            searchRequestBuilder.addAggregation(filterAgg);
        }
    }
}

+ 3 - 3
src/main/java/com/yihu/quota/vo/ViewQuotaFilterModel.java

@ -23,7 +23,7 @@ public class ViewQuotaFilterModel {
    /**
     * 过滤的值
     */
    private String filterValue;
    private Object filterValue;
    public String getDimensionCode() {
        return dimensionCode;
@ -49,11 +49,11 @@ public class ViewQuotaFilterModel {
        this.compareType = compareType;
    }
    public String getFilterValue() {
    public Object getFilterValue() {
        return filterValue;
    }
    public void setFilterValue(String filterValue) {
    public void setFilterValue(Object filterValue) {
        this.filterValue = filterValue;
    }
}