package com.smtaiserver.smtaiserver.javaai.metrics;
|
|
import java.util.ArrayList;
|
import java.util.Collection;
|
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import org.apache.ibatis.ognl.Ognl;
|
import org.dom4j.Document;
|
import org.dom4j.Element;
|
import org.dom4j.Node;
|
import org.duckdb.DuckDBAppender;
|
import org.duckdb.DuckDBConnection;
|
|
import com.smtaiserver.smtaiserver.core.SMTAIServerApp;
|
import com.smtaiserver.smtaiserver.core.SMTAIServerRequest;
|
import com.smtaiserver.smtaiserver.database.SMTDatabase;
|
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.ASTDBMap;
|
import com.smtaiserver.smtaiserver.javaai.ast.ASTDimFilter;
|
import com.smtaiserver.smtaiserver.javaai.ast.ASTEvalColInfo;
|
import com.smtaiserver.smtaiserver.javaai.ast.ASTTimeRange;
|
import com.smtaiserver.smtaiserver.javaai.duckdb.DuckCubeRecs;
|
import com.smtaiserver.smtaiserver.javaai.duckdb.DuckCubeRecs.DuckCubeColTitle;
|
import com.smtaiserver.smtaiserver.javaai.duckdb.DuckCubeRecs.DuckCubeRecsType;
|
import com.smtaiserver.smtaiserver.javaai.duckdb.DuckResult;
|
import com.smtaiserver.smtaiserver.javaai.metrics.base.SMTDimensionDef;
|
import com.smtaiserver.smtaiserver.javaai.metrics.base.SMTMetricSqlXml;
|
import com.smtaiserver.smtaiserver.javaai.metrics.base.SMTMetricsDefXmlDim;
|
import com.smtaiserver.smtaiserver.javaai.metrics.base.SMTMetricSqlXml.SQLXMLQuery;
|
import com.smtservlet.util.Json;
|
import com.smtservlet.util.SMTStatic;
|
|
public class SMTMetricsDefDuckTimeRec extends SMTMetricsDefXmlDim
|
{
|
|
private static class TimeBucketTable
|
{
|
public String _simSqlPerfix;
|
public String _chartType;
|
public String _timeField;
|
public int _timeStepUnit;
|
public int _timeStepValue;
|
public SMTMetricSqlXml _sqlxmlMeticValue;
|
public int _colKeyCount;
|
|
public TimeBucketTable(Element xmlTimeBucket) throws Exception
|
{
|
_simSqlPerfix = "";
|
String sTimeStep = SMTStatic.getXmlAttr(xmlTimeBucket, "time_step");
|
_chartType = SMTStatic.getXmlAttr(xmlTimeBucket, "chart_type", "");
|
|
int[] aTimeStep = SMTAIServerApp.convStrToTimeStep(sTimeStep);
|
if(aTimeStep == null)
|
throw new Exception("can't parse time_step : " + sTimeStep);
|
|
_timeStepUnit = aTimeStep[0];
|
_timeStepValue = aTimeStep[1];
|
|
Element xmlValueSQL = (Element)xmlTimeBucket.selectSingleNode("VALUE_SQL");
|
String sValueSQLRef = SMTStatic.getXmlAttr(xmlValueSQL, "ref", null);
|
if(!SMTStatic.isNullOrEmpty(sValueSQLRef))
|
xmlValueSQL = (Element) xmlValueSQL.getDocument().selectSingleNode("ROOT/" + sValueSQLRef);
|
_timeField = SMTStatic.getXmlAttr(xmlValueSQL, "time_field").toUpperCase();
|
_sqlxmlMeticValue = new SMTMetricSqlXml(xmlValueSQL);
|
|
_colKeyCount = SMTStatic.toInt(SMTStatic.getXmlAttr(xmlValueSQL, "key_cols", "1"));
|
|
}
|
|
public int compareTimeStep(int unit, int value)
|
{
|
int subUnit = _timeStepUnit - unit;
|
if(subUnit != 0)
|
return subUnit;
|
|
int subValue = _timeStepValue - value;
|
if(subValue == 0)
|
return 0;
|
if(subValue < 0)
|
return -1;
|
|
return 1;
|
}
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
private static class OgnlRecMap implements Map<String, Object>
|
{
|
public DBRecord _rec;
|
|
@Override
|
public int size() {
|
return 0;
|
}
|
|
@Override
|
public boolean isEmpty() {
|
return false;
|
}
|
|
@Override
|
public boolean containsKey(Object key) {
|
return false;
|
}
|
|
@Override
|
public boolean containsValue(Object value) {
|
return false;
|
}
|
|
@Override
|
public Object get(Object key) {
|
try {
|
String[] sp = ((String)key).split("\\$");
|
if(sp[0].equals("count"))
|
return 1;
|
else
|
return _rec.getValue(sp[1]);
|
} catch (Exception e) {
|
throw new RuntimeException("get record value error : " + key, e);
|
}
|
}
|
|
@Override
|
public Object put(String key, Object value) {
|
return null;
|
}
|
|
@Override
|
public Object remove(Object key) {
|
return null;
|
}
|
|
@Override
|
public void putAll(Map<? extends String, ? extends Object> m) {
|
}
|
|
@Override
|
public void clear() {
|
}
|
|
@Override
|
public Set<String> keySet() {
|
return null;
|
}
|
|
@Override
|
public Collection<Object> values() {
|
return null;
|
}
|
|
@Override
|
public Set<Entry<String, Object>> entrySet() {
|
return null;
|
}
|
}
|
|
|
private List<TimeBucketTable> _listTimeBucketTable = new ArrayList<>();
|
private Map<String, ASTEvalColInfo> _mapId2EvalColInfo = new HashMap<>();
|
|
@Override
|
protected void initInstanceByDoc(DBRecord rec, Document doc) throws Exception
|
{
|
super.initInstanceByDoc(rec, doc);
|
|
// 读取计算字段信息
|
for(Node nodeCalcCol : doc.selectNodes("ROOT/CALC_COLS/CALC_COL"))
|
{
|
ASTEvalColInfo evalColInfo = new ASTEvalColInfo((Element)nodeCalcCol);
|
_mapId2EvalColInfo.put(evalColInfo._id, evalColInfo);
|
}
|
|
// 将时序表
|
for(Node nodeTimeBucket : doc.selectNodes("ROOT/TIME_BUCKET"))
|
{
|
TimeBucketTable timeBucketTable = new TimeBucketTable((Element)nodeTimeBucket);
|
for(int i = 0; i < _listTimeBucketTable.size(); i ++)
|
{
|
TimeBucketTable curTable = _listTimeBucketTable.get(i);
|
|
int comp = curTable.compareTimeStep(timeBucketTable._timeStepUnit, timeBucketTable._timeStepValue);
|
|
if(comp == 0)
|
{
|
throw new Exception("time bucket existed at : " + SMTStatic.toInt(i));
|
}
|
else if(comp < 0)
|
{
|
_listTimeBucketTable.add(i, timeBucketTable);
|
timeBucketTable = null;
|
break;
|
}
|
}
|
if(timeBucketTable != null)
|
_listTimeBucketTable.add(timeBucketTable);
|
}
|
|
if(_listTimeBucketTable.size() == 0)
|
throw new Exception("time bucket is empty : " + this._id + " : " + this._title);
|
|
}
|
|
private SMTJavaAIError calculateEvalRS(DBRecord rec) throws Exception
|
{
|
for(ASTEvalColInfo evalColInfo : _mapId2EvalColInfo.values())
|
{
|
int colIndex = rec.getColIndex(evalColInfo._id);
|
if(colIndex < 0)
|
continue;
|
|
OgnlRecMap evalMap = new OgnlRecMap();
|
|
evalMap._rec = rec;
|
|
Object[] values = rec.getValues();
|
values[colIndex] = Ognl.getValue(evalColInfo._evalBin, evalMap);
|
}
|
|
return null;
|
}
|
|
@Override
|
public SMTJavaAIError queryMetrics(String jsonPath, ASTDBMap dbMap, Json jsonAST, Map<String, String> extArg, SMTAIServerRequest tranReq, DuckResult r_result) throws Exception
|
{
|
SMTJavaAIError error = this.executeQueryMetrics(jsonPath, dbMap, jsonAST, extArg, tranReq, r_result);
|
if(error != null)
|
{
|
appendSampleQuestionToResponse(jsonAST, tranReq);
|
return error;
|
}
|
else
|
{
|
return null;
|
}
|
}
|
|
private SMTJavaAIError queryTimeGroupRecords(DuckCubeRecs astRS, SQLXMLQuery queryValue, TimeBucketTable timeBucket, int[] timeStep, String jsonPath, String stepOpKey, Json jsonAST, SMTAIServerRequest tranReq) throws Exception
|
{
|
String sGroupField;
|
String groupOperate;
|
|
// 分组统计后,分组字段意义就失去了
|
astRS._dimNames.clear();
|
|
if("COUNT".equals(stepOpKey))
|
{
|
astRS._mapCol2Title.put("__CNT__", new DuckCubeColTitle("个数", true, true));
|
sGroupField = "*";
|
groupOperate = "COUNT";
|
}
|
else
|
{
|
sGroupField = jsonAST.safeGetStr("step_dim_name", null);
|
if(SMTStatic.isNullOrEmpty(sGroupField))
|
return new SMTJavaAIError("未指定需要统计的字段");
|
SMTSQLXMLDimDef sqlXmlDimDef = this._mapId2SqlDimDef.get(sGroupField);
|
if(sqlXmlDimDef == null)
|
return new SMTJavaAIError("需要统计的维度[" + sGroupField + "]未定义");
|
|
// 设置标题
|
SMTDimensionDef dimDef = SMTAIServerApp.getApp().getDimensionDef(sGroupField);
|
|
groupOperate = jsonAST.getJson("step_op_key").asString();
|
astRS._title += jsonAST.safeGetStr("step_op_title", "");
|
astRS._chartUnit = dimDef.getUnitName(true);
|
astRS._mapCol2Title.put("__CNT__", new DuckCubeColTitle(jsonAST.safeGetStr("step_op_title", "") + dimDef.getUnitName(true), true, false));
|
}
|
|
String timeField = "time_bucket('" + timeStep[1] + (timeStep[0] == 0 ? " minutes" : " months") + "', " + timeBucket._timeField + ")";
|
|
SMTDatabase db = tranReq.getResultDB();
|
String oldTableName = "d_" + SMTStatic.newUUID();
|
|
|
String sGroupType = "";
|
if("SUM".equals(groupOperate) || "AVG".equals(groupOperate))
|
sGroupType = "::float8";
|
|
String sql =
|
" SELECT"
|
+ " " + timeField + " AS " + timeBucket._timeField
|
+ " ,"+ groupOperate + "(" + sGroupField + ")" + sGroupType + " AS __CNT__"
|
+ " FROM " + oldTableName
|
+ " GROUP BY " + timeField
|
;
|
|
db.executeSQL("ALTER TABLE " + astRS._tableName + " RENAME TO " + oldTableName, null);
|
|
DuckDBAppender[] appender = new DuckDBAppender[1];
|
try
|
{
|
tranReq.traceLLMDebug("queryTimeGroupRecords SQL:\n" + sql);
|
db.querySQLNotify(sql, null, new SMTDatabase.DBQueryNotifyMeta()
|
{
|
@Override
|
public boolean onMetaInfo(DBRecords metaInfo, String[] colTypes) throws Exception
|
{
|
SMTDatabase dbResult = tranReq.createResultTable(astRS._tableName, metaInfo, colTypes);
|
appender[0] = ((DuckDBConnection)dbResult.getConnection()).createAppender(DuckDBConnection.DEFAULT_SCHEMA, astRS._tableName);
|
return true;
|
}
|
|
@Override
|
public boolean onNextRecord(DBRecord rec) throws Exception
|
{
|
Object[] values = rec.getValues();
|
appender[0].beginRow();
|
for(Object value : values)
|
{
|
if(value == null)
|
appender[0].append(null);
|
else
|
appender[0].append(SMTStatic.toString(value));
|
}
|
appender[0].endRow();
|
return true;
|
}
|
|
|
});
|
}
|
finally
|
{
|
if(appender[0] != null)
|
{
|
appender[0].close();
|
appender[0] = null;
|
}
|
}
|
|
|
db.executeSQL("DROP TABLE " + oldTableName, null);
|
|
|
return null;
|
}
|
|
private SMTJavaAIError queryHasDimGroupRecords(DuckCubeRecs astRS, SQLXMLQuery queryValue, TimeBucketTable timeBucket, String startTime, String endTime, String jsonPath, String stepOpKey, Json jsonAST, SMTAIServerRequest tranReq) throws Exception
|
{
|
String sGroupMainField = "";
|
String sGroupAggField = "*";
|
String sGroupTitle = "";
|
String groupOperate = "COUNT";
|
for(int i = 0; i < astRS._dimNames.size(); i ++)
|
{
|
if(i > 0)
|
sGroupMainField += ",";
|
sGroupMainField += astRS._dimNames.get(i);
|
|
if(sGroupTitle.length() > 0)
|
sGroupTitle += "-";
|
|
sGroupTitle += SMTAIServerApp.getApp().getDimensionDef(astRS._dimNames.get(i)).getName();
|
}
|
|
|
astRS._title = "按照" + sGroupTitle + "分类的" + astRS._title;
|
|
if(!"COUNT".equals(stepOpKey))
|
{
|
sGroupAggField = jsonAST.safeGetStr("step_dim_name", null);
|
if(SMTStatic.isNullOrEmpty(sGroupAggField))
|
return new SMTJavaAIError("未指定需要统计的字段");
|
SMTSQLXMLDimDef sqlXmlDimDef = this._mapId2SqlDimDef.get(sGroupAggField);
|
if(sqlXmlDimDef == null)
|
return new SMTJavaAIError("需要统计的维度[" + sGroupAggField + "]未定义");
|
|
// 设置标题
|
SMTDimensionDef dimDef = SMTAIServerApp.getApp().getDimensionDef(sGroupAggField);
|
|
groupOperate = jsonAST.getJson("step_op_key").asString();
|
astRS._title += jsonAST.safeGetStr("step_op_title", "");
|
astRS._chartUnit = dimDef.getUnitName(true);
|
astRS._mapCol2Title.put("__CNT__", new DuckCubeColTitle(jsonAST.safeGetStr("step_op_title", "") + dimDef.getUnitName(true), true, false));
|
}
|
else
|
{
|
astRS._mapCol2Title.put("__CNT__", new DuckCubeColTitle("个数", true, true));
|
}
|
|
String sql = "\nSELECT " + sGroupMainField + ", " + groupOperate + "(" + sGroupAggField + ") AS __CNT__ FROM (\n" + queryValue._sbSQLText.toString() + "\n)T \nGROUP BY " + sGroupMainField;
|
|
// 查询结果集
|
StringBuilder sbLogSQL = new StringBuilder();
|
sbLogSQL.append(sql + "\n");
|
for(Object param : queryValue._sqlParams)
|
{
|
sbLogSQL.append(" param : " + SMTStatic.toString(param) + "\n");
|
}
|
tranReq.traceLLMDebug(sbLogSQL.toString());
|
|
astRS._recsType = DuckCubeRecsType.RECORD;
|
astRS._timeRange = new ASTTimeRange();
|
astRS._timeRange._title = _title;
|
astRS._timeRange._startTime = SMTStatic.toDate(startTime);
|
astRS._timeRange._pathStartTime = jsonPath + "start_time";
|
astRS._timeRange._endTime = SMTStatic.toDate(endTime);
|
astRS._timeRange._pathEndTime = jsonPath + "end_time";
|
astRS._timeRange._timeField = timeBucket._timeField;
|
astRS._tableName = "t_" + SMTStatic.newUUID();
|
|
// 分组统计后,分组字段意义就失去了
|
astRS._dimNames.clear();
|
|
DuckDBAppender[] appender = new DuckDBAppender[1];
|
try
|
{
|
queryValue._db.querySQLNotify(sql, queryValue._sqlParams.toArray(new Object[queryValue._sqlParams.size()]), new SMTDatabase.DBQueryNotifyMeta()
|
{
|
@Override
|
public boolean onMetaInfo(DBRecords metaInfo, String[] colTypes) throws Exception
|
{
|
SMTDatabase dbResult = tranReq.createResultTable(astRS._tableName, metaInfo, colTypes);
|
appender[0] = ((DuckDBConnection)dbResult.getConnection()).createAppender(DuckDBConnection.DEFAULT_SCHEMA, astRS._tableName);
|
return true;
|
}
|
|
@Override
|
public boolean onNextRecord(DBRecord rec) throws Exception
|
{
|
Object[] values = rec.getValues();
|
appender[0].beginRow();
|
for(Object value : values)
|
{
|
if(value == null)
|
appender[0].append(null);
|
else
|
appender[0].append(SMTStatic.toString(value));
|
}
|
appender[0].endRow();
|
return true;
|
}
|
|
|
});
|
}
|
finally
|
{
|
if(appender[0] != null)
|
{
|
appender[0].close();
|
appender[0] = null;
|
}
|
}
|
|
|
return null;
|
}
|
|
private SMTJavaAIError summaryGroupNoDim(DuckCubeRecs astRS, SQLXMLQuery queryValue, TimeBucketTable timeBucket, String startTime, String endTime, String jsonPath, String stepOpKey, Json jsonAST, SMTAIServerRequest tranReq) throws Exception
|
{
|
String sGroupField = "*";
|
String groupOperate = "COUNT";
|
|
if(!"COUNT".equals(stepOpKey))
|
{
|
sGroupField = jsonAST.safeGetStr("step_dim_name", null);
|
if(SMTStatic.isNullOrEmpty(sGroupField))
|
return new SMTJavaAIError("未指定需要统计的字段");
|
SMTSQLXMLDimDef sqlXmlDimDef = this._mapId2SqlDimDef.get(sGroupField);
|
if(sqlXmlDimDef == null)
|
return new SMTJavaAIError("需要统计的维度[" + sGroupField + "]未定义");
|
|
// 设置标题
|
SMTDimensionDef dimDef = SMTAIServerApp.getApp().getDimensionDef(sGroupField);
|
|
groupOperate = jsonAST.getJson("step_op_key").asString();
|
astRS._title += jsonAST.safeGetStr("step_op_title", "");
|
astRS._chartUnit = dimDef.getUnitName(true);
|
astRS._mapCol2Title.put("__CNT__", new DuckCubeColTitle(jsonAST.safeGetStr("step_op_title", "") + dimDef.getUnitName(true), true, false));
|
}
|
else
|
{
|
astRS._mapCol2Title.put("__CNT__", new DuckCubeColTitle("个数", true, true));
|
}
|
|
String sql = "\nSELECT " + groupOperate + "(" + sGroupField + ") AS __CNT__ FROM (\n" + queryValue._sbSQLText.toString() + "\n)T";
|
|
// 查询结果集
|
StringBuilder sbLogSQL = new StringBuilder();
|
sbLogSQL.append(sql + "\n");
|
for(Object param : queryValue._sqlParams)
|
{
|
sbLogSQL.append(" param : " + SMTStatic.toString(param) + "\n");
|
}
|
tranReq.traceLLMDebug(sbLogSQL.toString());
|
|
astRS._recsType = DuckCubeRecsType.SUMMARY;
|
|
|
astRS._timeRange = new ASTTimeRange();
|
astRS._timeRange._title = _title;
|
astRS._timeRange._startTime = SMTStatic.toDate(startTime);
|
astRS._timeRange._pathStartTime = jsonPath + "start_time";
|
astRS._timeRange._endTime = SMTStatic.toDate(endTime);
|
astRS._timeRange._pathEndTime = jsonPath + "end_time";
|
astRS._timeRange._timeField = timeBucket._timeField;
|
|
DuckDBAppender[] appender = new DuckDBAppender[1];
|
try
|
{
|
queryValue._db.querySQLNotify(sql, queryValue._sqlParams.toArray(new Object[queryValue._sqlParams.size()]), new SMTDatabase.DBQueryNotifyMeta()
|
{
|
@Override
|
public boolean onMetaInfo(DBRecords metaInfo, String[] colTypes) throws Exception
|
{
|
astRS._tableName = "t_" + SMTStatic.newUUID();
|
SMTDatabase dbResult = tranReq.createResultTable(astRS._tableName, metaInfo, colTypes);
|
appender[0] = ((DuckDBConnection)dbResult.getConnection()).createAppender(DuckDBConnection.DEFAULT_SCHEMA, astRS._tableName);
|
return true;
|
}
|
|
@Override
|
public boolean onNextRecord(DBRecord rec) throws Exception
|
{
|
Object[] values = rec.getValues();
|
appender[0].beginRow();
|
for(Object value : values)
|
{
|
appender[0].append(value == null ? null : SMTStatic.toString(value));
|
}
|
appender[0].endRow();
|
|
return true;
|
}
|
|
|
});
|
}
|
finally
|
{
|
if(appender[0] != null)
|
{
|
appender[0].close();
|
appender[0] = null;
|
}
|
}
|
|
return null;
|
}
|
|
|
|
private SMTJavaAIError executeQueryMetrics(String jsonPath, ASTDBMap dbMap, Json jsonAST, Map<String, String> extArg, SMTAIServerRequest tranReq, DuckResult r_result) throws Exception
|
{
|
DuckCubeRecs astRS = new DuckCubeRecs();
|
astRS._recsType = DuckCubeRecsType.RECORD;
|
astRS._title = this._title;
|
r_result._listRecordset.add(astRS);
|
|
// 将维度过滤器加上
|
astRS._listDimFilter = new ArrayList<>();
|
for(SMTSQLXMLDimDef sqlXmlDimDef : _mapId2SqlDimDef.values())
|
{
|
if(SMTStatic.isNullOrEmpty(sqlXmlDimDef._filterType))
|
continue;
|
|
ASTDimFilter dimFilter = new ASTDimFilter();
|
astRS._listDimFilter.add(dimFilter);
|
dimFilter._dimPath = jsonPath + sqlXmlDimDef._dimDef.getId();
|
dimFilter._filterType = sqlXmlDimDef._filterType;
|
dimFilter._dimDef = sqlXmlDimDef._dimDef;
|
}
|
|
SMTJavaAIError error = null;
|
TimeBucketTable timeBucket = null;
|
|
// 获取步长时间
|
String stepTime = jsonAST.safeGetStr("step_time", null);
|
|
// 如果步长时间不为空,则采用最接近步长时间间隔的值
|
int[] timeStep = null;
|
if(!SMTStatic.isNullOrEmpty(stepTime))
|
{
|
timeStep = SMTAIServerApp.convStrToTimeStep(stepTime);
|
|
if(timeStep != null)
|
{
|
for(TimeBucketTable curTimeBucket : this._listTimeBucketTable)
|
{
|
int comp = curTimeBucket.compareTimeStep(timeStep[0], timeStep[1]);
|
if(comp <= 0)
|
{
|
timeBucket = curTimeBucket;
|
break;
|
}
|
}
|
}
|
|
}
|
|
// 如果步长时间为空,则采用最小时间间隔的值
|
if(timeBucket == null)
|
{
|
timeBucket = this._listTimeBucketTable.get(this._listTimeBucketTable.size() - 1);
|
}
|
|
astRS._colKeyCount = timeBucket._colKeyCount;
|
String chartType = timeBucket._chartType;
|
if(timeStep != null && SMTStatic.isNullOrEmpty(chartType))
|
{
|
if(timeStep[0] == 1 || timeStep[1] >= 24 * 60)
|
chartType = "bar";
|
}
|
|
String startTime = jsonAST.safeGetStr("start_time", null);
|
String endTime = jsonAST.safeGetStr("end_time", null);
|
|
if(SMTStatic.isNullOrEmpty(startTime) || SMTStatic.isNullOrEmpty(endTime))
|
{
|
String[] timeRange = new String[2];
|
if((error = this.promptUserForTimeRange("请输入时间范围", tranReq, timeRange)) != null)
|
return error;
|
|
jsonAST.set("start_time", timeRange[0]);
|
jsonAST.set("end_time", timeRange[1]);
|
startTime = timeRange[0];
|
endTime = timeRange[1];
|
}
|
|
// 如果存在查询名称的sqlxml,则首先查询名称
|
Map<String, Object> mapId2SqlArg = new HashMap<>();
|
|
// 解析要分组的维度列表
|
Map<String, SMTSQLXMLDimDef> mapIdDimDef = new LinkedHashMap<>();
|
if((error = parseDimListFromJson(jsonAST, tranReq, mapIdDimDef)) != null)
|
return error;
|
|
StringBuilder sbDIM_NAME_GROUP = new StringBuilder();
|
for(String name : mapIdDimDef.keySet())
|
{
|
sbDIM_NAME_GROUP.append(",");
|
sbDIM_NAME_GROUP.append(name);
|
}
|
|
// 创建cube记录集
|
for(String dimName : mapIdDimDef.keySet())
|
{
|
astRS._dimNames.add(dimName);
|
}
|
|
// 解析过滤条件
|
StringBuilder sbDIM_NAME_FILTERS = new StringBuilder();
|
if((error = parseSQLFilterFromJson(dbMap, jsonAST, timeBucket._simSqlPerfix, tranReq, sbDIM_NAME_FILTERS, null)) != null)
|
return error;
|
|
// 设置sql参数
|
mapId2SqlArg.put("__METRICS_ID__", this.getId());
|
mapId2SqlArg.put("__DIM_NAME_FILTERS__", sbDIM_NAME_FILTERS.toString());
|
mapId2SqlArg.put("__DIM_NAME_GROUP__", sbDIM_NAME_GROUP.toString());
|
|
// 生成SQL
|
SQLXMLQuery queryValue = new SQLXMLQuery();
|
if((error = timeBucket._sqlxmlMeticValue.parseSQL(dbMap, jsonAST, mapId2SqlArg, tranReq, queryValue)) != null)
|
return error;
|
|
String stepOpKey = jsonAST.safeGetStr("step_op_key", "");
|
|
// 输出查询条件
|
StringBuilder sbQueryProcess = new StringBuilder();
|
sbQueryProcess.append("查询从" + startTime + "到" + endTime);
|
if(!SMTStatic.isNullOrEmpty(stepTime))
|
sbQueryProcess.append("的按照" + stepTime + " 为步长");
|
if(astRS._dimNames.size() > 0)
|
{
|
sbQueryProcess.append("以:");
|
for(String dimName : astRS._dimNames)
|
{
|
sbQueryProcess.append(SMTAIServerApp.getApp().getDimensionDef(dimName).getName() + " ");
|
}
|
sbQueryProcess.append("为分组");
|
}
|
sbQueryProcess.append("的数据...");
|
tranReq.sendChunkedBlock("begin", sbQueryProcess.toString());
|
|
// 如果存在统计,且不存在时间分级,则直接统计个数
|
if(!SMTStatic.isNullOrEmpty(stepOpKey) && timeStep == null)
|
{
|
astRS._title += jsonAST.safeGetStr("step_op_title", "");
|
|
// 按照分组进行统计个数
|
if(astRS._dimNames.size() > 0)
|
{
|
error = this.queryHasDimGroupRecords(astRS, queryValue, timeBucket, startTime, endTime, jsonPath, stepOpKey, jsonAST, tranReq);
|
return error;
|
}
|
// 无分组直接统计个数
|
else
|
{
|
// 全局统计
|
error = this.summaryGroupNoDim(astRS, queryValue, timeBucket, startTime, endTime, jsonPath, stepOpKey, jsonAST, tranReq);
|
return error;
|
}
|
}
|
else
|
{
|
astRS._timeRange = new ASTTimeRange();
|
astRS._timeRange._title = _title;
|
astRS._timeRange._startTime = SMTStatic.toDate(startTime);
|
astRS._timeRange._pathStartTime = jsonPath + "start_time";
|
astRS._timeRange._endTime = SMTStatic.toDate(endTime);
|
astRS._timeRange._pathEndTime = jsonPath + "end_time";
|
astRS._timeRange._timeField = timeBucket._timeField;
|
|
// 查询结果集
|
tranReq.traceLLMDebug(queryValue.getSqlLog());
|
|
DuckDBAppender[] appender = new DuckDBAppender[1];
|
try
|
{
|
queryValue._db.querySQLNotify(queryValue._sbSQLText.toString(), queryValue._sqlParams.toArray(new Object[] {queryValue._sqlParams.size()})
|
, new SMTDatabase.DBQueryNotifyMeta() {
|
|
@Override
|
public boolean onMetaInfo(DBRecords metaInfo, String[] colTypes) throws Exception
|
{
|
astRS._tableName = "t_" + SMTStatic.newUUID();
|
SMTDatabase dbResult = tranReq.createResultTable(astRS._tableName, metaInfo, colTypes);
|
appender[0] = ((DuckDBConnection)dbResult.getConnection()).createAppender(DuckDBConnection.DEFAULT_SCHEMA, astRS._tableName);
|
|
return true;
|
}
|
|
@Override
|
public boolean onNextRecord(DBRecord rec) throws Exception
|
{
|
calculateEvalRS(rec);
|
Object[] values = rec.getValues();
|
|
appender[0].beginRow();
|
for(Object value : values)
|
{
|
if(value == null)
|
appender[0].append(null);
|
else
|
appender[0].append(SMTStatic.toString(value));
|
}
|
appender[0].endRow();
|
|
return true;
|
}
|
});
|
}
|
finally
|
{
|
if(appender[0] != null)
|
{
|
appender[0].close();
|
appender[0] = null;
|
}
|
}
|
|
// 如果存在时间步长,则按照时间步长做二次统计
|
if(timeStep != null)
|
{
|
error = this.queryTimeGroupRecords(astRS, queryValue, timeBucket, timeStep, jsonPath, stepOpKey, jsonAST, tranReq);
|
if(error != null)
|
return null;
|
}
|
|
}
|
|
return null;
|
}
|
}
|