package org.gcube.accounting.analytics.persistence.postgresql; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.HashSet; import java.util.List; import java.util.Set; import org.gcube.accounting.analytics.Filter; import org.gcube.accounting.analytics.TemporalConstraint; import org.gcube.accounting.analytics.TemporalConstraint.AggregationMode; import org.gcube.accounting.analytics.TemporalConstraint.CalendarEnum; import org.gcube.accounting.datamodel.UsageRecord; import org.gcube.accounting.datamodel.aggregation.AggregatedServiceUsageRecord; import org.gcube.accounting.utility.postgresql.RecordToDBFields; import org.gcube.accounting.utility.postgresql.RecordToDBMapping; import org.gcube.documentstore.records.AggregatedRecord; public class Query { public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS Z"; public static final String DATE_OF_TIMESERIES_AS_FIELD = AggregatedRecord.START_TIME; private Class> clz; private final RecordToDBFields recordToDBMapper; private List requestedTableField; private StringBuffer stringBuffer; protected TemporalConstraint temporalConstraint; protected List filters; protected Set contexts; public Query(Class> clz) throws Exception { this.clz = clz; this.recordToDBMapper = RecordToDBMapping.getRecordToDB(clz); } public List getRequestedTableField() { return requestedTableField; } public void setTemporalConstraint(TemporalConstraint temporalConstraint) { this.temporalConstraint = temporalConstraint; } public void setFilters(List filters) { this.filters = filters; } public void setContexts(Set contexts) { this.contexts = contexts; } public void addContext(String context) { if(contexts == null) { contexts = new HashSet<>(); } contexts.add(context); } public RecordToDBFields getRecordToDBMapper() { return recordToDBMapper; } private void appendString(String string) { stringBuffer.append("'"); stringBuffer.append(string); stringBuffer.append("'"); } protected String getTableField(String fieldName) { return recordToDBMapper.getTableField(fieldName); } protected void appendTableField(String fieldName) { stringBuffer.append(getTableField(fieldName)); } public static String getFormattedDate(Calendar calendar) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATETIME_PATTERN); String date = simpleDateFormat.format(calendar.getTime()); return date; } protected void appendValue(Serializable serializable) { if(serializable instanceof Number) { stringBuffer.append(serializable.toString()); return; } if(serializable instanceof Calendar) { Calendar calendar = (Calendar) serializable; appendString(getFormattedDate(calendar)); return; } if(serializable instanceof Enum) { Enum e = (Enum) serializable; appendString(e.name()); return; } // String, URI etc appendString(serializable.toString()); } protected void addFilters() { if(filters!=null && filters.size()>0) { // The first filter if the time_bucket stringBuffer.append(" AND "); boolean first = true; for(Filter filter : filters) { if(first) { first = false; }else { stringBuffer.append(" AND "); } appendTableField(filter.getKey()); stringBuffer.append("="); appendValue(filter.getValue()); } } } protected void addContextFilter() { if(contexts!=null && contexts.size()>0) { // The first filter if the time_bucket stringBuffer.append(" AND ("); boolean first = true; for(String context : contexts) { if(first) { first = false; }else { stringBuffer.append(" OR "); } appendTableField(UsageRecord.SCOPE); stringBuffer.append("="); appendValue(context); } stringBuffer.append(")"); } } protected void addEmittedFields() throws Exception { Set aggregatedField = clz.newInstance().getAggregatedFields(); for(String fieldName : aggregatedField) { String dbField = getTableField(fieldName); switch (fieldName) { case AggregatedRecord.START_TIME: case AggregatedRecord.END_TIME: case AggregatedRecord.AGGREGATED: continue; case AggregatedRecord.OPERATION_COUNT: stringBuffer.append(", SUM("); stringBuffer.append(dbField); stringBuffer.append(") AS "); break; // WEIGHTED AVERAGE case AggregatedServiceUsageRecord.DURATION: stringBuffer.append(", ROUND(SUM("); stringBuffer.append(dbField); stringBuffer.append("*"); appendTableField(AggregatedRecord.OPERATION_COUNT); stringBuffer.append(")/SUM("); appendTableField(AggregatedRecord.OPERATION_COUNT); stringBuffer.append(")) AS "); break; case AggregatedServiceUsageRecord.MAX_INVOCATION_TIME: stringBuffer.append(", MAX("); stringBuffer.append(dbField); stringBuffer.append(") AS "); break; case AggregatedServiceUsageRecord.MIN_INVOCATION_TIME: stringBuffer.append(", MIN("); stringBuffer.append(dbField); stringBuffer.append(") AS "); break; default: stringBuffer.append(", "); break; } stringBuffer.append(dbField); requestedTableField.add(dbField); } } protected String getTimeBucketCalendarInterval(AggregationMode aggregationMode) { CalendarEnum calendarEnum = CalendarEnum.values()[aggregationMode.ordinal()]; return calendarEnum.name().toLowerCase(); } protected void addTimeBucket() { stringBuffer.append("time_bucket('1 "); String calendarInterval = getTimeBucketCalendarInterval(temporalConstraint.getAggregationMode()); stringBuffer.append(calendarInterval); stringBuffer.append("',"); appendTableField(AggregatedRecord.START_TIME); stringBuffer.append(") AS "); stringBuffer.append(DATE_OF_TIMESERIES_AS_FIELD); requestedTableField.add(DATE_OF_TIMESERIES_AS_FIELD); } private void newQuery() { stringBuffer = new StringBuffer(); requestedTableField = new ArrayList<>(); stringBuffer.append("SELECT "); } protected void addTemporalConstraintToQuery() { stringBuffer.append(" WHERE "); String tableField = getTableField(AggregatedRecord.START_TIME); stringBuffer.append(tableField); stringBuffer.append(" > "); appendValue(temporalConstraint.getAlignedStartTime()); stringBuffer.append(" AND "); stringBuffer.append(tableField); stringBuffer.append(" < "); appendValue(temporalConstraint.getAlignedEndTime()); } protected void addGropuBy() { stringBuffer.append(" GROUP BY "); stringBuffer.append(DATE_OF_TIMESERIES_AS_FIELD); } protected void addOrderBy() { stringBuffer.append(" ORDER BY "); stringBuffer.append(DATE_OF_TIMESERIES_AS_FIELD); stringBuffer.append(" ASC"); } public String getTimeSeriesQuery() throws Exception { newQuery(); addTimeBucket(); addEmittedFields(); stringBuffer.append(" FROM "); stringBuffer.append(recordToDBMapper.getTableName()); addTemporalConstraintToQuery(); addFilters(); addContextFilter(); addGropuBy(); addOrderBy(); return stringBuffer.toString(); } }