package com.smtaiserver.smtaiserver.javaai.ast; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.apache.ibatis.ognl.Ognl; import java.util.Map.Entry; import java.util.Set; import com.smtaiserver.smtaiserver.core.SMTAIServerApp; import com.smtaiserver.smtaiserver.database.SMTDatabase.DBRecord; import com.smtaiserver.smtaiserver.database.SMTDatabase.DBRecords; import com.smtaiserver.smtaiserver.javaai.SMTJavaAIError; import com.smtaiserver.smtaiserver.javaai.ast.ASTCubeRecs.ASTCubeRecsType; import com.smtaiserver.smtaiserver.javaai.metrics.base.SMTDimensionDef; import com.smtservlet.util.SMTStatic; import com.smtservlet.util.SMTStatic.SMTCalcTime; public class ASTCubeRecsValue { private enum GROUP_OP { MIN, MAX, AVG, SUM, COUNT } private enum GROUP_UNIT { NONE, MINUTE, MONTH, } private enum DIFF_OP { ADD, SUB, YOY } private static class TimeGroupInfo { public GROUP_OP _op; public GROUP_UNIT _unit; public int _step; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public String _countField; public String _groupField; public char _groupType; public String _chartType = null; public DBRecords _recsValue; public String _title; public ASTTimeRange _timeRange = null; private static Date _baseTime = SMTStatic.toDate("1980-01-01"); public ASTCubeRecsValue cloneCubeRecsValue() { ASTCubeRecsValue newRecsValue = new ASTCubeRecsValue(); newRecsValue._countField = this._countField; newRecsValue._groupField = this._groupField; newRecsValue._groupType = this._groupType; newRecsValue._chartType = this._chartType; newRecsValue._title = this._title; if(_timeRange != null) newRecsValue._timeRange = _timeRange.cloneTimeRange(); newRecsValue._recsValue = new DBRecords(); newRecsValue._recsValue.initColumn(this._recsValue.getFieldMap()); for(DBRecord rec : _recsValue.getRecords()) { newRecsValue._recsValue.addRecord(rec.getValues()); } return newRecsValue; } public void distinctRecTime() throws Exception { if(_timeRange == null) return; List recs = _recsValue.getRecords(); Set setExistTime = new HashSet<>(); for(int recIndex = recs.size() - 1; recIndex >= 0; recIndex --) { Date curTime = recs.get(recIndex).getDate(_timeRange._timeField); if(setExistTime.contains(curTime)) { recs.remove(recIndex); continue; } else { setExistTime.add(curTime); } } } public void limitRecords(int limit) { _recsValue.limitRecord(limit); } private Object convGroupTypeValue(char groupType, Object value) throws Exception { if(value == null) return null; switch(groupType) { case 'D': return SMTStatic.toDouble(value); case 'S': return SMTStatic.toString(value); case 'L': return SMTStatic.toLong(value); case 'I': return SMTStatic.toInt(value); case 'T': return SMTStatic.toDate(value); default: throw new Exception("can't convert to type : " + groupType); } } private int compGroupTypeValue(char groupType, Object v1, Object v2) throws Exception { switch(groupType) { case 'I': return Integer.compare(SMTStatic.toInt(v1), SMTStatic.toInt(v2)); case 'L': return Long.compare(SMTStatic.toLong(v1), SMTStatic.toLong(v2)); case 'D': return Double.compare(SMTStatic.toDouble(v1), SMTStatic.toDouble(v2)); case 'S': return ((String)v1).compareTo((String)v2); case 'T': return ((Date)v1).compareTo((Date)v2); default: throw new Exception("can't convert to type : " + groupType); } } private Date alignGroupTime(TimeGroupInfo timeGroupInfo, Date time) { if(timeGroupInfo._unit == GROUP_UNIT.NONE) return _baseTime; else if(timeGroupInfo._unit == GROUP_UNIT.MINUTE) { long minutes = (time.getTime() - _baseTime.getTime()) / 1000 / 60; return new Date(_baseTime.getTime() + (minutes / timeGroupInfo._step) * timeGroupInfo._step * 1000 * 60); } else if(timeGroupInfo._unit == GROUP_UNIT.MONTH) { int subMonths = ((time.getYear() + 1900) * 12 + time.getMonth()) % timeGroupInfo._step; return SMTStatic.calculateTime(time, SMTCalcTime.ZERO_TIME, 0, SMTCalcTime.SET_DATE, 1, SMTCalcTime.ADD_MONTH, - subMonths); } return null; } public SMTJavaAIError groupEvalValueRecords(ASTEvalColInfo evalColInfo, String sGroupOP, String sGroupStep, ASTCubeRecsType[] r_isSummary, String[][] r_colNames) throws Exception { if(_recsValue == null) return null; if(!SMTStatic.isNullOrEmpty(sGroupStep) && SMTStatic.isNullOrEmpty(_timeRange._timeField)) return new SMTJavaAIError("未指定时间字段无法聚合"); TimeGroupInfo timeGroupInfo = new TimeGroupInfo(); if(SMTStatic.isNullOrEmpty(sGroupStep)) { timeGroupInfo._unit = GROUP_UNIT.NONE; if(r_isSummary[0] != ASTCubeRecsType.RECORD) r_isSummary[0] = ASTCubeRecsType.SUMMARY; if(r_colNames != null && r_colNames[0] != null) r_colNames[0] = new String[] {"值"}; } else { int[] aTimeStep = SMTAIServerApp.convStrToTimeStep(sGroupStep); if(aTimeStep == null) return new SMTJavaAIError("无法辨认分组步长:" + sGroupStep); timeGroupInfo._unit = aTimeStep[0] == 0 ? GROUP_UNIT.MINUTE : GROUP_UNIT.MONTH; timeGroupInfo._step = aTimeStep[1]; if(SMTStatic.isNullOrEmpty(this._chartType) && (aTimeStep[0] == 1 || aTimeStep[1] >= 24 * 60)) { this._chartType = "bar"; } if(r_colNames != null && r_colNames[0] != null) r_colNames[0] = new String[] {"时间", "值"}; } TreeMap> mapTime2Recs = new TreeMap<>(); SMTJavaAIError errors[] = new SMTJavaAIError[1]; DBRecords[] recsGroups = new DBRecords[1]; if(_timeRange == null) { throw new Exception("_timeRange can't null"); } else { for(DBRecord rec : _recsValue.getRecords()) { // 初始化字段名 if(recsGroups[0] == null) { recsGroups[0] = new DBRecords(); if(r_isSummary[0] == ASTCubeRecsType.RECORD && timeGroupInfo._unit == GROUP_UNIT.NONE) recsGroups[0].initColumn(new String[] {_groupField}); else recsGroups[0].initColumn(new String[] {_timeRange._timeField, _groupField}); } // 对齐时间 Date time = alignGroupTime(timeGroupInfo, rec.getDate(_timeRange._timeField)); for(SMTDimensionDef dimDef : evalColInfo._setDimDef) { char valueType = dimDef.getType(); // 获取原始值,忽略空值 Object curValue = convGroupTypeValue(valueType, rec.getValue(dimDef.getId())); Map orgValueMap = mapTime2Recs.get(time); if(orgValueMap == null) { orgValueMap = new HashMap<>(); mapTime2Recs.put(time, orgValueMap); } Object orgValue; // 进行统计计算 orgValue = orgValueMap.get("min$" + dimDef.getId()); if(orgValue == null || compGroupTypeValue(valueType, orgValue, curValue) > 0) orgValueMap.put("min$" + dimDef.getId(), SMTStatic.toDouble(curValue)); orgValue = orgValueMap.get("max$" + dimDef.getId()); if(orgValue == null || compGroupTypeValue(valueType, orgValue, curValue) < 0) orgValueMap.put("max$" + dimDef.getId(), SMTStatic.toDouble(curValue)); orgValue = orgValueMap.get("count$" + dimDef.getId()); if(orgValue == null) { orgValueMap.put("count$" + dimDef.getId(), 1); } else { orgValueMap.put("count$" + dimDef.getId(), SMTStatic.toInt(orgValue) + 1); } orgValue = orgValueMap.get("sum$" + dimDef.getId()); if(orgValue == null) orgValue = (double)0; orgValueMap.put("sum$" + dimDef.getId(), SMTStatic.toDouble(orgValue) + SMTStatic.toDouble(curValue)); } } if(errors[0] != null) return errors[0]; // 汇总成记录 for(Entry> entry : mapTime2Recs.entrySet()) { Object[] values = new Object[2]; values[0] = entry.getKey(); values[1] = Ognl.getValue(evalColInfo._evalBin, entry.getValue()); if(r_isSummary[0] == ASTCubeRecsType.RECORD && timeGroupInfo._unit == GROUP_UNIT.NONE) recsGroups[0].addRecord(new Object[] {values[1]}); else recsGroups[0].addRecord(values); } // 输出结果 if(recsGroups[0] == null) { recsGroups[0] = new DBRecords(); if(r_isSummary[0] == ASTCubeRecsType.RECORD && timeGroupInfo._unit == GROUP_UNIT.NONE) recsGroups[0].initColumn(new String[] {_groupField}); else recsGroups[0].initColumn(new String[] {_timeRange._timeField, _groupField}); recsGroups[0].addRecord(new Object[] {0}); } _recsValue = recsGroups[0]; } return null; } public SMTJavaAIError groupValueRecords(String sGroupOP, String sGroupStep, ASTCubeRecsType[] r_isSummary, String[][] r_colNames) throws Exception { if(_recsValue == null) return null; if(!SMTStatic.isNullOrEmpty(sGroupStep) && SMTStatic.isNullOrEmpty(_timeRange._timeField)) return new SMTJavaAIError("未指定时间字段无法聚合"); TimeGroupInfo timeGroupInfo = new TimeGroupInfo(); boolean noGroupField = SMTStatic.isNullOrEmpty(_groupField); String sGroupTitle; if("MAX".equalsIgnoreCase(sGroupOP)) { timeGroupInfo._op = GROUP_OP.MAX; sGroupTitle = "最大值"; } else if("MIN".equalsIgnoreCase(sGroupOP)) { timeGroupInfo._op = GROUP_OP.MIN; sGroupTitle = "最小值"; } else if("AVG".equalsIgnoreCase(sGroupOP)) { timeGroupInfo._op = GROUP_OP.AVG; sGroupTitle = "平均值"; } else if("SUM".equalsIgnoreCase(sGroupOP)) { timeGroupInfo._op = GROUP_OP.SUM; sGroupTitle = "累计值"; } else if("COUNT".equalsIgnoreCase(sGroupOP)) { timeGroupInfo._op = GROUP_OP.COUNT; sGroupTitle = "个数"; } else return new SMTJavaAIError("无法辨认分组操作符:" + sGroupOP); if(timeGroupInfo._op != GROUP_OP.COUNT && SMTStatic.isNullOrEmpty(_groupField)) return new SMTJavaAIError("未指定值字段无法统计"); if(SMTStatic.isNullOrEmpty(sGroupStep)) { timeGroupInfo._unit = GROUP_UNIT.NONE; if(r_isSummary[0] != ASTCubeRecsType.RECORD) r_isSummary[0] = ASTCubeRecsType.SUMMARY; if(r_colNames != null && r_colNames[0] != null) r_colNames[0] = new String[] {sGroupTitle}; } else { int[] aTimeStep = SMTAIServerApp.convStrToTimeStep(sGroupStep); if(aTimeStep == null) return new SMTJavaAIError("无法辨认分组步长:" + sGroupStep); timeGroupInfo._unit = aTimeStep[0] == 0 ? GROUP_UNIT.MINUTE : GROUP_UNIT.MONTH; timeGroupInfo._step = aTimeStep[1]; if(SMTStatic.isNullOrEmpty(this._chartType) && (aTimeStep[0] == 1 || aTimeStep[1] >= 24 * 60)) { this._chartType = "bar"; } if(r_colNames != null && r_colNames[0] != null) r_colNames[0] = new String[] {"时间", sGroupTitle}; } TreeMap mapTime2Recs = new TreeMap<>(); TreeMap mapTime2Count = new TreeMap<>(); SMTJavaAIError errors[] = new SMTJavaAIError[1]; DBRecords[] recsGroups = new DBRecords[1]; if(_timeRange == null) { DBRecords recsValue = new DBRecords(); if(noGroupField) _groupField = "CNT"; recsValue.initColumn(new String[] {_groupField}); recsValue.addRecord(new Object[] {_recsValue.getRowCount()}); _recsValue = recsValue; } else { for(DBRecord rec : _recsValue.getRecords()) { // 初始化字段名 if(recsGroups[0] == null) { recsGroups[0] = new DBRecords(); if(r_isSummary[0] == ASTCubeRecsType.RECORD && timeGroupInfo._unit == GROUP_UNIT.NONE) recsGroups[0].initColumn(new String[] {noGroupField ? "CNT" : _groupField}); else recsGroups[0].initColumn(new String[] {_timeRange._timeField, noGroupField ? "CNT" : _groupField}); } // 获取原始值,忽略空值 Object curValue = noGroupField ? 0 : convGroupTypeValue(_groupType, rec.getValue(_groupField)); if(curValue == null) continue; // 对齐时间 Date time = alignGroupTime(timeGroupInfo, rec.getDate(_timeRange._timeField)); Object orgValue = mapTime2Recs.get(time); // 进行统计计算 switch(timeGroupInfo._op) { case MIN: if(orgValue == null || compGroupTypeValue(_groupType, orgValue, curValue) > 0) mapTime2Recs.put(time, curValue); break; case MAX: if(orgValue == null || compGroupTypeValue(_groupType, orgValue, curValue) < 0) mapTime2Recs.put(time, curValue); break; case SUM: case AVG: case COUNT: if(orgValue == null) { if(!noGroupField) mapTime2Recs.put(time, curValue); else mapTime2Recs.put(time, 0); mapTime2Count.put(time, 1); } else { mapTime2Count.put(time, mapTime2Count.get(time) + 1); if(!noGroupField) { switch(_groupType) { case 'I': mapTime2Recs.put(time, (int)orgValue + (int)curValue); break; case 'L': mapTime2Recs.put(time, (long)orgValue + (long)curValue); break; case 'D': mapTime2Recs.put(time, (double)orgValue + (double)curValue); break; } } } break; } } if(errors[0] != null) return errors[0]; // 汇总成记录 for(Entry entry : mapTime2Recs.entrySet()) { Object[] values = new Object[2]; values[0] = entry.getKey(); switch(timeGroupInfo._op) { case MIN: case MAX: case SUM: values[1] = entry.getValue(); break; case AVG: { int count = mapTime2Count.get(entry.getKey()); switch(_groupType) { case 'I': values[1] = (int)((int)entry.getValue() / count); break; case 'L': values[1] = (long)((long)entry.getValue() / count); break; case 'D': values[1] = (double)((double)entry.getValue() / count); break; } } break; case COUNT: values[1] = mapTime2Count.get(entry.getKey()); break; default: break; } if(r_isSummary[0] == ASTCubeRecsType.RECORD && timeGroupInfo._unit == GROUP_UNIT.NONE) recsGroups[0].addRecord(new Object[] {values[1]}); else recsGroups[0].addRecord(values); } if(recsGroups[0] == null) { recsGroups[0] = new DBRecords(); if(r_isSummary[0] == ASTCubeRecsType.RECORD && timeGroupInfo._unit == GROUP_UNIT.NONE) recsGroups[0].initColumn(new String[] {noGroupField ? "CNT" : _groupField}); else recsGroups[0].initColumn(new String[] {_timeRange._timeField, noGroupField ? "CNT" : _groupField}); recsGroups[0].addRecord(new Object[] {0}); } _recsValue = recsGroups[0]; } return null; } public SMTJavaAIError diffValueRecords(String strDiffOP, ASTCubeRecsValue tagASTRecsValue) throws Exception { DIFF_OP diffOP; if("ADD".equals(strDiffOP)) diffOP = DIFF_OP.ADD; else if("SUB".equals(strDiffOP)) diffOP = DIFF_OP.SUB; else if("YOY".equals(strDiffOP)) diffOP = DIFF_OP.YOY; else if("MOM".equals(strDiffOP)) diffOP = DIFF_OP.YOY; else throw new Exception("未支持的差分:" + strDiffOP); if(_timeRange._startTime == null || tagASTRecsValue._timeRange._startTime == null) return new SMTJavaAIError("无延时数据可对比"); if(SMTStatic.isNullOrEmpty(_groupField) || SMTStatic.isNullOrEmpty(tagASTRecsValue._groupField)) return new SMTJavaAIError("无延时字段可对比"); _timeRange.copyToTimeRange2(tagASTRecsValue._timeRange); // 计算时间偏差 long subTime = tagASTRecsValue._timeRange._startTime.getTime() - this._timeRange._startTime.getTime(); // 将目标延时数据转换成映射表 HashMap mapTime2Value = new HashMap<>(); for(DBRecord recTag : tagASTRecsValue._recsValue.getRecords()) { mapTime2Value.put( new Date(recTag.getDate(tagASTRecsValue._timeRange._timeField).getTime() - subTime), recTag.getValue(tagASTRecsValue._groupField) ); } // 创建新的结果集 DBRecords recsDiff = new DBRecords(); recsDiff.initColumn(new String[] {this._timeRange._timeField, this._groupField}); // 扫描源表记录 for(DBRecord recSrc : this._recsValue.getRecords()) { Date time = recSrc.getDate(this._timeRange._timeField); Object srcValue= recSrc.getValue(this._groupField); if(srcValue == null) continue; Object tagValue = mapTime2Value.get(time); if(tagValue == null) continue; Object diffValue = null; switch(diffOP) { case ADD: switch(_groupType) { case 'I': diffValue = (int)srcValue + (int)tagValue; break; case 'L': diffValue = (long)srcValue + (long)tagValue; break; case 'D': diffValue = (double)srcValue + (double)tagValue; break; } break; case SUB: switch(_groupType) { case 'I': diffValue = (int)srcValue - (int)tagValue; break; case 'L': diffValue = (long)srcValue - (long)tagValue; break; case 'D': diffValue = (double)srcValue - (double)tagValue; break; } break; case YOY: switch(_groupType) { case 'I': diffValue = (((int)tagValue == 0) ? 0 : (double)((int)srcValue - (int)tagValue) / (int)tagValue); break; case 'L': diffValue = (((long)tagValue == 0) ? 0 : (double)((long)srcValue - (long)tagValue) / (long)tagValue); break; case 'D': diffValue = (((double)tagValue == 0) ? 0 : ((double)srcValue - (double)tagValue) / (double)tagValue); break; } break; } recsDiff.addRecord(new Object[] {time, diffValue}); } this._recsValue = recsDiff; return null; } }