diff --git a/pom.xml b/pom.xml
index a5b2adf..3bb0ee2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
org.gcube.accounting
accounting-analytics
- 1.2.0-SNAPSHOT
+ 2.0.0-SNAPSHOT
accounting-analytics
diff --git a/src/main/java/org/gcube/accounting/analytics/Filter.java b/src/main/java/org/gcube/accounting/analytics/Filter.java
index 17d8aa0..a6d09f3 100644
--- a/src/main/java/org/gcube/accounting/analytics/Filter.java
+++ b/src/main/java/org/gcube/accounting/analytics/Filter.java
@@ -7,7 +7,7 @@ package org.gcube.accounting.analytics;
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
*
*/
-public class Filter {
+public class Filter implements Comparable {
protected String key;
protected String value;
@@ -49,4 +49,53 @@ public class Filter {
this.value = value;
}
+ @Override
+ public String toString(){
+ return String.format("{ \"%s\" : \"%s\" }", key, value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int compareTo(Filter filter) {
+ int compareResult = this.key.compareTo(filter.key);
+ if(compareResult == 0){
+ compareResult = this.value.compareTo(filter.value);
+ }
+ return compareResult;
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((key == null) ? 0 : key.hashCode());
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Filter other = (Filter) obj;
+ if (key == null) {
+ if (other.key != null)
+ return false;
+ } else if (!key.equals(other.key))
+ return false;
+ if (value == null) {
+ if (other.value != null)
+ return false;
+ } else if (!value.equals(other.value))
+ return false;
+ return true;
+ }
+
}
diff --git a/src/main/java/org/gcube/accounting/analytics/ResourceRecordQuery.java b/src/main/java/org/gcube/accounting/analytics/ResourceRecordQuery.java
index b35445d..602a85f 100644
--- a/src/main/java/org/gcube/accounting/analytics/ResourceRecordQuery.java
+++ b/src/main/java/org/gcube/accounting/analytics/ResourceRecordQuery.java
@@ -6,7 +6,6 @@ package org.gcube.accounting.analytics;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -29,139 +28,156 @@ import org.slf4j.LoggerFactory;
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
*/
public class ResourceRecordQuery {
-
- private static Logger logger = LoggerFactory.getLogger(ResourceRecordQuery.class);
-
+
+ private static Logger logger = LoggerFactory
+ .getLogger(ResourceRecordQuery.class);
+
protected static Map, Set> resourceRecords = null;
-
+
/**
- * Return a Map containing a set of required fields for each Resource
+ * Return a Map containing a set of required fields for each Resource
* Records Types
+ *
* @return the Map
*/
public static synchronized Map, Set> getResourceRecordsTypes() {
- if(resourceRecords==null){
+ if (resourceRecords == null) {
resourceRecords = new HashMap, Set>();
- Collection> resourceRecordsTypes = RecordUtility.getRecordClassesFound().values();
- for(Class extends Record> resourceRecordsType : resourceRecordsTypes){
+ Collection> resourceRecordsTypes = RecordUtility
+ .getRecordClassesFound().values();
+ for (Class extends Record> resourceRecordsType : resourceRecordsTypes) {
try {
Record record = resourceRecordsType.newInstance();
- resourceRecords.put(resourceRecordsType, record.getRequiredFields());
+ resourceRecords.put(resourceRecordsType,
+ record.getRequiredFields());
} catch (InstantiationException | IllegalAccessException e) {
- logger.error(String.format("Unable to correctly istantiate %s", resourceRecordsType.getSimpleName()), e);
+ logger.error(String.format(
+ "Unable to correctly istantiate %s",
+ resourceRecordsType.getSimpleName()), e);
}
}
-
+
}
return resourceRecords;
}
-
-
+
protected AccountingPersistenceBackendQuery accountingPersistenceQuery;
-
+
/**
* Instantiate the ResourceRecord for the current scope
- * @throws NoAvailableScopeException if there is not possible to query in
- * the current scope
- * @throws NoUsableAccountingPersistenceQueryFound if there is no available
- * instance which can query in that scope
+ *
+ * @throws NoAvailableScopeException
+ * if there is not possible to query in the current scope
+ * @throws NoUsableAccountingPersistenceQueryFound
+ * if there is no available instance which can query in that
+ * scope
*/
- public ResourceRecordQuery() throws NoAvailableScopeException, NoUsableAccountingPersistenceQueryFound {
- this.accountingPersistenceQuery = AccountingPersistenceBackendQueryFactory.getInstance();
+ public ResourceRecordQuery() throws NoAvailableScopeException,
+ NoUsableAccountingPersistenceQueryFound {
+ this.accountingPersistenceQuery = AccountingPersistenceBackendQueryFactory
+ .getInstance();
}
-
- protected static JSONObject getPaddingJSONObject(Map unpaddedResults) throws JSONException{
+
+ protected static JSONObject getPaddingJSONObject(
+ Map unpaddedResults) throws JSONException {
Info auxInfo = new ArrayList(unpaddedResults.values()).get(0);
- JSONObject auxJsonObject = auxInfo.getValue();
+ JSONObject auxJsonObject = auxInfo.getValue();
@SuppressWarnings("unchecked")
Iterator keys = auxJsonObject.keys();
-
+
JSONObject jsonObject = new JSONObject();
- while(keys.hasNext()){
+ while (keys.hasNext()) {
String key = keys.next();
jsonObject.put(key, 0);
}
-
+
return jsonObject;
}
-
+
/**
* Pad the data
- * @param unpaddedData the data to be pad
- * @param temporalConstraint temporalConstraint the temporal interval and
- * the granularity of the data to pad
+ *
+ * @param unpaddedData
+ * the data to be pad
+ * @param temporalConstraint
+ * temporalConstraint the temporal interval and the granularity
+ * of the data to pad
* @return the data padded taking in account the TemporalConstraint
- * @throws Exception if fails
+ * @throws Exception
+ * if fails
*/
- public static List getPaddedResults(Map unpaddedData,
+ public static List getPaddedResults(Map unpaddedData,
TemporalConstraint temporalConstraint) throws Exception {
JSONObject jsonObject = getPaddingJSONObject(unpaddedData);
-
+
List paddedResults = new ArrayList();
List sequence = temporalConstraint.getCalendarSequence();
- for(Calendar progressTime : sequence){
- if(unpaddedData.get(progressTime)!=null){
+ for (Calendar progressTime : sequence) {
+ if (unpaddedData.get(progressTime) != null) {
paddedResults.add(unpaddedData.get(progressTime));
- }else{
+ } else {
Info info = new Info(progressTime, jsonObject);
paddedResults.add(info);
}
}
-
+
return paddedResults;
}
-
+
/**
* Return results with padding if pad is set to true.
- * @param usageRecordType the UsageRecord type to query
- * @param temporalConstraint the temporal interval and the granularity
- * @param filters the list keys to filter (in AND)
- * @param pad indicate is the results have to be padded with zeros when
- * there is no data available at certain data points of sequence
- * @return the requested list of Info
- * @throws Exception if fails
+ *
+ * @param aggregatedRecordClass
+ * the UsageRecord type to query
+ * @param temporalConstraint
+ * the temporal interval and the granularity
+ * @param filters
+ * the list keys to filter (in AND)
+ * @param pad
+ * indicate is the results have to be padded with zeros when
+ * there is no data available at certain data points of sequence
+ * @return the requested list of Info
+ * @throws Exception
+ * if fails
*/
- public List getInfo(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> usageRecordType,
- TemporalConstraint temporalConstraint, List filters, boolean pad) throws Exception {
- Map unpaddedResults = accountingPersistenceQuery.query(usageRecordType, temporalConstraint, filters);
- if(!pad){
+ public List getInfo(
+ Class extends AggregatedRecord,?>> aggregatedRecordClass,
+ TemporalConstraint temporalConstraint, List filters,
+ boolean pad) throws Exception {
+
+ Map unpaddedResults = accountingPersistenceQuery
+ .getTimeSeries(aggregatedRecordClass, temporalConstraint,
+ filters);
+
+ if (!pad) {
return new ArrayList(unpaddedResults.values());
}
+
return getPaddedResults(unpaddedResults, temporalConstraint);
}
-
+
/**
* Return unpadded results
- * @param usageRecordType the UsageRecord type to query
- * @param temporalConstraint the temporal interval and the granularity
- * @param filters the list keys to filter (in AND)
- * @return the requested list of Info
- * @throws Exception if fails
+ *
+ * @param aggregatedRecordClass
+ * the UsageRecord type to query
+ * @param temporalConstraint
+ * the temporal interval and the granularity
+ * @param filters
+ * the list keys to filter (in AND)
+ * @return the requested list of Info
+ * @throws Exception
+ * if fails
*/
- public List getInfo(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> usageRecordType,
- TemporalConstraint temporalConstraint, List filters) throws Exception{
- return getInfo(usageRecordType, temporalConstraint, filters, false);
+ public List getInfo(
+ Class extends AggregatedRecord,?>> aggregatedRecordClass,
+ TemporalConstraint temporalConstraint, List filters)
+ throws Exception {
+
+ return getInfo(aggregatedRecordClass, temporalConstraint, filters,
+ false);
+
}
-
- /**
- * Return the list of key valid for queries a certain usage record type
- * @param usageRecordType the usage record type
- * @return a set containing the list of key
- * @throws Exception if fails
- */
- public List getKeys(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> usageRecordType) throws Exception{
- Set keys = accountingPersistenceQuery.getKeys(usageRecordType);
- List toSort = new ArrayList(keys);
- Collections.sort(toSort);
- return toSort;
- }
-
- public List getPossibleValuesForKey(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> usageRecordType, String key) throws Exception {
- Set keys = accountingPersistenceQuery.getPossibleValuesForKey(usageRecordType, key);
- List toSort = new ArrayList(keys);
- Collections.sort(toSort);
- return toSort;
- }
-
+
}
diff --git a/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceBackendQuery.java b/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceBackendQuery.java
index 8e32ff6..5509b51 100644
--- a/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceBackendQuery.java
+++ b/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceBackendQuery.java
@@ -6,88 +6,98 @@ package org.gcube.accounting.analytics.persistence;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
import org.gcube.accounting.analytics.Filter;
import org.gcube.accounting.analytics.Info;
import org.gcube.accounting.analytics.TemporalConstraint;
import org.gcube.documentstore.records.AggregatedRecord;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
- *
*/
public abstract class AccountingPersistenceBackendQuery {
- private static final Logger logger = LoggerFactory.getLogger(AccountingPersistenceBackendQuery.class);
-
public static final int KEY_VALUES_LIMIT = 25;
protected abstract void prepareConnection(AccountingPersistenceBackendQueryConfiguration configuration) throws Exception;
- protected abstract Map reallyQuery(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> usageRecordType,
- TemporalConstraint temporalConstraint, List filters) throws Exception;
-
/**
* Query the persistence obtaining a Map where the date is the key and
* the #Info is the value. The result is relative to an Usage Record Type,
* respect a TemporalConstraint and can be applied one or more filters.
- * @param recordClass the Usage Record Type of interest
+ * @param aggregatedRecordClass the Record Class of interest
* @param temporalConstraint the TemporalConstraint (interval and aggregation)
- * @param filters the filter for the query. If null or empty string get all
- * data. The filters are evaluated in the order the are presented and are
- * considered in AND
+ * @param filters list of filter to obtain the time series. If null or
+ * empty list get all data for the interested Record Class with the applying
+ * temporal constraint. All Filter must have not null and not empty key and
+ * value.
+ * The filters are must be related to different keys and are in AND.
+ * If the list contains more than one filter with the same key an Exception
+ * is thrown.
* @return the Map containing for each date in the required interval the
* requested data
* @throws Exception if fails
*/
- public Map query(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> recordClass,
- TemporalConstraint temporalConstraint, List filters) throws Exception{
- logger.trace("Request query: RecordClass={}, {}={}, {}s={}", recordClass.newInstance().getRecordType(),
- TemporalConstraint.class.getSimpleName(), temporalConstraint.toString(),
- Filter.class.getSimpleName(), filters);
- return reallyQuery(recordClass, temporalConstraint, filters);
- }
-
- /**
- * Return the list of key valid for queries a certain usage record type
- * @param recordClass the usage record class
- * @return a set containing the list of key
- * @throws Exception if fails
- */
- public abstract Set getKeys(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> recordClass) throws Exception;
+ public abstract SortedMap getTimeSeries(
+ Class extends AggregatedRecord, ?>> aggregatedRecordClass,
+ TemporalConstraint temporalConstraint,
+ List filters) throws Exception;
/**
- * Return the list of possible values for a key for a certain usage record
- * type.
- * The result are limited to {@link #KEY_VALUES_LIMIT} value.
- * If you want a different limit please use the
- * {@link #getPossibleValuesForKey(Class, String, int)} function.
- * Invoking this function has the same effect of invoking
- * {@link #getPossibleValuesForKey(Class, String, int)} function passing
- * {@link #KEY_VALUES_LIMIT} has third argument.
- * @param recordClass the usage record type
- * @param key the key
+ * Return a SortedMap containing the TimeSeries for top values for a
+ * certain key taking in account all Filters. The key is identified
+ * adding a Filter with a null value. Only one Filter with null value is
+ * allowed otherwise an Exception is thrown.
+ * The values are ordered from the most occurred value.
+ * @param aggregatedRecordClass the Usage Record Class of interest
+ * @param temporalConstraint the TemporalConstraint (interval and aggregation)
+ * @param filters list of filter to obtain the time series. If null or
+ * empty list get all data for the interested Record Class with the applying
+ * temporal constraint. All Filter (except one) must have not null and not
+ * empty key and value. One Filter must have not null and not
+ * empty key and a null value.
+ * The filters are must be related to different keys and are in AND.
+ * If the list contains more than one filter with the same key an Exception
+ * is thrown.
+ * If the list contains more than one filter with null value an Exception
+ * is thrown.
* @return a set containing the list of possible values
* @throws Exception if fails
*/
- public abstract Set getPossibleValuesForKey(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> recordClass, String key) throws Exception;
+ public abstract SortedMap> getTopValues(
+ Class extends AggregatedRecord, ?>> recordClass,
+ TemporalConstraint temporalConstraint, List filters)
+ throws Exception;
+
/**
- * Return the list of possible values for a key for a certain usage record
- * type.
- * The result are limited to limit value. When limit is <= 0 this means
- * no limit.
- * @param recordClass the usage record type
- * @param key the key
- * @param limit limit of result to return.
+ * Return the list of possible values for a key for a certain usageRecord
+ * taking in account all Filters. The value for a certain key is identified
+ * adding a Filter with a null value. Only one Filter with null value is
+ * allowed otherwise an Exception is thrown.
+ * The values are ordered from the most occurred value.
+ * @param aggregatedRecordClass the Usage Record Class of interest
+ * @param temporalConstraint the TemporalConstraint (interval and aggregation)
+ * @param filters list of filter to obtain the time series. If null or
+ * empty list get all data for the interested Record Class with the applying
+ * temporal constraint. All Filter (except one) must have not null and not
+ * empty key and value. One Filter must have not null and not
+ * empty key and a null value.
+ * The filters are must be related to different keys and are in AND.
+ * If the list contains more than one filter with the same key an Exception
+ * is thrown.
+ * If the list contains more than one filter with null value an Exception
+ * is thrown.
* @return a set containing the list of possible values
* @throws Exception if fails
*/
- public abstract Set getPossibleValuesForKey(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> recordClass, String key, int limit) throws Exception;
+ public abstract SortedSet getNextPossibleValues(
+ Class extends AggregatedRecord, ?>> aggregatedRecordClass,
+ TemporalConstraint temporalConstraint,
+ List filters) throws Exception;
/**
* Close the connection to persistence
diff --git a/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceQuery.java b/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceQuery.java
index cb64479..eae42bc 100644
--- a/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceQuery.java
+++ b/src/main/java/org/gcube/accounting/analytics/persistence/AccountingPersistenceQuery.java
@@ -6,78 +6,94 @@ package org.gcube.accounting.analytics.persistence;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
import org.gcube.accounting.analytics.Filter;
import org.gcube.accounting.analytics.Info;
import org.gcube.accounting.analytics.TemporalConstraint;
+import org.gcube.accounting.datamodel.UsageRecord;
import org.gcube.documentstore.records.AggregatedRecord;
+import org.gcube.documentstore.records.Record;
/**
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
- *
+ *
*/
public class AccountingPersistenceQuery {
private static final AccountingPersistenceQuery accountingPersistenceQuery;
-
- private AccountingPersistenceQuery(){}
-
+
+ private AccountingPersistenceQuery() {
+ }
+
static {
accountingPersistenceQuery = new AccountingPersistenceQuery();
}
-
- protected static synchronized AccountingPersistenceQuery getInstance(){
+
+ protected static synchronized AccountingPersistenceQuery getInstance() {
return accountingPersistenceQuery;
}
-
-
- /**
- * Query the persistence obtaining a Map where the date is the key and
- * the #Info is the value. The result is relative to an Usage Record Type,
- * respect a TemporalConstraint and can be applied one or more filters.
- * @param recordClass the Usage Record Type of interest
- * @param temporalConstraint the TemporalConstraint (interval and aggregation)
- * @param filters the filter for the query. If null or empty string get all
- * data. The filters are evaluated in the order the are presented and are
- * considered in AND
- * @return the Map containing for each date in the required interval the
- * requested data
- * @throws Exception if fails
- */
- public Map query(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> recordClass,
- TemporalConstraint temporalConstraint, List filters) throws Exception{
- return AccountingPersistenceBackendQueryFactory.getInstance().query(recordClass, temporalConstraint, filters);
+
+ public static SortedSet getQuerableKeys(
+ @SuppressWarnings("rawtypes") AggregatedRecord instance)
+ throws Exception {
+ SortedSet properties = new TreeSet<>(
+ instance.getRequiredFields());
+
+ properties.removeAll(instance.getAggregatedFields());
+ properties.removeAll(instance.getComputedFields());
+ properties.remove(Record.ID);
+ properties.remove(Record.CREATION_TIME);
+ properties.remove(Record.RECORD_TYPE);
+ properties.remove(UsageRecord.SCOPE);
+
+ return properties;
}
-
- /**
- * Return the list of key valid for queries a certain usage record type
- * @param recordClass the usage record type
- * @return a set containing the list of key
- * @throws Exception if fails
- */
- public Set getKeys(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> recordClass) throws Exception {
- return AccountingPersistenceBackendQueryFactory.getInstance().getKeys(recordClass);
+
+ public static SortedSet getQuerableKeys(
+ Class extends AggregatedRecord,?>> aggregatedRecordClass)
+ throws Exception {
+ AggregatedRecord,?> instance = aggregatedRecordClass.newInstance();
+ return getQuerableKeys(instance);
}
-
-
- /**
- * Return the list of possible values for a key for a certain usage record type
- * @param usageRecordType the usage record type
- * @param key the key
- * @return a set containing the list of possible values
- * @throws Exception if fails
- */
- public Set getPossibleValuesForKey(@SuppressWarnings("rawtypes") Class extends AggregatedRecord> usageRecordType, String key) throws Exception {
- return AccountingPersistenceBackendQueryFactory.getInstance().getPossibleValuesForKey(usageRecordType, key);
+
+ public Map getTimeSeries(
+ Class extends AggregatedRecord,?>> aggregatedRecordClass,
+ TemporalConstraint temporalConstraint, List filters)
+ throws Exception {
+ return AccountingPersistenceBackendQueryFactory.getInstance()
+ .getTimeSeries(aggregatedRecordClass, temporalConstraint,
+ filters);
}
-
+
+ public static SortedMap> getTopValues(
+ Class extends AggregatedRecord,?>> aggregatedRecordClass,
+ TemporalConstraint temporalConstraint, List filters)
+ throws Exception {
+ return AccountingPersistenceBackendQueryFactory.getInstance()
+ .getTopValues(aggregatedRecordClass, temporalConstraint,
+ filters);
+ }
+
+ public static SortedSet getNextPossibleValues(
+ Class extends AggregatedRecord,?>> aggregatedRecordClass,
+ TemporalConstraint temporalConstraint, List filters)
+ throws Exception {
+ return AccountingPersistenceBackendQueryFactory.getInstance()
+ .getNextPossibleValues(aggregatedRecordClass,
+ temporalConstraint, filters);
+ }
+
/**
* Close the connection to persistence
- * @throws Exception if the close fails
+ *
+ * @throws Exception
+ * if the close fails
*/
public void close() throws Exception {
AccountingPersistenceBackendQueryFactory.getInstance().close();
}
-
+
}