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<DBRecord> recs = _recsValue.getRecords();
|
|
Set<Date> 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<Date, Map<String, Object>> 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<String, Object> 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<Date, Map<String, Object>> 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<Date, Object> mapTime2Recs = new TreeMap<>();
|
TreeMap<Date, Integer> 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<Date, Object> 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<Date, Object> 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;
|
}
|
}
|