This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 7411fdf400904ccb6ecb1c1b47ccd52e297a65dc Author: Nikita Timofeev <stari...@gmail.com> AuthorDate: Wed Sep 16 12:26:07 2020 +0300 CAY-2673 Support ordering by aggregate functions --- .../access/translator/select/GroupByStage.java | 11 ++++++ .../access/translator/select/OrderingStage.java | 17 +++------ .../translator/select/ResultNodeDescriptor.java | 6 ++- .../cayenne/query/ObjectSelect_AggregateIT.java | 43 ++++++++++++++++++++-- 4 files changed, 61 insertions(+), 16 deletions(-) diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/GroupByStage.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/GroupByStage.java index 42be789..211a55a 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/GroupByStage.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/GroupByStage.java @@ -19,6 +19,9 @@ package org.apache.cayenne.access.translator.select; +import org.apache.cayenne.exp.parser.ASTAggregateFunctionCall; +import org.apache.cayenne.query.Ordering; + /** * @since 4.2 */ @@ -49,6 +52,14 @@ class GroupByStage implements TranslationStage { } } + if(context.getQuery().getOrderings() != null) { + for(Ordering ordering : context.getQuery().getOrderings()) { + if(ordering.getSortSpec() instanceof ASTAggregateFunctionCall) { + return true; + } + } + } + return false; } } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingStage.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingStage.java index 7cde628..d3085d6 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingStage.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/OrderingStage.java @@ -55,10 +55,13 @@ class OrderingStage implements TranslationStage { } // If query is DISTINCT then we need to add all ORDER BY clauses as result columns - if(shouldAddToResult(context, exp)) { + if(!context.isDistinctSuppression()) { // TODO: need to check duplicates? // need UPPER() function here too, as some DB expect exactly the same expression in select and in ordering - context.addResultNode(nodeBuilder.build().deepCopy()); + ResultNodeDescriptor descriptor = context.addResultNode(nodeBuilder.build().deepCopy()); + if(exp instanceof ASTAggregateFunctionCall) { + descriptor.setAggregate(true); + } } OrderingNodeBuilder orderingNodeBuilder = order(nodeBuilder); @@ -68,14 +71,4 @@ class OrderingStage implements TranslationStage { context.getSelectBuilder().orderBy(orderingNodeBuilder); } - private boolean shouldAddToResult(TranslatorContext context, Expression exp) { - if(context.isDistinctSuppression()) { - return false; - } - if(exp instanceof ASTAggregateFunctionCall) { - return false; - } - return true; - } - } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java index 4eb03d7..805210f 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/ResultNodeDescriptor.java @@ -34,9 +34,9 @@ import org.apache.cayenne.map.DbAttribute; class ResultNodeDescriptor { private final Node node; private final boolean inDataRow; - private final boolean isAggregate; private final Property<?> property; + private boolean isAggregate; private String dataRowKey; private DbAttribute dbAttribute; private String javaType; @@ -50,6 +50,10 @@ class ResultNodeDescriptor { && property.getExpression() instanceof ASTAggregateFunctionCall; } + public void setAggregate(boolean aggregate) { + isAggregate = aggregate; + } + public boolean isAggregate() { return isAggregate; } diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java index f3dc231..6c0943d 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java @@ -34,6 +34,7 @@ import org.apache.cayenne.exp.property.PropertyFactory; import org.apache.cayenne.test.jdbc.DBHelper; import org.apache.cayenne.test.jdbc.TableHelper; import org.apache.cayenne.testdo.testmap.Artist; +import org.apache.cayenne.testdo.testmap.Painting; import org.apache.cayenne.unit.di.server.CayenneProjects; import org.apache.cayenne.unit.di.server.ServerCase; import org.apache.cayenne.unit.di.server.UseServerRuntime; @@ -79,11 +80,11 @@ public class ObjectSelect_AggregateIT extends ServerCase { tGallery.insert(1, "tate modern"); TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING"); - tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID"); + tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID", "ESTIMATED_PRICE"); for (int i = 1; i <= 20; i++) { - tPaintings.insert(i, "painting" + i, i % 5 + 1, 1); + tPaintings.insert(i, "painting" + i, i % 5 + 1, 1, i * 10); } - tPaintings.insert(21, "painting21", 2, 1); + tPaintings.insert(21, "painting21", 2, 1, 1000); } @After @@ -178,4 +179,40 @@ public class ObjectSelect_AggregateIT extends ServerCase { assertEquals("artist1", result[0]); assertEquals(4L, result[1]); } + + @Test + public void testOrderByCount() { + List<Artist> artists = ObjectSelect.query(Artist.class) + .orderBy(Artist.PAINTING_ARRAY.outer().count().desc()) + .prefetch(Artist.PAINTING_ARRAY.disjoint()) + .select(context); + + assertEquals(20, artists.size()); + + assertEquals(5, artists.get(0).getPaintingArray().size()); + assertEquals("artist2", artists.get(0).getArtistName()); + assertEquals(4, artists.get(1).getPaintingArray().size()); + assertEquals(4, artists.get(2).getPaintingArray().size()); + + assertEquals(0, artists.get(17).getPaintingArray().size()); + assertEquals(0, artists.get(18).getPaintingArray().size()); + assertEquals(0, artists.get(19).getPaintingArray().size()); + } + + @Test + public void testOrderByAvg() { + List<Artist> artists = ObjectSelect.query(Artist.class) + .orderBy(Artist.PAINTING_ARRAY.dot(Painting.ESTIMATED_PRICE).avg().asc()) + .prefetch(Artist.PAINTING_ARRAY.disjoint()) + .select(context); + + assertEquals(5, artists.size()); + + assertEquals("artist3", artists.get(0).getArtistName()); + assertEquals("artist4", artists.get(1).getArtistName()); + assertEquals("artist5", artists.get(2).getArtistName()); + assertEquals("artist1", artists.get(3).getArtistName()); + assertEquals("artist2", artists.get(4).getArtistName()); + + } } \ No newline at end of file