mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1619 - Support aggregation formula on fetch clause e.g. query.fetch("machineUsage", "sum(totalKms)")
This commit is contained in:
@@ -2586,9 +2586,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* Return a 'dynamic property' used to read a formula.
|
||||
*/
|
||||
private STreeProperty findSqlTreeFormula(String formulaExpression) {
|
||||
|
||||
return dynamicProperty.computeIfAbsent(formulaExpression, (formula) -> new FormulaPropertyPath(this, formula).build());
|
||||
private STreeProperty findSqlTreeFormula(String formula, String path) {
|
||||
String key = formula + "-" + path;
|
||||
return dynamicProperty.computeIfAbsent(key, (fullKey) -> new FormulaPropertyPath(this, formula, path).build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2597,9 +2597,9 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
* The property can be a dynamic formula or a well known bean property.
|
||||
*/
|
||||
@Override
|
||||
public STreeProperty findPropertyWithDynamic(String propName) {
|
||||
public STreeProperty findPropertyWithDynamic(String propName, String path) {
|
||||
if (propName.indexOf('(') > -1) {
|
||||
return findSqlTreeFormula(propName);
|
||||
return findSqlTreeFormula(propName, path);
|
||||
}
|
||||
return _findBeanProperty(propName);
|
||||
}
|
||||
|
||||
@@ -20,15 +20,17 @@ class FormulaPropertyPath {
|
||||
|
||||
private final String internalExpression;
|
||||
|
||||
private final String path;
|
||||
|
||||
private boolean countDistinct;
|
||||
|
||||
private String cast;
|
||||
private String alias;
|
||||
|
||||
FormulaPropertyPath(BeanDescriptor<?> descriptor, String formula) {
|
||||
|
||||
FormulaPropertyPath(BeanDescriptor<?> descriptor, String formula, String path) {
|
||||
this.descriptor = descriptor;
|
||||
this.formula = formula;
|
||||
this.path = path;
|
||||
|
||||
int openBracket = formula.indexOf('(');
|
||||
int closeBracket = formula.lastIndexOf(')');
|
||||
@@ -94,6 +96,11 @@ class FormulaPropertyPath {
|
||||
DeployPropertyParser parser = descriptor.parser().setCatchFirst(true);
|
||||
|
||||
String parsed = parser.parse(internalExpression);
|
||||
if (path != null) {
|
||||
// fetch("machineStats", "sum(hours), sum(totalKms)")
|
||||
parsed = parsed.replace("${}", "${" + path + "}");
|
||||
}
|
||||
|
||||
ElPropertyDeploy firstProp = parser.getFirstProp();
|
||||
|
||||
ScalarType<?> scalarType;
|
||||
|
||||
@@ -120,7 +120,7 @@ public interface STreeType {
|
||||
/**
|
||||
* Find and return property allowing for dynamic formula properties.
|
||||
*/
|
||||
STreeProperty findPropertyWithDynamic(String baseName);
|
||||
STreeProperty findPropertyWithDynamic(String baseName, String path);
|
||||
|
||||
/**
|
||||
* Return an extra join if the property path requires it.
|
||||
|
||||
@@ -273,7 +273,10 @@ public final class SqlTreeBuilder {
|
||||
OrmQueryProperties queryProps = queryDetail.getChunk(prefix, false);
|
||||
SqlTreeProperties props = getBaseSelect(desc, queryProps);
|
||||
|
||||
if (prefix == null && !rawSql) {
|
||||
if (prefix != null) {
|
||||
// check for aggregation on a fetch
|
||||
props.checkAggregation();
|
||||
} else if (!rawSql) {
|
||||
if (props.requireSqlDistinct(manyWhereJoins)) {
|
||||
sqlDistinct = true;
|
||||
}
|
||||
@@ -417,10 +420,10 @@ public final class SqlTreeBuilder {
|
||||
|
||||
// make sure we only included the base/embedded bean once
|
||||
if (!selectProps.containsProperty(baseName)) {
|
||||
STreeProperty p = desc.findPropertyWithDynamic(baseName);
|
||||
STreeProperty p = desc.findPropertyWithDynamic(baseName, null);
|
||||
if (p == null) {
|
||||
// maybe dynamic formula with schema prefix
|
||||
p = desc.findPropertyWithDynamic(propName);
|
||||
p = desc.findPropertyWithDynamic(propName, null);
|
||||
if (p != null) {
|
||||
selectProps.add(p);
|
||||
} else {
|
||||
@@ -437,7 +440,7 @@ public final class SqlTreeBuilder {
|
||||
} else {
|
||||
// find the property including searching the
|
||||
// sub class hierarchy if required
|
||||
STreeProperty p = desc.findPropertyWithDynamic(propName);
|
||||
STreeProperty p = desc.findPropertyWithDynamic(propName, queryProps.getPath());
|
||||
if (p == null) {
|
||||
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
|
||||
p = desc.findProperty("id");
|
||||
|
||||
@@ -378,7 +378,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
if (!readId || temporalVersions) {
|
||||
// a bean with no Id (never found in context)
|
||||
if (lazyLoadParentId != null && desc.isElementType()) {
|
||||
if (lazyLoadParentId != null) {
|
||||
ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId);
|
||||
}
|
||||
return localBean;
|
||||
@@ -422,6 +422,9 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
ctx.pushJoin(prefix);
|
||||
ctx.pushTableAlias(prefix);
|
||||
if (lazyLoadParent != null) {
|
||||
lazyLoadParent.addSelectExported(ctx, prefix);
|
||||
}
|
||||
if (readId) {
|
||||
appendSelectId(ctx, idBinder.getBeanProperty());
|
||||
}
|
||||
@@ -486,7 +489,15 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
@Override
|
||||
public boolean isAggregation() {
|
||||
return aggregation;
|
||||
if (aggregation) {
|
||||
return true;
|
||||
}
|
||||
for (SqlTreeNode child : children) {
|
||||
if (child.isAggregation()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,6 +92,13 @@ public class SqlTreeProperties {
|
||||
return aggregation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for aggregation (need for groug by clause).
|
||||
*/
|
||||
public void checkAggregation() {
|
||||
aggregationJoin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property to join for aggregation.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user