diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimension.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimension.java
index f95726862f..70d76531b6 100644
--- a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimension.java
+++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimension.java
@@ -140,13 +140,13 @@ public EntityDimension(
/**
* Constructor.
*
+ * @param schema The schema this {@link Column} belongs to.
* @param dimensionField The entity field or relation that this {@link Dimension} represents
* @param annotation Provides static meta data about this {@link Dimension}
* @param fieldType The Java type for this entity field or relation
* @param dimensionType The physical storage structure backing this {@link Dimension}, such as a table or a column
* @param cardinality The estimated cardinality of this {@link Dimension} in SQL table
* @param friendlyName A human-readable name representing this {@link Dimension}
- *
* @throws NullPointerException any argument, except for {@code annotation}, is {@code null}
*/
protected EntityDimension(
diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/AbstractEntityHydrator.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/AbstractEntityHydrator.java
new file mode 100644
index 0000000000..fa9c9ca507
--- /dev/null
+++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/AbstractEntityHydrator.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2019, Yahoo Inc.
+ * Licensed under the Apache License, Version 2.0
+ * See LICENSE file in project root for terms.
+ */
+package com.yahoo.elide.datastores.aggregation.engine;
+
+import com.yahoo.elide.core.EntityDictionary;
+import com.yahoo.elide.datastores.aggregation.Query;
+import com.yahoo.elide.datastores.aggregation.QueryEngine;
+import com.yahoo.elide.datastores.aggregation.dimension.Dimension;
+import com.yahoo.elide.datastores.aggregation.dimension.DimensionType;
+import com.yahoo.elide.datastores.aggregation.metric.Metric;
+
+import com.google.common.base.Preconditions;
+
+import org.apache.commons.lang3.mutable.MutableInt;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * {@link AbstractEntityHydrator} hydrates the entity loaded by {@link QueryEngine#executeQuery(Query)}.
+ *
+ * {@link AbstractEntityHydrator} is not thread-safe and should be accessed by only 1 thread in this application,
+ * because it uses {@link StitchList}. See {@link StitchList} for more details.
+ */
+public abstract class AbstractEntityHydrator {
+
+ @Getter(AccessLevel.PROTECTED)
+ private final EntityDictionary entityDictionary;
+
+ @Getter(AccessLevel.PRIVATE)
+ private final StitchList stitchList;
+
+ @Getter(AccessLevel.PROTECTED)
+ private final List> results = new ArrayList<>();
+
+ @Getter(AccessLevel.PRIVATE)
+ private final Query query;
+
+ /**
+ * Constructor.
+ *
+ * @param results The loaded objects from {@link QueryEngine#executeQuery(Query)}
+ * @param query The query passed to {@link QueryEngine#executeQuery(Query)} to load the objects
+ * @param entityDictionary An object that sets entity instance values and provides entity metadata info
+ */
+ public AbstractEntityHydrator(List results, Query query, EntityDictionary entityDictionary) {
+ this.stitchList = new StitchList(entityDictionary);
+ this.query = query;
+ this.entityDictionary = entityDictionary;
+
+ //Get all the projections from the client query.
+ List projections = this.query.getMetrics().keySet().stream()
+ .map(Metric::getName)
+ .collect(Collectors.toList());
+
+ projections.addAll(this.query.getDimensions().stream()
+ .map(Dimension::getName)
+ .collect(Collectors.toList()));
+
+
+ results.forEach(result -> {
+ Map row = new HashMap<>();
+
+ Object[] resultValues = result instanceof Object[] ? (Object[]) result : new Object[] { result };
+
+ Preconditions.checkArgument(projections.size() == resultValues.length);
+
+ for (int idx = 0; idx < resultValues.length; idx++) {
+ Object value = resultValues[idx];
+ String fieldName = projections.get(idx);
+ row.put(fieldName, value);
+ }
+
+ this.results.add(row);
+ });
+ }
+
+ /**
+ * Loads a map of relationship object ID to relationship object instance.
+ *
+ * Note the relationship cannot be toMany. This method will be invoked for every relationship field of the
+ * requested entity. Its implementation should return the result of the following query
+ *
+ * Given a relationship with type {@code relationshipType} in an entity, loads all relationship
+ * objects whose foreign keys are one of the specified list, {@code joinFieldIds} .
+ *
+ * For example, when the relationship is loaded from SQL and we have the following example identity:
+ *
+ * public class PlayerStats {
+ * private String id;
+ * private Country country;
+ *
+ * @OneToOne
+ * @JoinColumn(name = "country_id")
+ * public Country getCountry() {
+ * return country;
+ * }
+ * }
+ *
+ * In this case {@code relationshipType = Country.class}. If {@code country} is
+ * requested in {@code PlayerStats} query and 3 stats, for example, are found in database whose country ID's are
+ * {@code joinFieldIds = [840, 344, 840]}, then this method should effectively run the following query (JPQL as
+ * example)
+ *
+ * {@code
+ * SELECT e FROM country_table e WHERE country_id IN (840, 344);
+ * }
+ *
+ * and returns the map of [840: Country(id:840), 344: Country(id:344)]
+ *
+ * @param relationshipType The type of relationship
+ * @param joinFieldIds The specified list of join ID's against the relationship
+ *
+ * @return a list of hydrating values
+ */
+ protected abstract Map getRelationshipValues(
+ Class> relationshipType,
+ List joinFieldIds
+ );
+
+ public Iterable hydrate() {
+ //Coerce the results into entity objects.
+ MutableInt counter = new MutableInt(0);
+
+ List queryResults = getResults().stream()
+ .map((result) -> coerceObjectToEntity(result, counter))
+ .collect(Collectors.toList());
+
+ if (getStitchList().shouldStitch()) {
+ // relationship is requested, stitch relationship then
+ populateObjectLookupTable();
+ getStitchList().stitch();
+ }
+
+ return queryResults;
+ }
+
+ /**
+ * Coerces results from a {@link Query} into an Object.
+ *
+ * @param result a fieldName-value map
+ * @param counter Monotonically increasing number to generate IDs.
+ * @return A hydrated entity object.
+ */
+ protected Object coerceObjectToEntity(Map result, MutableInt counter) {
+ Class> entityClass = query.getSchema().getEntityClass();
+
+ //Construct the object.
+ Object entityInstance;
+ try {
+ entityInstance = entityClass.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+
+ result.forEach((fieldName, value) -> {
+ Dimension dim = query.getSchema().getDimension(fieldName);
+
+ if (dim != null && dim.getDimensionType() == DimensionType.ENTITY) {
+ getStitchList().todo(entityInstance, fieldName, value); // We don't hydrate relationships here.
+ } else {
+ getEntityDictionary().setValue(entityInstance, fieldName, value);
+ }
+ });
+
+ //Set the ID (it must be coerced from an integer)
+ getEntityDictionary().setValue(
+ entityInstance,
+ getEntityDictionary().getIdFieldName(entityClass),
+ counter.getAndIncrement()
+ );
+
+ return entityInstance;
+ }
+
+ /**
+ * Foe each requested relationship, run a single query to load all relationship objects whose ID's are involved in
+ * the request.
+ */
+ private void populateObjectLookupTable() {
+ // mapping: relationship field name -> join ID's
+ Map> hydrationIdsByRelationship = getStitchList().getHydrationMapping();
+
+ // hydrate each relationship
+ for (Map.Entry> entry : hydrationIdsByRelationship.entrySet()) {
+ String joinField = entry.getKey();
+ List joinFieldIds = entry.getValue();
+ Class> relationshipType = getEntityDictionary().getParameterizedType(
+ getQuery().getSchema().getEntityClass(),
+ joinField);
+
+ getStitchList().populateLookup(relationshipType, getRelationshipValues(relationshipType, joinFieldIds));
+ }
+ }
+}
diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/SQLEntityHydrator.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/SQLEntityHydrator.java
new file mode 100644
index 0000000000..257c7c40a0
--- /dev/null
+++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/SQLEntityHydrator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019, Yahoo Inc.
+ * Licensed under the Apache License, Version 2.0
+ * See LICENSE file in project root for terms.
+ */
+package com.yahoo.elide.datastores.aggregation.engine;
+
+import com.yahoo.elide.core.EntityDictionary;
+import com.yahoo.elide.datastores.aggregation.Query;
+import lombok.AccessLevel;
+import lombok.Getter;
+
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.persistence.EntityManager;
+
+/**
+ * {@link SQLEntityHydrator} hydrates the entity loaded by {@link SQLQueryEngine#executeQuery(Query)}.
+ */
+public class SQLEntityHydrator extends AbstractEntityHydrator {
+
+ @Getter(AccessLevel.PRIVATE)
+ private final EntityManager entityManager;
+
+ /**
+ * Constructor.
+ *
+ * @param results The loaded objects from {@link SQLQueryEngine#executeQuery(Query)}
+ * @param query The query passed to {@link SQLQueryEngine#executeQuery(Query)} to load the objects
+ * @param entityDictionary An object that sets entity instance values and provides entity metadata info
+ * @param entityManager An service that issues JPQL queries to load relationship objects
+ */
+ public SQLEntityHydrator(
+ List results,
+ Query query,
+ EntityDictionary entityDictionary,
+ EntityManager entityManager
+ ) {
+ super(results, query, entityDictionary);
+ this.entityManager = entityManager;
+ }
+
+ @Override
+ protected Map getRelationshipValues(
+ Class> relationshipType,
+ List joinFieldIds
+ ) {
+ if (joinFieldIds.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ List uniqueIds = joinFieldIds.stream().distinct().collect(Collectors.toCollection(LinkedList::new));
+
+ List loaded = getEntityManager()
+ .createQuery(
+ String.format(
+ "SELECT e FROM %s e WHERE %s IN (:idList)",
+ relationshipType.getCanonicalName(),
+ getEntityDictionary().getIdFieldName(relationshipType)
+ )
+ )
+ .setParameter("idList", uniqueIds)
+ .getResultList();
+
+ return loaded.stream()
+ .map(obj -> new AbstractMap.SimpleImmutableEntry<>((Object) getEntityDictionary().getId(obj), obj))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+}
diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngine.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngine.java
index 9b01f2af39..170da7a723 100644
--- a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngine.java
+++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngine.java
@@ -19,7 +19,6 @@
import com.yahoo.elide.datastores.aggregation.Query;
import com.yahoo.elide.datastores.aggregation.QueryEngine;
import com.yahoo.elide.datastores.aggregation.dimension.Dimension;
-import com.yahoo.elide.datastores.aggregation.dimension.DimensionType;
import com.yahoo.elide.datastores.aggregation.engine.annotation.FromSubquery;
import com.yahoo.elide.datastores.aggregation.engine.annotation.FromTable;
import com.yahoo.elide.datastores.aggregation.engine.schema.SQLSchema;
@@ -29,7 +28,7 @@
import com.yahoo.elide.utils.coerce.CoerceUtil;
import com.google.common.base.Preconditions;
-import org.apache.commons.lang3.mutable.MutableInt;
+
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@@ -39,7 +38,6 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -69,7 +67,7 @@ public SQLQueryEngine(EntityManager entityManager, EntityDictionary dictionary)
.stream()
.filter((clazz) ->
dictionary.getAnnotation(clazz, FromTable.class) != null
- || dictionary.getAnnotation(clazz, FromSubquery.class) != null
+ || dictionary.getAnnotation(clazz, FromSubquery.class) != null
)
.collect(Collectors.toMap(
Function.identity(),
@@ -104,9 +102,10 @@ public Iterable executeQuery(Query query) {
supplyFilterQueryParameters(query, pageTotalQuery);
//Run the Pagination query and log the time spent.
- long total = new TimedFunction<>(() -> {
- return CoerceUtil.coerce(pageTotalQuery.getSingleResult(), Long.class);
- }, "Running Query: " + paginationSQL).get();
+ long total = new TimedFunction<>(
+ () -> CoerceUtil.coerce(pageTotalQuery.getSingleResult(), Long.class),
+ "Running Query: " + paginationSQL
+ ).get();
pagination.setPageTotals(total);
}
@@ -116,17 +115,9 @@ public Iterable executeQuery(Query query) {
supplyFilterQueryParameters(query, jpaQuery);
//Run the primary query and log the time spent.
- List results = new TimedFunction<>(() -> {
- return jpaQuery.getResultList();
- }, "Running Query: " + sql).get();
-
+ List results = new TimedFunction<>(() -> jpaQuery.getResultList(), "Running Query: " + sql).get();
- //Coerce the results into entity objects.
- MutableInt counter = new MutableInt(0);
- return results.stream()
- .map((result) -> { return result instanceof Object[] ? (Object []) result : new Object[] { result }; })
- .map((result) -> coerceObjectToEntity(query, result, counter))
- .collect(Collectors.toList());
+ return new SQLEntityHydrator(results, query, dictionary, entityManager).hydrate();
}
/**
@@ -149,16 +140,16 @@ protected SQLQuery toSQL(Query query) {
if (query.getWhereFilter() != null) {
joinPredicates.addAll(extractPathElements(query.getWhereFilter()));
- builder.whereClause("WHERE " + translateFilterExpression(schema, query.getWhereFilter(),
+ builder.whereClause("WHERE " + translateFilterExpression(query.getWhereFilter(),
this::generateWhereClauseColumnReference));
}
if (query.getHavingFilter() != null) {
- builder.havingClause("HAVING " + translateFilterExpression(schema, query.getHavingFilter(),
+ builder.havingClause("HAVING " + translateFilterExpression(query.getHavingFilter(),
(predicate) -> { return generateHavingClauseColumnReference(predicate, query); }));
}
- if (!query.getDimensions().isEmpty()) {
+ if (! query.getDimensions().isEmpty()) {
builder.groupByClause(extractGroupBy(query));
}
@@ -181,67 +172,13 @@ protected SQLQuery toSQL(Query query) {
return builder.build();
}
- /**
- * Coerces results from a JPA query into an Object.
- * @param query The client query
- * @param result A row from the results.
- * @param counter Monotonically increasing number to generate IDs.
- * @return A hydrated entity object.
- */
- protected Object coerceObjectToEntity(Query query, Object[] result, MutableInt counter) {
- Class> entityClass = query.getSchema().getEntityClass();
-
- //Get all the projections from the client query.
- List projections = query.getMetrics().entrySet().stream()
- .map(Map.Entry::getKey)
- .map(Metric::getName)
- .collect(Collectors.toList());
-
- projections.addAll(query.getDimensions().stream()
- .map(Dimension::getName)
- .collect(Collectors.toList()));
-
- Preconditions.checkArgument(result.length == projections.size());
-
- SQLSchema schema = (SQLSchema) query.getSchema();
-
- //Construct the object.
- Object entityInstance;
- try {
- entityInstance = entityClass.newInstance();
- } catch (InstantiationException | IllegalAccessException e) {
- throw new IllegalStateException(e);
- }
-
- //Populate all of the fields.
- for (int idx = 0; idx < result.length; idx++) {
- Object value = result[idx];
- String fieldName = projections.get(idx);
-
- Dimension dim = schema.getDimension(fieldName);
- if (dim != null && dim.getDimensionType() == DimensionType.ENTITY) {
- //We don't hydrate relationships here.
- continue;
- }
-
- dictionary.setValue(entityInstance, fieldName, value);
- }
-
- //Set the ID (it must be coerced from an integer)
- dictionary.setValue(entityInstance, dictionary.getIdFieldName(entityClass), counter.getAndIncrement());
-
- return entityInstance;
- }
-
/**
* Translates a filter expression into SQL.
- * @param schema The schema being queried.
* @param expression The filter expression
* @param columnGenerator A function which generates a column reference in SQL from a FilterPredicate.
* @return A SQL expression
*/
- private String translateFilterExpression(SQLSchema schema,
- FilterExpression expression,
+ private String translateFilterExpression(FilterExpression expression,
Function columnGenerator) {
FilterTranslator filterVisitor = new FilterTranslator();
@@ -397,11 +334,11 @@ private SQLQuery toPageTotalSQL(SQLQuery sql) {
Query clientQuery = sql.getClientQuery();
String groupByDimensions = clientQuery.getDimensions().stream()
- .map(Dimension::getName)
- .map((name) -> getColumnName(clientQuery.getSchema().getEntityClass(), name))
- .collect(Collectors.joining(","));
+ .map(Dimension::getName)
+ .map((name) -> getColumnName(clientQuery.getSchema().getEntityClass(), name))
+ .collect(Collectors.joining(","));
- String projectionClause = String.format("COUNT(DISTINCT(%s))", groupByDimensions);
+ String projectionClause = String.format("SELECT COUNT(DISTINCT(%s))", groupByDimensions);
return SQLQuery.builder()
.clientQuery(sql.getClientQuery())
@@ -422,7 +359,7 @@ private String extractProjection(Query query) {
.map((entry) -> {
Metric metric = entry.getKey();
Class extends Aggregation> agg = entry.getValue();
- return metric.getMetricExpression(Optional.of(agg)) + " AS " + metric.getName();
+ return metric.getMetricExpression(agg) + " AS " + metric.getName();
})
.collect(Collectors.toList());
@@ -455,9 +392,8 @@ private String extractGroupBy(Query query) {
.collect(Collectors.toList());
return "GROUP BY " + dimensionProjections.stream()
- .map((name) -> query.getSchema().getAlias() + "." + name)
- .collect(Collectors.joining(","));
-
+ .map((name) -> query.getSchema().getAlias() + "." + name)
+ .collect(Collectors.joining(","));
}
/**
@@ -481,7 +417,7 @@ private String generateHavingClauseColumnReference(FilterPredicate predicate, Qu
Path.PathElement last = predicate.getPath().lastElement().get();
Class> lastClass = last.getType();
- if (!lastClass.equals(query.getSchema().getEntityClass())) {
+ if (! lastClass.equals(query.getSchema().getEntityClass())) {
throw new InvalidPredicateException("The having clause can only reference fact table aggregations.");
}
@@ -489,6 +425,6 @@ private String generateHavingClauseColumnReference(FilterPredicate predicate, Qu
Metric metric = schema.getMetric(last.getFieldName());
Class extends Aggregation> agg = query.getMetrics().get(metric);
- return metric.getMetricExpression(Optional.of(agg));
+ return metric.getMetricExpression(agg);
}
}
diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/StitchList.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/StitchList.java
new file mode 100644
index 0000000000..9d8c18dd2c
--- /dev/null
+++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/engine/StitchList.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2019, Yahoo Inc.
+ * Licensed under the Apache License, Version 2.0
+ * See LICENSE file in project root for terms.
+ */
+package com.yahoo.elide.datastores.aggregation.engine;
+
+import com.yahoo.elide.core.EntityDictionary;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * An auxiliary class for {@link AbstractEntityHydrator} and is responsible for setting relationship values of an entity
+ * instance.
+ *
+ * {@link StitchList} should not be subclassed.
+ */
+public final class StitchList {
+ /**
+ * Maps an relationship entity class to a map of object ID to object instance.
+ *
+ * For example, [Country.class: [340: Country(id:340), 100: Country(id:100)]]
+ */
+ @Getter(AccessLevel.PRIVATE)
+ private final Map, Map> objectLookups;
+
+ /**
+ * List of relationships to hydrate
+ */
+ @Getter(AccessLevel.PRIVATE)
+ private final List todoList;
+
+ @Getter(AccessLevel.PRIVATE)
+ private final EntityDictionary entityDictionary;
+
+ /**
+ * A representation of an TODO item in a {@link StitchList}.
+ */
+ @Data
+ public static class Todo {
+ private final Object entityInstance;
+ private final String relationshipName;
+ private final Object foreignKey;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param entityDictionary An object that sets entity instance values and provides entity metadata info
+ */
+ public StitchList(EntityDictionary entityDictionary) {
+ this.objectLookups = new HashMap<>();
+ this.todoList = new ArrayList<>();
+ this.entityDictionary = entityDictionary;
+ }
+
+ /**
+ * Returns whether or not the entity instances in this {@link StitchList} have relationships that are unset.
+ *
+ * @return {@code true} if the entity instances in this {@link StitchList} should be further hydrated because they
+ * have one or more relationship fields.
+ */
+ public boolean shouldStitch() {
+ return !getTodoList().isEmpty();
+ }
+
+ /**
+ * Enqueues an entity instance which will be further hydrated on one of its relationship fields later
+ *
+ * @param entityInstance The entity instance to be hydrated
+ * @param fieldName The relationship field to hydrate in the entity instance
+ * @param value The foreign key between the entity instance and the field entity.
+ */
+ public void todo(Object entityInstance, String fieldName, Object value) {
+ getTodoList().add(new Todo(entityInstance, fieldName, value));
+ }
+
+ /**
+ * Sets all the relationship values of an requested entity.
+ *
+ * Values associated with the existing key will be overwritten.
+ *
+ * @param relationshipType The type of the relationship to set
+ * @param idToInstance A map from relationship ID to the actual relationship instance with that ID
+ */
+ public void populateLookup(Class> relationshipType, Map idToInstance) {
+ if (getObjectLookups().containsKey(relationshipType)) {
+ getObjectLookups().get(relationshipType).putAll(idToInstance);
+ } else {
+ getObjectLookups().put(relationshipType, idToInstance);
+ }
+ }
+
+ /**
+ * Stitch all entity instances currently in this {@link StitchList} by setting their relationship fields whose
+ * values are determined by relationship ID's.
+ */
+ public void stitch() {
+ for (Todo todo : getTodoList()) {
+ Object entityInstance = todo.getEntityInstance();
+ String relationshipName = todo.getRelationshipName();
+ Object foreignKey = todo.getForeignKey();
+
+ Class> relationshipType = getEntityDictionary().getParameterizedType(entityInstance, relationshipName);
+ Object relationshipValue = getObjectLookups().get(relationshipType).get(foreignKey);
+
+ getEntityDictionary().setValue(entityInstance, relationshipName, relationshipValue);
+ }
+ }
+
+ /**
+ * Returns a mapping from relationship name to an immutable list of foreign key ID objects.
+ *
+ * For example, given the following {@code todoList}:
+ *
+ * {@code
+ * [PlayerStats, country, 344]
+ * [PlayerStats, country, 840]
+ * [PlayerStats, country, 344]
+ * [PlayerStats, player, 1]
+ * [PlayerStats, player, 1]
+ * [PlayerStats, player, 1]
+ * }
+ *
+ * this method returns a map of the following:
+ *
+ * [
+ * "country": [344, 840]
+ * "player": [1]
+ * ]
+ *
+ *
+ * @return a mapping from relationship name to an ordered list of relationship join ID's
+ */
+ public Map> getHydrationMapping() {
+ return getTodoList().stream()
+ .collect(
+ Collectors.groupingBy(
+ Todo::getRelationshipName,
+ Collectors.mapping(
+ Todo::getForeignKey,
+ Collectors.collectingAndThen(
+ Collectors.toCollection(LinkedList::new),
+ Collections::unmodifiableList
+ )
+ )
+ )
+ );
+ }
+}
diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetric.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetric.java
index f8149c1208..20c3ebea3a 100644
--- a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetric.java
+++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetric.java
@@ -62,20 +62,16 @@ public AggregatedMetric(
}
@Override
- public String getMetricExpression(final Optional> aggregation) {
- if (!aggregation.isPresent()) {
- return "";
- }
-
+ public String getMetricExpression(final Class extends Aggregation> aggregation) {
try {
- Class> clazz = Class.forName(aggregation.get().getCanonicalName());
+ Class> clazz = Class.forName(aggregation.getCanonicalName());
Constructor> ctor = clazz.getConstructor();
Aggregation instance = (Aggregation) ctor.newInstance();
return String.format(instance.getAggFunctionFormat(), schema.getAlias() + "." + name);
} catch (Exception exception) {
String message = String.format(
"Cannot generate aggregation function for '%s'",
- aggregation.get().getCanonicalName()
+ aggregation.getCanonicalName()
);
log.error(message, exception);
throw new IllegalStateException(message, exception);
diff --git a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/Metric.java b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/Metric.java
index 049df39e0f..d7152adefc 100644
--- a/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/Metric.java
+++ b/elide-datastore/elide-datastore-aggregation/src/main/java/com/yahoo/elide/datastores/aggregation/metric/Metric.java
@@ -11,7 +11,6 @@
import java.io.Serializable;
import java.util.List;
-import java.util.Optional;
/**
* Elide's definition of metric.
@@ -52,9 +51,10 @@ public interface Metric extends Serializable {
/**
* Returns a metric expression that represents a specified aggregation.
*
+ * @param aggregation aggregation type to be applied
* @return a arithmetic formula for computing this {@link Metric} or default aggregation UDF on a base/simple metric
*/
- String getMetricExpression(Optional> aggregation);
+ String getMetricExpression(Class extends Aggregation> aggregation);
/**
* Returns a list of supported aggregations with the first as the default aggregation.
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/SchemaTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/SchemaTest.java
index 9b7e7a4fbc..089564b0f8 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/SchemaTest.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/SchemaTest.java
@@ -5,6 +5,10 @@
*/
package com.yahoo.elide.datastores.aggregation;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import com.yahoo.elide.core.EntityDictionary;
import com.yahoo.elide.datastores.aggregation.annotation.CardinalitySize;
import com.yahoo.elide.datastores.aggregation.example.Country;
@@ -13,21 +17,18 @@
import com.yahoo.elide.datastores.aggregation.example.VideoGame;
import com.yahoo.elide.datastores.aggregation.metric.Max;
import com.yahoo.elide.datastores.aggregation.schema.Schema;
-
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
import java.util.Collections;
-import java.util.Optional;
public class SchemaTest {
- private EntityDictionary entityDictionary;
- private Schema playerStatsSchema;
+ private static EntityDictionary entityDictionary;
+ private static Schema playerStatsSchema;
- @BeforeMethod
- public void setupEntityDictionary() {
+ @BeforeAll
+ public static void setupEntityDictionary() {
entityDictionary = new EntityDictionary(Collections.emptyMap());
entityDictionary.bindEntity(Country.class);
entityDictionary.bindEntity(VideoGame.class);
@@ -37,21 +38,22 @@ public void setupEntityDictionary() {
playerStatsSchema = new Schema(PlayerStats.class, entityDictionary);
}
- @Test void testMetricCheck() {
- Assert.assertTrue(playerStatsSchema.isMetricField("highScore"));
- Assert.assertFalse(playerStatsSchema.isMetricField("country"));
+ @Test
+ public void testMetricCheck() {
+ assertTrue(playerStatsSchema.isMetricField("highScore"));
+ assertFalse(playerStatsSchema.isMetricField("country"));
}
@Test
public void testGetDimension() {
- Assert.assertEquals(playerStatsSchema.getDimension("country").getCardinality(), CardinalitySize.SMALL);
+ assertEquals(CardinalitySize.SMALL, playerStatsSchema.getDimension("country").getCardinality());
}
@Test
public void testGetMetric() {
- Assert.assertEquals(
- playerStatsSchema.getMetric("highScore").getMetricExpression(Optional.of(Max.class)),
- "MAX(com_yahoo_elide_datastores_aggregation_example_PlayerStats.highScore)"
+ assertEquals(
+ "MAX(com_yahoo_elide_datastores_aggregation_example_PlayerStats.highScore)",
+ playerStatsSchema.getMetric("highScore").getMetricExpression(Max.class)
);
}
}
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/DimensionTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/DimensionTest.java
index bbafa9db59..46e187c18f 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/DimensionTest.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/DimensionTest.java
@@ -5,15 +5,15 @@
*/
package com.yahoo.elide.datastores.aggregation.dimension;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.mockito.Mockito.mock;
import com.yahoo.elide.datastores.aggregation.annotation.CardinalitySize;
import com.yahoo.elide.datastores.aggregation.example.Country;
import com.yahoo.elide.datastores.aggregation.schema.Schema;
import com.yahoo.elide.datastores.aggregation.time.TimeGrain;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
+import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.Set;
@@ -55,16 +55,16 @@ public class DimensionTest {
@Test
public void testDimensionAsCollectionElement() {
- Assert.assertEquals(ENTITY_DIMENSION, ENTITY_DIMENSION);
- Assert.assertEquals(DEGENERATE_DIMENSION, DEGENERATE_DIMENSION);
- Assert.assertNotEquals(ENTITY_DIMENSION, DEGENERATE_DIMENSION);
- Assert.assertNotEquals(ENTITY_DIMENSION.hashCode(), DEGENERATE_DIMENSION.hashCode());
+ assertEquals(ENTITY_DIMENSION, ENTITY_DIMENSION);
+ assertEquals(DEGENERATE_DIMENSION, DEGENERATE_DIMENSION);
+ assertNotEquals(DEGENERATE_DIMENSION, ENTITY_DIMENSION);
+ assertNotEquals(DEGENERATE_DIMENSION.hashCode(), ENTITY_DIMENSION.hashCode());
// different dimensions should be separate elements in Set
Set dimensions = new HashSet<>();
dimensions.add(ENTITY_DIMENSION);
- Assert.assertEquals(dimensions.size(), 1);
+ assertEquals(1, dimensions.size());
// a separate same object doesn't increase collection size
Dimension sameEntityDimension = new EntityDimension(
@@ -75,35 +75,35 @@ public void testDimensionAsCollectionElement() {
CardinalitySize.SMALL,
"name"
);
- Assert.assertEquals(sameEntityDimension, ENTITY_DIMENSION);
+ assertEquals(ENTITY_DIMENSION, sameEntityDimension);
dimensions.add(sameEntityDimension);
- Assert.assertEquals(dimensions.size(), 1);
+ assertEquals(1, dimensions.size());
dimensions.add(ENTITY_DIMENSION);
- Assert.assertEquals(dimensions.size(), 1);
+ assertEquals(1, dimensions.size());
dimensions.add(DEGENERATE_DIMENSION);
- Assert.assertEquals(dimensions.size(), 2);
+ assertEquals(2, dimensions.size());
dimensions.add(TIME_DIMENSION);
- Assert.assertEquals(dimensions.size(), 3);
+ assertEquals(3, dimensions.size());
}
@Test
public void testToString() {
// table dimension
- Assert.assertEquals(
+ assertEquals(
ENTITY_DIMENSION.toString(),
"EntityDimension[name='country', longName='country', description='country', dimensionType=ENTITY, dataType=Country, cardinality=SMALL, friendlyName='name']"
);
// degenerate dimension
- Assert.assertEquals(
+ assertEquals(
DEGENERATE_DIMENSION.toString(),
"DegenerateDimension[columnType=FIELD, name='overallRating', longName='overallRating', description='overallRating', dimensionType=DEGENERATE, dataType=String, cardinality=SMALL, friendlyName='overallRating']"
);
- Assert.assertEquals(
+ assertEquals(
TIME_DIMENSION.toString(),
"TimeDimension[timeZone=Pacific Standard Time, timeGrain=DAY, columnType=TEMPORAL, name='recordedTime', longName='recordedTime', description='recordedTime', dimensionType=DEGENERATE, dataType=class java.lang.Long, cardinality=LARGE, friendlyName='recordedTime']"
);
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimensionTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimensionTest.java
index 64b9856862..e3e5c17f37 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimensionTest.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/dimension/EntityDimensionTest.java
@@ -5,6 +5,9 @@
*/
package com.yahoo.elide.datastores.aggregation.dimension;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
import com.yahoo.elide.annotation.Include;
import com.yahoo.elide.core.EntityDictionary;
import com.yahoo.elide.datastores.aggregation.annotation.CardinalitySize;
@@ -12,16 +15,15 @@
import com.yahoo.elide.datastores.aggregation.example.Country;
import com.yahoo.elide.datastores.aggregation.example.PlayerStats;
import com.yahoo.elide.datastores.aggregation.example.VideoGame;
-
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
import java.util.Collections;
import javax.persistence.Entity;
public class EntityDimensionTest {
+ private static EntityDictionary entityDictionary;
/**
* A class for testing un-happy path on finding friendly name.
@@ -52,10 +54,8 @@ public String getSubTitle() {
}
}
- private EntityDictionary entityDictionary;
-
- @BeforeMethod
- public void setupEntityDictionary() {
+ @BeforeAll
+ public static void setupEntityDictionary() {
entityDictionary = new EntityDictionary(Collections.emptyMap());
entityDictionary.bindEntity(PlayerStats.class);
entityDictionary.bindEntity(Country.class);
@@ -66,44 +66,46 @@ public void setupEntityDictionary() {
@Test
public void testHappyPathFriendlyNameScan() {
// 1 field with @FriendlyName
- Assert.assertEquals(
- EntityDimension.getFriendlyNameField(PlayerStats.class, entityDictionary),
- "overallRating"
+ assertEquals(
+ "overallRating",
+ EntityDimension.getFriendlyNameField(PlayerStats.class, entityDictionary)
);
// no field with @FriendlyName
- Assert.assertEquals(
- EntityDimension.getFriendlyNameField(VideoGame.class, entityDictionary),
- "id"
+ assertEquals(
+ "id",
+ EntityDimension.getFriendlyNameField(VideoGame.class, entityDictionary)
);
}
/**
* Multiple {@link FriendlyName} annotations in entity is illegal.
*/
- @Test(expectedExceptions = IllegalStateException.class)
+ @Test
public void testUnhappyPathFriendlyNameScan() {
- EntityDimension.getFriendlyNameField(Book.class, entityDictionary);
+ assertThrows(
+ IllegalStateException.class,
+ () -> EntityDimension.getFriendlyNameField(Book.class, entityDictionary));
}
@Test
public void testCardinalityScan() {
// annotation on entity
- Assert.assertEquals(
- EntityDimension.getEstimatedCardinality("country", PlayerStats.class, entityDictionary),
- CardinalitySize.SMALL
+ assertEquals(
+ CardinalitySize.SMALL,
+ EntityDimension.getEstimatedCardinality("country", PlayerStats.class, entityDictionary)
);
// annotation on field
- Assert.assertEquals(
- EntityDimension.getEstimatedCardinality("overallRating", PlayerStats.class, entityDictionary),
- CardinalitySize.MEDIUM
+ assertEquals(
+ CardinalitySize.MEDIUM,
+ EntityDimension.getEstimatedCardinality("overallRating", PlayerStats.class, entityDictionary)
);
// default is used
- Assert.assertEquals(
- EntityDimension.getEstimatedCardinality("recordedDate", PlayerStats.class, entityDictionary),
- EntityDimension.getDefaultCardinality()
+ assertEquals(
+ EntityDimension.getDefaultCardinality(),
+ EntityDimension.getEstimatedCardinality("recordedDate", PlayerStats.class, entityDictionary)
);
}
}
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngineTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngineTest.java
index bc69f7a953..bc930b3a75 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngineTest.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/engine/SQLQueryEngineTest.java
@@ -6,6 +6,9 @@
package com.yahoo.elide.datastores.aggregation.engine;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
import com.yahoo.elide.core.EntityDictionary;
import com.yahoo.elide.core.filter.dialect.RSQLFilterDialect;
import com.yahoo.elide.core.pagination.Pagination;
@@ -20,8 +23,8 @@
import com.yahoo.elide.datastores.aggregation.example.PlayerStatsView;
import com.yahoo.elide.datastores.aggregation.metric.Sum;
import com.yahoo.elide.datastores.aggregation.schema.Schema;
-import org.testng.Assert;
-import org.testng.annotations.Test;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
import java.sql.Timestamp;
import java.util.HashMap;
@@ -35,15 +38,14 @@
import javax.persistence.Persistence;
public class SQLQueryEngineTest {
-
- private EntityManagerFactory emf;
-
- private Schema playerStatsSchema;
- private Schema playerStatsViewSchema;
- private EntityDictionary dictionary;
- private RSQLFilterDialect filterParser;
-
- public SQLQueryEngineTest() {
+ private static EntityManagerFactory emf;
+ private static Schema playerStatsSchema;
+ private static Schema playerStatsViewSchema;
+ private static EntityDictionary dictionary;
+ private static RSQLFilterDialect filterParser;
+
+ @BeforeAll
+ public static void init() {
emf = Persistence.createEntityManagerFactory("aggregationStore");
dictionary = new EntityDictionary(new HashMap<>());
dictionary.bindEntity(PlayerStats.class);
@@ -56,8 +58,11 @@ public SQLQueryEngineTest() {
playerStatsViewSchema = new SQLSchema(PlayerStatsView.class, dictionary);
}
+ /**
+ * Test loading all three records from the table.
+ */
@Test
- public void testFullTableLoad() throws Exception {
+ public void testFullTableLoad() {
EntityManager em = emf.createEntityManager();
QueryEngine engine = new SQLQueryEngine(em, dictionary);
@@ -65,33 +70,41 @@ public void testFullTableLoad() throws Exception {
.schema(playerStatsSchema)
.metric(playerStatsSchema.getMetric("lowScore"), Sum.class)
.metric(playerStatsSchema.getMetric("highScore"), Sum.class)
- .groupDimension(playerStatsSchema.getDimension("overallRating"))
.timeDimension((TimeDimension) playerStatsSchema.getDimension("recordedDate"))
.build();
List results = StreamSupport.stream(engine.executeQuery(query).spliterator(), false)
.collect(Collectors.toList());
- //Jon Doe,1234,72,Good,840,2019-07-12 00:00:00
+ PlayerStats stats0 = new PlayerStats();
+ stats0.setId("0");
+ stats0.setLowScore(241);
+ stats0.setHighScore(2412);
+ stats0.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
+
PlayerStats stats1 = new PlayerStats();
- stats1.setId("0");
+ stats1.setId("1");
stats1.setLowScore(72);
stats1.setHighScore(1234);
- stats1.setOverallRating("Good");
stats1.setRecordedDate(Timestamp.valueOf("2019-07-12 00:00:00"));
PlayerStats stats2 = new PlayerStats();
- stats2.setId("1");
- stats2.setLowScore(241);
- stats2.setHighScore(2412);
- stats2.setOverallRating("Great");
- stats2.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
+ stats2.setId("2");
+ stats2.setLowScore(72);
+ stats2.setHighScore(1000);
+ stats2.setRecordedDate(Timestamp.valueOf("2019-07-13 00:00:00"));
- Assert.assertEquals(results.size(), 2);
- Assert.assertEquals(results.get(0), stats1);
- Assert.assertEquals(results.get(1), stats2);
+ assertEquals(3, results.size());
+ assertEquals(stats0, results.get(0));
+ assertEquals(stats1, results.get(1));
+ assertEquals(stats2, results.get(2));
}
+ /**
+ * Test group by a degenerate dimension with a filter applied.
+ *
+ * @throws Exception exception
+ */
@Test
public void testDegenerateDimensionFilter() throws Exception {
EntityManager em = emf.createEntityManager();
@@ -117,10 +130,15 @@ public void testDegenerateDimensionFilter() throws Exception {
stats1.setOverallRating("Great");
stats1.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
- Assert.assertEquals(results.size(), 1);
- Assert.assertEquals(results.get(0), stats1);
+ assertEquals(1, results.size());
+ assertEquals(stats1, results.get(0));
}
+ /**
+ * Test filtering on a dimension attribute.
+ *
+ * @throws Exception exception
+ */
@Test
public void testFilterJoin() throws Exception {
EntityManager em = emf.createEntityManager();
@@ -131,6 +149,7 @@ public void testFilterJoin() throws Exception {
.metric(playerStatsSchema.getMetric("lowScore"), Sum.class)
.metric(playerStatsSchema.getMetric("highScore"), Sum.class)
.groupDimension(playerStatsSchema.getDimension("overallRating"))
+ .groupDimension(playerStatsSchema.getDimension("country"))
.timeDimension((TimeDimension) playerStatsSchema.getDimension("recordedDate"))
.whereFilter(filterParser.parseFilterExpression("country.name=='United States'",
PlayerStats.class, false))
@@ -139,25 +158,42 @@ public void testFilterJoin() throws Exception {
List results = StreamSupport.stream(engine.executeQuery(query).spliterator(), false)
.collect(Collectors.toList());
- PlayerStats stats1 = new PlayerStats();
- stats1.setId("0");
- stats1.setLowScore(72);
- stats1.setHighScore(1234);
- stats1.setOverallRating("Good");
- stats1.setRecordedDate(Timestamp.valueOf("2019-07-12 00:00:00"));
-
- PlayerStats stats2 = new PlayerStats();
- stats2.setId("1");
- stats2.setLowScore(241);
- stats2.setHighScore(2412);
- stats2.setOverallRating("Great");
- stats2.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
-
- Assert.assertEquals(results.size(), 2);
- Assert.assertEquals(results.get(0), stats1);
- Assert.assertEquals(results.get(1), stats2);
+ Country expectedCountry = new Country();
+ expectedCountry.setId("840");
+ expectedCountry.setIsoCode("USA");
+ expectedCountry.setName("United States");
+
+
+ PlayerStats usa0 = new PlayerStats();
+ usa0.setId("0");
+ usa0.setLowScore(241);
+ usa0.setHighScore(2412);
+ usa0.setOverallRating("Great");
+ usa0.setCountry(expectedCountry);
+ usa0.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
+
+ PlayerStats usa1 = new PlayerStats();
+ usa1.setId("1");
+ usa1.setLowScore(72);
+ usa1.setHighScore(1234);
+ usa1.setOverallRating("Good");
+ usa1.setCountry(expectedCountry);
+ usa1.setRecordedDate(Timestamp.valueOf("2019-07-12 00:00:00"));
+
+ assertEquals(2, results.size());
+ assertEquals(usa0, results.get(0));
+ assertEquals(usa1, results.get(1));
+
+ // test join
+ PlayerStats actualStats1 = (PlayerStats) results.get(0);
+ assertNotNull(actualStats1.getCountry());
}
+ /**
+ * Test filtering on an attribute that's not present in the query.
+ *
+ * @throws Exception exception
+ */
@Test
public void testSubqueryFilterJoin() throws Exception {
EntityManager em = emf.createEntityManager();
@@ -177,10 +213,15 @@ public void testSubqueryFilterJoin() throws Exception {
stats2.setId("0");
stats2.setHighScore(2412);
- Assert.assertEquals(results.size(), 1);
- Assert.assertEquals(results.get(0), stats2);
+ assertEquals(1, results.size());
+ assertEquals(stats2, results.get(0));
}
+ /**
+ * Test a view which filters on "stats.overallRating = 'Great'".
+ *
+ * @throws Exception exception
+ */
@Test
public void testSubqueryLoad() throws Exception {
EntityManager em = emf.createEntityManager();
@@ -198,12 +239,15 @@ public void testSubqueryLoad() throws Exception {
stats2.setId("0");
stats2.setHighScore(2412);
- Assert.assertEquals(results.size(), 1);
- Assert.assertEquals(results.get(0), stats2);
+ assertEquals(1, results.size());
+ assertEquals(stats2, results.get(0));
}
+ /**
+ * Test sorting by dimension attribute which is not present in the query.
+ */
@Test
- public void testSortJoin() throws Exception {
+ public void testSortJoin() {
EntityManager em = emf.createEntityManager();
QueryEngine engine = new SQLQueryEngine(em, dictionary);
@@ -221,25 +265,35 @@ public void testSortJoin() throws Exception {
List results = StreamSupport.stream(engine.executeQuery(query).spliterator(), false)
.collect(Collectors.toList());
+ PlayerStats stats0 = new PlayerStats();
+ stats0.setId("0");
+ stats0.setLowScore(72);
+ stats0.setOverallRating("Good");
+ stats0.setRecordedDate(Timestamp.valueOf("2019-07-13 00:00:00"));
+
PlayerStats stats1 = new PlayerStats();
- stats1.setId("0");
+ stats1.setId("1");
stats1.setLowScore(241);
stats1.setOverallRating("Great");
stats1.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
PlayerStats stats2 = new PlayerStats();
- stats2.setId("1");
+ stats2.setId("2");
stats2.setLowScore(72);
stats2.setOverallRating("Good");
stats2.setRecordedDate(Timestamp.valueOf("2019-07-12 00:00:00"));
- Assert.assertEquals(results.size(), 2);
- Assert.assertEquals(results.get(0), stats1);
- Assert.assertEquals(results.get(1), stats2);
+ assertEquals(3, results.size());
+ assertEquals(stats0, results.get(0));
+ assertEquals(stats1, results.get(1));
+ assertEquals(stats2, results.get(2));
}
+ /**
+ * Test pagination.
+ */
@Test
- public void testPagination() throws Exception {
+ public void testPagination() {
EntityManager em = emf.createEntityManager();
QueryEngine engine = new SQLQueryEngine(em, dictionary);
@@ -265,11 +319,16 @@ public void testPagination() throws Exception {
stats1.setOverallRating("Good");
stats1.setRecordedDate(Timestamp.valueOf("2019-07-12 00:00:00"));
- Assert.assertEquals(results.size(), 1, "Number of records returned does not match");
- Assert.assertEquals(results.get(0), stats1, "Returned record does not match");
- Assert.assertEquals(pagination.getPageTotals(), 2, "Page totals does not match");
+ assertEquals(results.size(), 1, "Number of records returned does not match");
+ assertEquals(results.get(0), stats1, "Returned record does not match");
+ assertEquals(pagination.getPageTotals(), 3, "Page totals does not match");
}
+ /**
+ * Test having clause integrates with group by clause.
+ *
+ * @throws Exception exception
+ */
@Test
public void testHavingClause() throws Exception {
EntityManager em = emf.createEntityManager();
@@ -278,22 +337,29 @@ public void testHavingClause() throws Exception {
Query query = Query.builder()
.schema(playerStatsSchema)
.metric(playerStatsSchema.getMetric("highScore"), Sum.class)
- .havingFilter(filterParser.parseFilterExpression("highScore > 300",
+ .groupDimension(playerStatsSchema.getDimension("overallRating"))
+ .havingFilter(filterParser.parseFilterExpression("highScore < 2400",
PlayerStats.class, false))
.build();
List results = StreamSupport.stream(engine.executeQuery(query).spliterator(), false)
.collect(Collectors.toList());
- //Jon Doe,1234,72,Good,840,2019-07-12 00:00:00
+ // Only "Good" rating would have total high score less than 2400
PlayerStats stats1 = new PlayerStats();
stats1.setId("0");
- stats1.setHighScore(3646);
+ stats1.setOverallRating("Good");
+ stats1.setHighScore(2234);
- Assert.assertEquals(results.size(), 1);
- Assert.assertEquals(results.get(0), stats1);
+ assertEquals(1, results.size());
+ assertEquals(stats1, results.get(0));
}
+ /**
+ * Test group by, having, dimension, metric at the same time.
+ *
+ * @throws Exception exception
+ */
@Test
public void testTheEverythingQuery() throws Exception {
EntityManager em = emf.createEntityManager();
@@ -321,13 +387,15 @@ public void testTheEverythingQuery() throws Exception {
stats2.setHighScore(2412);
stats2.setCountryName("United States");
-
- Assert.assertEquals(results.size(), 1);
- Assert.assertEquals(results.get(0), stats2);
+ assertEquals(1, results.size());
+ assertEquals(stats2, results.get(0));
}
+ /**
+ * Test sorting by two different columns-one metric and one dimension.
+ */
@Test
- public void testSortByMultipleColumns() throws Exception {
+ public void testSortByMultipleColumns() {
EntityManager em = emf.createEntityManager();
QueryEngine engine = new SQLQueryEngine(em, dictionary);
@@ -346,20 +414,95 @@ public void testSortByMultipleColumns() throws Exception {
List results = StreamSupport.stream(engine.executeQuery(query).spliterator(), false)
.collect(Collectors.toList());
+ PlayerStats stats0 = new PlayerStats();
+ stats0.setId("0");
+ stats0.setLowScore(241);
+ stats0.setOverallRating("Great");
+ stats0.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
+
PlayerStats stats1 = new PlayerStats();
- stats1.setId("0");
- stats1.setLowScore(241);
- stats1.setOverallRating("Great");
- stats1.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
+ stats1.setId("1");
+ stats1.setLowScore(72);
+ stats1.setOverallRating("Good");
+ stats1.setRecordedDate(Timestamp.valueOf("2019-07-13 00:00:00"));
PlayerStats stats2 = new PlayerStats();
- stats2.setId("1");
+ stats2.setId("2");
stats2.setLowScore(72);
stats2.setOverallRating("Good");
stats2.setRecordedDate(Timestamp.valueOf("2019-07-12 00:00:00"));
- Assert.assertEquals(results.size(), 2);
- Assert.assertEquals(results.get(0), stats1);
- Assert.assertEquals(results.get(1), stats2);
+ assertEquals(3, results.size());
+ assertEquals(stats0, results.get(0));
+ assertEquals(stats1, results.get(1));
+ assertEquals(stats2, results.get(2));
+ }
+
+ /**
+ * Test hydrating multiple relationship values. Make sure the objects are constructed correctly.
+ */
+ @Test
+ public void testRelationshipHydration() {
+ EntityManager em = emf.createEntityManager();
+ QueryEngine engine = new SQLQueryEngine(em, dictionary);
+
+ Map sortMap = new TreeMap<>();
+ sortMap.put("country.name", Sorting.SortOrder.desc);
+
+ Query query = Query.builder()
+ .schema(playerStatsSchema)
+ .metric(playerStatsSchema.getMetric("lowScore"), Sum.class)
+ .metric(playerStatsSchema.getMetric("highScore"), Sum.class)
+ .groupDimension(playerStatsSchema.getDimension("overallRating"))
+ .groupDimension(playerStatsSchema.getDimension("country"))
+ .timeDimension((TimeDimension) playerStatsSchema.getDimension("recordedDate"))
+ .sorting(new Sorting(sortMap))
+ .build();
+
+ List results = StreamSupport.stream(engine.executeQuery(query).spliterator(), false)
+ .collect(Collectors.toList());
+
+ Country usa = new Country();
+ usa.setId("840");
+ usa.setIsoCode("USA");
+ usa.setName("United States");
+
+ Country hk = new Country();
+ hk.setId("344");
+ hk.setIsoCode("HKG");
+ hk.setName("Hong Kong");
+
+ PlayerStats usa0 = new PlayerStats();
+ usa0.setId("0");
+ usa0.setLowScore(241);
+ usa0.setHighScore(2412);
+ usa0.setOverallRating("Great");
+ usa0.setCountry(usa);
+ usa0.setRecordedDate(Timestamp.valueOf("2019-07-11 00:00:00"));
+
+ PlayerStats usa1 = new PlayerStats();
+ usa1.setId("1");
+ usa1.setLowScore(72);
+ usa1.setHighScore(1234);
+ usa1.setOverallRating("Good");
+ usa1.setCountry(usa);
+ usa1.setRecordedDate(Timestamp.valueOf("2019-07-12 00:00:00"));
+
+ PlayerStats hk2 = new PlayerStats();
+ hk2.setId("2");
+ hk2.setLowScore(72);
+ hk2.setHighScore(1000);
+ hk2.setOverallRating("Good");
+ hk2.setCountry(hk);
+ hk2.setRecordedDate(Timestamp.valueOf("2019-07-13 00:00:00"));
+
+ assertEquals(3, results.size());
+ assertEquals(usa0, results.get(0));
+ assertEquals(usa1, results.get(1));
+ assertEquals(hk2, results.get(2));
+
+ // test join
+ PlayerStats actualStats1 = (PlayerStats) results.get(0);
+ assertNotNull(actualStats1.getCountry());
}
}
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/example/Country.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/example/Country.java
index f9804b308b..6da056c26f 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/example/Country.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/example/Country.java
@@ -10,12 +10,15 @@
import com.yahoo.elide.datastores.aggregation.annotation.CardinalitySize;
import com.yahoo.elide.datastores.aggregation.annotation.FriendlyName;
+import lombok.Data;
+
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* A root level entity for testing AggregationDataStore.
*/
+@Data
@Entity
@Include(rootLevel = true)
@Cardinality(size = CardinalitySize.SMALL)
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/FilterConstraintsTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/FilterConstraintsTest.java
index 89ef468efd..b0195b60ac 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/FilterConstraintsTest.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/FilterConstraintsTest.java
@@ -5,13 +5,16 @@
*/
package com.yahoo.elide.datastores.aggregation.filter.visitor;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import com.yahoo.elide.core.Path;
import com.yahoo.elide.core.filter.FilterPredicate;
import com.yahoo.elide.core.filter.Operator;
import com.yahoo.elide.datastores.aggregation.example.PlayerStats;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
+import org.junit.jupiter.api.Test;
import java.util.Collections;
@@ -30,37 +33,37 @@ public class FilterConstraintsTest {
@Test
public void testPureHaving() {
- Assert.assertTrue(FilterConstraints.pureHaving(HAVING_PREDICATE).isPureHaving());
- Assert.assertFalse(FilterConstraints.pureHaving(HAVING_PREDICATE).isPureWhere());
- Assert.assertEquals(
- FilterConstraints.pureHaving(HAVING_PREDICATE).getHavingExpression().toString(),
- "playerStats.highScore GT [99]"
+ assertTrue(FilterConstraints.pureHaving(HAVING_PREDICATE).isPureHaving());
+ assertFalse(FilterConstraints.pureHaving(HAVING_PREDICATE).isPureWhere());
+ assertEquals(
+ "playerStats.highScore GT [99]",
+ FilterConstraints.pureHaving(HAVING_PREDICATE).getHavingExpression().toString()
);
- Assert.assertNull(FilterConstraints.pureHaving(HAVING_PREDICATE).getWhereExpression());
+ assertNull(FilterConstraints.pureHaving(HAVING_PREDICATE).getWhereExpression());
}
@Test
public void testPureWhere() {
- Assert.assertTrue(FilterConstraints.pureWhere(WHERE_PREDICATE).isPureWhere());
- Assert.assertFalse(FilterConstraints.pureWhere(WHERE_PREDICATE).isPureHaving());
- Assert.assertEquals(
- FilterConstraints.pureWhere(WHERE_PREDICATE).getWhereExpression().toString(),
- "playerStats.id IN [foo]"
+ assertTrue(FilterConstraints.pureWhere(WHERE_PREDICATE).isPureWhere());
+ assertFalse(FilterConstraints.pureWhere(WHERE_PREDICATE).isPureHaving());
+ assertEquals(
+ "playerStats.id IN [foo]",
+ FilterConstraints.pureWhere(WHERE_PREDICATE).getWhereExpression().toString()
);
- Assert.assertNull(FilterConstraints.pureWhere(WHERE_PREDICATE).getHavingExpression());
+ assertNull(FilterConstraints.pureWhere(WHERE_PREDICATE).getHavingExpression());
}
@Test
public void testWithWhereAndHaving() {
- Assert.assertFalse(FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).isPureWhere());
- Assert.assertFalse(FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).isPureHaving());
- Assert.assertEquals(
- FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).getWhereExpression().toString(),
- "playerStats.id IN [foo]"
+ assertFalse(FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).isPureWhere());
+ assertFalse(FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).isPureHaving());
+ assertEquals(
+ "playerStats.id IN [foo]",
+ FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).getWhereExpression().toString()
);
- Assert.assertEquals(
- FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).getHavingExpression().toString(),
- "playerStats.highScore GT [99]"
+ assertEquals(
+ "playerStats.highScore GT [99]",
+ FilterConstraints.withWhereAndHaving(WHERE_PREDICATE, HAVING_PREDICATE).getHavingExpression().toString()
);
}
}
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/SplitFilterExpressionVisitorTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/SplitFilterExpressionVisitorTest.java
index 441437b24b..834d1ec16e 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/SplitFilterExpressionVisitorTest.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/filter/visitor/SplitFilterExpressionVisitorTest.java
@@ -5,6 +5,11 @@
*/
package com.yahoo.elide.datastores.aggregation.filter.visitor;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import com.yahoo.elide.core.EntityDictionary;
import com.yahoo.elide.core.Path;
import com.yahoo.elide.core.filter.FilterPredicate;
@@ -17,10 +22,8 @@
import com.yahoo.elide.datastores.aggregation.example.Player;
import com.yahoo.elide.datastores.aggregation.example.PlayerStats;
import com.yahoo.elide.datastores.aggregation.schema.Schema;
-
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
import java.util.Collections;
@@ -37,12 +40,12 @@ public class SplitFilterExpressionVisitorTest {
Collections.singletonList(99)
);
- private EntityDictionary entityDictionary;
- private Schema schema;
- private FilterExpressionVisitor splitFilterExpressionVisitor;
+ private static EntityDictionary entityDictionary;
+ private static Schema schema;
+ private static FilterExpressionVisitor splitFilterExpressionVisitor;
- @BeforeMethod
- public void setupEntityDictionary() {
+ @BeforeAll
+ public static void setupEntityDictionary() {
entityDictionary = new EntityDictionary(Collections.emptyMap());
entityDictionary.bindEntity(PlayerStats.class);
entityDictionary.bindEntity(Country.class);
@@ -54,78 +57,78 @@ public void setupEntityDictionary() {
@Test
public void testVisitPredicate() {
// predicate should be a WHERE
- Assert.assertTrue(splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).isPureWhere());
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).getWhereExpression().toString(),
- "playerStats.id IN [foo]"
+ assertTrue(splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).isPureWhere());
+ assertEquals(
+ "playerStats.id IN [foo]",
+ splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).getWhereExpression().toString()
);
- Assert.assertFalse(splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).isPureHaving());
- Assert.assertNull(splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).getHavingExpression());
+ assertFalse(splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).isPureHaving());
+ assertNull(splitFilterExpressionVisitor.visitPredicate(WHERE_PREDICATE).getHavingExpression());
// predicate should be a HAVING
- Assert.assertTrue(splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).isPureHaving());
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).getHavingExpression().toString(),
- "playerStats.highScore GT [99]"
+ assertTrue(splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).isPureHaving());
+ assertEquals(
+ "playerStats.highScore GT [99]",
+ splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).getHavingExpression().toString()
);
- Assert.assertFalse(splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).isPureWhere());
- Assert.assertNull(splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).getWhereExpression());
+ assertFalse(splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).isPureWhere());
+ assertNull(splitFilterExpressionVisitor.visitPredicate(HAVING_PREDICATE).getWhereExpression());
}
@Test
public void testVisitAndExpression() {
// pure-W AND pure-W
AndFilterExpression filterExpression = new AndFilterExpression(WHERE_PREDICATE, WHERE_PREDICATE);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(filterExpression).getWhereExpression().toString(),
- "(playerStats.id IN [foo] AND playerStats.id IN [foo])"
+ assertEquals(
+ "(playerStats.id IN [foo] AND playerStats.id IN [foo])",
+ splitFilterExpressionVisitor.visitAndExpression(filterExpression).getWhereExpression().toString()
);
- Assert.assertNull(splitFilterExpressionVisitor.visitAndExpression(filterExpression).getHavingExpression());
+ assertNull(splitFilterExpressionVisitor.visitAndExpression(filterExpression).getHavingExpression());
// pure-H AND pure-W
filterExpression = new AndFilterExpression(HAVING_PREDICATE, WHERE_PREDICATE);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(filterExpression).getWhereExpression().toString(),
- "playerStats.id IN [foo]"
+ assertEquals(
+ "playerStats.id IN [foo]",
+ splitFilterExpressionVisitor.visitAndExpression(filterExpression).getWhereExpression().toString()
);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(filterExpression).getHavingExpression().toString(),
- "playerStats.highScore GT [99]"
+ assertEquals(
+ "playerStats.highScore GT [99]",
+ splitFilterExpressionVisitor.visitAndExpression(filterExpression).getHavingExpression().toString()
);
// pure-W AND pure-H
filterExpression = new AndFilterExpression(WHERE_PREDICATE, HAVING_PREDICATE);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(filterExpression).getWhereExpression().toString(),
- "playerStats.id IN [foo]"
+ assertEquals(
+ "playerStats.id IN [foo]",
+ splitFilterExpressionVisitor.visitAndExpression(filterExpression).getWhereExpression().toString()
);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(filterExpression).getHavingExpression().toString(),
- "playerStats.highScore GT [99]"
+ assertEquals(
+ "playerStats.highScore GT [99]",
+ splitFilterExpressionVisitor.visitAndExpression(filterExpression).getHavingExpression().toString()
);
// non-pure case - H1 AND W1 AND H2
AndFilterExpression and1 = new AndFilterExpression(HAVING_PREDICATE, WHERE_PREDICATE);
AndFilterExpression and2 = new AndFilterExpression(and1, HAVING_PREDICATE);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(and2).getWhereExpression().toString(),
- "playerStats.id IN [foo]"
+ assertEquals(
+ "playerStats.id IN [foo]",
+ splitFilterExpressionVisitor.visitAndExpression(and2).getWhereExpression().toString()
);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(and2).getHavingExpression().toString(),
- "(playerStats.highScore GT [99] AND playerStats.highScore GT [99])"
+ assertEquals(
+ "(playerStats.highScore GT [99] AND playerStats.highScore GT [99])",
+ splitFilterExpressionVisitor.visitAndExpression(and2).getHavingExpression().toString()
);
// non-pure case - (H1 OR H2) AND W1
OrFilterExpression or = new OrFilterExpression(HAVING_PREDICATE, HAVING_PREDICATE);
AndFilterExpression and = new AndFilterExpression(or, WHERE_PREDICATE);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(and).getWhereExpression().toString(),
- "playerStats.id IN [foo]"
+ assertEquals(
+ "playerStats.id IN [foo]",
+ splitFilterExpressionVisitor.visitAndExpression(and).getWhereExpression().toString()
);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitAndExpression(and).getHavingExpression().toString(),
- "(playerStats.highScore GT [99] OR playerStats.highScore GT [99])"
+ assertEquals(
+ "(playerStats.highScore GT [99] OR playerStats.highScore GT [99])",
+ splitFilterExpressionVisitor.visitAndExpression(and).getHavingExpression().toString()
);
}
@@ -133,27 +136,27 @@ public void testVisitAndExpression() {
public void testVisitOrExpression() {
// pure-W OR pure-W
OrFilterExpression filterExpression = new OrFilterExpression(WHERE_PREDICATE, WHERE_PREDICATE);
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitOrExpression(filterExpression).getWhereExpression().toString(),
- "(playerStats.id IN [foo] OR playerStats.id IN [foo])"
+ assertEquals(
+ "(playerStats.id IN [foo] OR playerStats.id IN [foo])",
+ splitFilterExpressionVisitor.visitOrExpression(filterExpression).getWhereExpression().toString()
);
- Assert.assertNull(splitFilterExpressionVisitor.visitOrExpression(filterExpression).getHavingExpression());
+ assertNull(splitFilterExpressionVisitor.visitOrExpression(filterExpression).getHavingExpression());
// H1 OR W1
OrFilterExpression or = new OrFilterExpression(HAVING_PREDICATE, WHERE_PREDICATE);
- Assert.assertNull(splitFilterExpressionVisitor.visitOrExpression(or).getWhereExpression());
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitOrExpression(or).getHavingExpression().toString(),
- "(playerStats.highScore GT [99] OR playerStats.id IN [foo])"
+ assertNull(splitFilterExpressionVisitor.visitOrExpression(or).getWhereExpression());
+ assertEquals(
+ "(playerStats.highScore GT [99] OR playerStats.id IN [foo])",
+ splitFilterExpressionVisitor.visitOrExpression(or).getHavingExpression().toString()
);
// (W1 AND H1) OR W2
AndFilterExpression and = new AndFilterExpression(WHERE_PREDICATE, HAVING_PREDICATE);
or = new OrFilterExpression(and, WHERE_PREDICATE);
- Assert.assertNull(splitFilterExpressionVisitor.visitOrExpression(or).getWhereExpression());
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitOrExpression(or).getHavingExpression().toString(),
- "((playerStats.id IN [foo] AND playerStats.highScore GT [99]) OR playerStats.id IN [foo])"
+ assertNull(splitFilterExpressionVisitor.visitOrExpression(or).getWhereExpression());
+ assertEquals(
+ "((playerStats.id IN [foo] AND playerStats.highScore GT [99]) OR playerStats.id IN [foo])",
+ splitFilterExpressionVisitor.visitOrExpression(or).getHavingExpression().toString()
);
}
@@ -162,10 +165,10 @@ public void testVisitNotExpression() {
NotFilterExpression notExpression = new NotFilterExpression(
new AndFilterExpression(WHERE_PREDICATE, HAVING_PREDICATE)
);
- Assert.assertNull(splitFilterExpressionVisitor.visitNotExpression(notExpression).getWhereExpression());
- Assert.assertEquals(
- splitFilterExpressionVisitor.visitNotExpression(notExpression).getHavingExpression().toString(),
- "(playerStats.id NOT [foo] OR playerStats.highScore LE [99])"
+ assertNull(splitFilterExpressionVisitor.visitNotExpression(notExpression).getWhereExpression());
+ assertEquals(
+ "(playerStats.id NOT [foo] OR playerStats.highScore LE [99])",
+ splitFilterExpressionVisitor.visitNotExpression(notExpression).getHavingExpression().toString()
);
}
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetricTest.java b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetricTest.java
index 24225a3557..51510f1f2a 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetricTest.java
+++ b/elide-datastore/elide-datastore-aggregation/src/test/java/com/yahoo/elide/datastores/aggregation/metric/AggregatedMetricTest.java
@@ -5,11 +5,12 @@
*/
package com.yahoo.elide.datastores.aggregation.metric;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.mockito.Mockito.mock;
import com.yahoo.elide.datastores.aggregation.schema.Schema;
-import org.testng.Assert;
-import org.testng.annotations.Test;
+import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.HashSet;
@@ -37,16 +38,16 @@ public class AggregatedMetricTest {
@Test
public void testMetricAsCollectionElement() {
- Assert.assertEquals(SIMPLE_METRIC_1, SIMPLE_METRIC_1);
- Assert.assertEquals(SIMPLE_METRIC_2, SIMPLE_METRIC_2);
- Assert.assertNotEquals(SIMPLE_METRIC_1, SIMPLE_METRIC_2);
- Assert.assertNotEquals(SIMPLE_METRIC_1.hashCode(), SIMPLE_METRIC_2.hashCode());
+ assertEquals(SIMPLE_METRIC_1, SIMPLE_METRIC_1);
+ assertEquals(SIMPLE_METRIC_2, SIMPLE_METRIC_2);
+ assertNotEquals(SIMPLE_METRIC_1, SIMPLE_METRIC_2);
+ assertNotEquals(SIMPLE_METRIC_1.hashCode(), SIMPLE_METRIC_2.hashCode());
// different metrics should be separate elements in Set
Set set = new HashSet<>();
set.add(SIMPLE_METRIC_1);
- Assert.assertEquals(set.size(), 1);
+ assertEquals(1, set.size());
// a separate same object doesn't increase collection size
Metric sameMetric = new AggregatedMetric(
@@ -56,29 +57,29 @@ public void testMetricAsCollectionElement() {
long.class,
Collections.singletonList(Max.class)
);
- Assert.assertEquals(sameMetric, SIMPLE_METRIC_1);
+ assertEquals(SIMPLE_METRIC_1, sameMetric);
set.add(sameMetric);
- Assert.assertEquals(set.size(), 1);
+ assertEquals(1, set.size());
set.add(SIMPLE_METRIC_1);
- Assert.assertEquals(set.size(), 1);
+ assertEquals(1, set.size());
set.add(SIMPLE_METRIC_2);
- Assert.assertEquals(set.size(), 2);
+ assertEquals(2, set.size());
}
@Test
public void testToString() {
// simple metric
- Assert.assertEquals(
- SIMPLE_METRIC_1.toString(),
- "AggregatedMetric[name='highScore', longName='highScore', description='highScore', dataType=long, aggregations=Max]"
+ assertEquals(
+ "AggregatedMetric[name='highScore', longName='highScore', description='highScore', dataType=long, aggregations=Max]",
+ SIMPLE_METRIC_1.toString()
);
// computed metric
- Assert.assertEquals(
- SIMPLE_METRIC_2.toString(),
- "AggregatedMetric[name='timeSpentPerGame', longName='timeSpentPerGame', description='timeSpentPerGame', dataType=class java.lang.Float, aggregations=Max]"
+ assertEquals(
+ "AggregatedMetric[name='timeSpentPerGame', longName='timeSpentPerGame', description='timeSpentPerGame', dataType=class java.lang.Float, aggregations=Max]",
+ SIMPLE_METRIC_2.toString()
);
}
}
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/resources/country.csv b/elide-datastore/elide-datastore-aggregation/src/test/resources/country.csv
index 207eb92c5e..e618f4e629 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/resources/country.csv
+++ b/elide-datastore/elide-datastore-aggregation/src/test/resources/country.csv
@@ -1,3 +1,3 @@
id,isoCode,name
-840,USA,United States
344,HKG,Hong Kong
+840,USA,United States
diff --git a/elide-datastore/elide-datastore-aggregation/src/test/resources/player_stats.csv b/elide-datastore/elide-datastore-aggregation/src/test/resources/player_stats.csv
index 7a75dd4765..e0b18466d0 100644
--- a/elide-datastore/elide-datastore-aggregation/src/test/resources/player_stats.csv
+++ b/elide-datastore/elide-datastore-aggregation/src/test/resources/player_stats.csv
@@ -1,3 +1,4 @@
id,highScore,lowScore,overallRating,country_id,player_id,recordedDate
Jon Doe,1234,72,Good,840,1,2019-07-12 00:00:00
Jane Doe,2412,241,Great,840,2,2019-07-11 00:00:00
+Han,1000,72,Good,344,3,2019-07-13 00:00:00
diff --git a/elide-datastore/elide-datastore-hibernate/src/main/java/com/yahoo/elide/core/filter/FilterTranslator.java b/elide-datastore/elide-datastore-hibernate/src/main/java/com/yahoo/elide/core/filter/FilterTranslator.java
index d334acb2ee..a9604ecaad 100644
--- a/elide-datastore/elide-datastore-hibernate/src/main/java/com/yahoo/elide/core/filter/FilterTranslator.java
+++ b/elide-datastore/elide-datastore-hibernate/src/main/java/com/yahoo/elide/core/filter/FilterTranslator.java
@@ -202,8 +202,8 @@ public static void registerJPQLGenerator(Operator op,
* @return Returns null if no generator is registered.
*/
public static JPQLPredicateGenerator lookupJPQLGenerator(Operator op,
- Class> entityClass,
- String fieldName) {
+ Class> entityClass,
+ String fieldName) {
return predicateOverrides.get(Triple.of(op, entityClass, fieldName));
}
diff --git a/pom.xml b/pom.xml
index b93680dcb3..5999f99a8c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -400,11 +400,11 @@
1.11.2
-
+
org.apache.maven.scm
maven-scm-api
1.11.2
-
+
@{project.version}