change the new syntax for HQL collection-aggregates
this feels more consistent to me, and avoids introducing "new" syntax
This commit is contained in:
parent
5828536428
commit
9ebac67737
|
@ -970,6 +970,8 @@ The following functions may be applied to a collection-valued path expression to
|
|||
| Only legal as a terminal path, and only allowed in the `select` clause.
|
||||
|===
|
||||
|
||||
We've intentionally left two functions off this list, so we can come back to them <<hql-elements-indices,later>>.
|
||||
|
||||
NOTE: Of these, only `index()` is defined by the JPQL specification.
|
||||
|
||||
[[hql-collection-qualification-example]]
|
||||
|
@ -994,37 +996,6 @@ include::{sourcedir}/HQLTest.java[tags=hql-collection-index-operator-example]
|
|||
----
|
||||
====
|
||||
|
||||
There are even more functions which accept a collection-valued attribute or to-many association:
|
||||
|
||||
|===
|
||||
| HQL Function | Synonym | Old syntax | Applies to | Purpose
|
||||
|
||||
| `max(element x)` | `max(value x)` | `maxelement(x)` | Any collection with sortable elements | The maximum element or map value
|
||||
| `min(element x)` | `min(value x)` | `minelement(x)` | Any collection with sortable elements | The minimum element or map value
|
||||
| `sum(element x)` | `sum(value x)` | — | Any collection with numeric elements | The sum of the elements or map values
|
||||
| `avg(element x)` | `avg(value x)` | — | Any collection with numeric elements | The sum of the elements or map values
|
||||
| `max(index x)` | `max(key x)` | `maxindex(x)` | Indexed collections (lists and maps) | The maximum list index or map key
|
||||
| `min(index x)` | `min(key x)` | `minindex(x)` | Indexed collections (lists and maps) | The minimum list index or map key
|
||||
| `sum(index x)` | `sum(key x)` | — | Indexed collections (lists and maps) | The sum of the list indexes or map keys
|
||||
| `avg(index x)` | `avg(key x)`| — | Indexed collections (lists and maps) | The average of the list indexes or map keys
|
||||
|===
|
||||
|
||||
We've intentionally left two functions off this list, so we can come back to them <<hql-elements-indices,later>>.
|
||||
|
||||
[[hql-collection-expressions-example]]
|
||||
//.Collection-related expressions examples
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/HQLTest.java[tags=hql-collection-expressions-example]
|
||||
----
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
These operations can almost always be written in another way, without the use of these convenience functions.
|
||||
====
|
||||
|
||||
[[hql-more-functions]]
|
||||
==== More HQL functions
|
||||
|
||||
|
@ -1223,7 +1194,7 @@ As you can guess, `not like` and `not ilike` are the enemies of `like` and `ilik
|
|||
[[hql-elements-indices]]
|
||||
==== Elements and indices
|
||||
|
||||
There's two special HQL functions that we didn't mention <<hql-more-functions,earlier>>, since they're only useful in conjunction with the predicate operators we're about to meet.
|
||||
There's two special HQL functions that we didn't mention <<hql-collection-qualification,earlier>>, since they're only useful in conjunction with the predicate operators we're about to meet.
|
||||
|
||||
These functions are only allowed in the `where` clause, and result in a subquery in the generated SQL.
|
||||
Indeed, you can think of them as just a shortcut way to write a subquery.
|
||||
|
@ -1896,6 +1867,38 @@ HQL defines the two additional aggregate functions which accept a logical predic
|
|||
|
||||
NOTE: Aggregate functions usually appear in the `select` clause, but control over aggregation is the responsibility of the `group by` clause, as described <<hql-group-by,below>>.
|
||||
|
||||
[[hql-aggregate-functions-collections]]
|
||||
==== Aggregate functions and collections
|
||||
|
||||
The `elements()` and `indices()` functions we met <<hql-elements-indices,earlier>> let us apply aggregate functions to a collection:
|
||||
|
||||
|===
|
||||
| New syntax | Legacy HQL function | Applies to | Purpose
|
||||
|
||||
| `max(elements(x))` | `maxelement(x)` | Any collection with sortable elements | The maximum element or map value
|
||||
| `min(elements(x))` | `minelement(x)` | Any collection with sortable elements | The minimum element or map value
|
||||
| `sum(elements(x))` | — | Any collection with numeric elements | The sum of the elements or map values
|
||||
| `avg(elements(x))` | — | Any collection with numeric elements | The average of the elements or map values
|
||||
| `max(indices(x))` | `maxindex(x)` | Indexed collections (lists and maps) | The maximum list index or map key
|
||||
| `min(indices(x))` | `minindex(x)` | Indexed collections (lists and maps) | The minimum list index or map key
|
||||
| `sum(indices(x))` | — | Indexed collections (lists and maps) | The sum of the list indexes or map keys
|
||||
| `avg(indices(x))` | — | Indexed collections (lists and maps) | The average of the list indexes or map keys
|
||||
|===
|
||||
|
||||
[[hql-collection-expressions-example]]
|
||||
//.Collection-related expressions examples
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/HQLTest.java[tags=hql-collection-expressions-example]
|
||||
----
|
||||
====
|
||||
|
||||
[TIP]
|
||||
====
|
||||
These operations can almost always be written in another way, without the use of these convenience functions.
|
||||
====
|
||||
|
||||
[[hql-aggregate-functions-filter]]
|
||||
==== `filter`
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ import org.hibernate.dialect.SybaseASEDialect;
|
|||
import org.hibernate.dialect.TiDBDialect;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.query.QueryProducer;
|
||||
import org.hibernate.testing.Skip;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.userguide.model.Account;
|
||||
import org.hibernate.userguide.model.AddressType;
|
||||
|
@ -1801,7 +1800,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
List<Phone> phones = entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Phone p " +
|
||||
"where max(element p.calls) = :call",
|
||||
"where max(elements(p.calls)) = :call",
|
||||
Phone.class)
|
||||
.setParameter("call", call)
|
||||
.getResultList();
|
||||
|
@ -1819,7 +1818,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
List<Phone> phones = entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Phone p " +
|
||||
"where min(element p.calls) = :call",
|
||||
"where min(elements(p.calls)) = :call",
|
||||
Phone.class)
|
||||
.setParameter("call", call)
|
||||
.getResultList();
|
||||
|
@ -1836,7 +1835,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
List<Person> persons = entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where max(index p.phones) = 0",
|
||||
"where max(indices(p.phones)) = 0",
|
||||
Person.class)
|
||||
.getResultList();
|
||||
//end::hql-collection-expressions-example[]
|
||||
|
@ -1993,7 +1992,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
|||
List<Person> persons = entityManager.createQuery(
|
||||
"select pr " +
|
||||
"from Person pr " +
|
||||
"where pr.phones[max(index pr.phones)].type = 'LAND_LINE'",
|
||||
"where pr.phones[max(indices(pr.phones))].type = 'LAND_LINE'",
|
||||
Person.class)
|
||||
.getResultList();
|
||||
//end::hql-collection-index-operator-example[]
|
||||
|
|
|
@ -597,7 +597,7 @@ predicate
|
|||
| expression NOT? BETWEEN expression AND expression # BetweenPredicate
|
||||
| expression NOT? (LIKE | ILIKE) expression likeEscape? # LikePredicate
|
||||
| expression comparisonOperator expression # ComparisonPredicate
|
||||
| EXISTS (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN # ExistsCollectionPartPredicate
|
||||
| EXISTS (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN # ExistsCollectionPartPredicate
|
||||
| EXISTS expression # ExistsPredicate
|
||||
| expression NOT? MEMBER OF? path # MemberOfPredicate
|
||||
| NOT predicate # NegatedPredicate
|
||||
|
@ -626,7 +626,7 @@ comparisonOperator
|
|||
* A list of values, a parameter (for a parameterized list of values), a subquery, or an 'elements()' or 'indices()' function
|
||||
*/
|
||||
inList
|
||||
: (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN # PersistentCollectionReferenceInList
|
||||
: (ELEMENTS|INDICES) LEFT_PAREN simplePath RIGHT_PAREN # PersistentCollectionReferenceInList
|
||||
| LEFT_PAREN (expressionOrPredicate (COMMA expressionOrPredicate)*)? RIGHT_PAREN# ExplicitTupleInList
|
||||
| LEFT_PAREN subquery RIGHT_PAREN # SubqueryInList
|
||||
| parameter # ParamInList
|
||||
|
@ -1008,10 +1008,10 @@ jpaCollectionFunction
|
|||
indexAggregateFunction
|
||||
: MAXINDEX LEFT_PAREN path RIGHT_PAREN
|
||||
| MININDEX LEFT_PAREN path RIGHT_PAREN
|
||||
| MAX LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
||||
| MIN LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
||||
| SUM LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
||||
| AVG LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
||||
| MAX LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
| MIN LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
| SUM LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
| AVG LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
;
|
||||
|
||||
/**
|
||||
|
@ -1020,10 +1020,10 @@ indexAggregateFunction
|
|||
elementAggregateFunction
|
||||
: MAXELEMENT LEFT_PAREN path RIGHT_PAREN
|
||||
| MINELEMENT LEFT_PAREN path RIGHT_PAREN
|
||||
| MAX LEFT_PAREN (ELEMENT|VALUE) path RIGHT_PAREN
|
||||
| MIN LEFT_PAREN (ELEMENT|VALUE) path RIGHT_PAREN
|
||||
| SUM LEFT_PAREN (ELEMENT|VALUE) path RIGHT_PAREN
|
||||
| AVG LEFT_PAREN (ELEMENT|VALUE) path RIGHT_PAREN
|
||||
| MAX LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
| MIN LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
| SUM LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
| AVG LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||
;
|
||||
|
||||
/**
|
||||
|
|
|
@ -89,39 +89,115 @@ public class FunctionTests {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// @Test
|
||||
// public void testMaxMinSumIndexElement(SessionFactoryScope scope) {
|
||||
// scope.inTransaction(
|
||||
// session -> {
|
||||
// assertThat( session.createQuery("select max(index eol.listOfNumbers) from EntityOfLists eol")
|
||||
// .getSingleResult(), is(1) );
|
||||
// assertThat( session.createQuery("select max(element eol.listOfNumbers) from EntityOfLists eol")
|
||||
// .getSingleResult(), is(2.0) );
|
||||
//
|
||||
// assertThat( session.createQuery("select sum(index eol.listOfNumbers) from EntityOfLists eol")
|
||||
// .getSingleResult(), is(1) );
|
||||
// assertThat( session.createQuery("select sum(element eol.listOfNumbers) from EntityOfLists eol")
|
||||
// .getSingleResult(), is(3.0) );
|
||||
//
|
||||
// //TODO: why does this fail??
|
||||
//// assertThat( session.createQuery("select avg(index eol.listOfNumbers) from EntityOfLists eol")
|
||||
//// .getSingleResult(), is(0.5) );
|
||||
// assertThat( session.createQuery("select avg(element eol.listOfNumbers) from EntityOfLists eol")
|
||||
// .getSingleResult(), is(1.5) );
|
||||
//
|
||||
// assertThat( session.createQuery("select max(index eom.numberByNumber) from EntityOfMaps eom")
|
||||
// .getSingleResult(), is(1) );
|
||||
// assertThat( session.createQuery("select max(element eom.numberByNumber) from EntityOfMaps eom")
|
||||
// .getSingleResult(), is(1.0) );
|
||||
//
|
||||
// assertThat( session.createQuery("select sum(index eom.numberByNumber) from EntityOfMaps eom")
|
||||
// .getSingleResult(), is(1) );
|
||||
// assertThat( session.createQuery("select sum(element eom.numberByNumber) from EntityOfMaps eom")
|
||||
// .getSingleResult(), is(1.0) );
|
||||
//
|
||||
// assertThat( session.createQuery("select avg(index eom.numberByNumber) from EntityOfMaps eom")
|
||||
// .getSingleResult(), is(1) );
|
||||
// assertThat( session.createQuery("select avg(element eom.numberByNumber) from EntityOfMaps eom")
|
||||
// .getSingleResult(), is(1.0) );
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
//
|
||||
@Test
|
||||
public void testMaxMinSumIndexElement(SessionFactoryScope scope) {
|
||||
public void testAltMaxMinSumIndexElement(SessionFactoryScope scope) {
|
||||
//TODO: make the commented tests work!
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
assertThat( session.createQuery("select max(index eol.listOfNumbers) from EntityOfLists eol")
|
||||
.getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select max(element eol.listOfNumbers) from EntityOfLists eol")
|
||||
// assertThat( session.createQuery("select max(index(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||
// .getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select max(element(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||
.getSingleResult(), is(2.0) );
|
||||
|
||||
assertThat( session.createQuery("select sum(index eol.listOfNumbers) from EntityOfLists eol")
|
||||
.getSingleResult(), is(1L) );
|
||||
assertThat( session.createQuery("select sum(element eol.listOfNumbers) from EntityOfLists eol")
|
||||
// assertThat( session.createQuery("select sum(index(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||
// .getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select sum(element(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||
.getSingleResult(), is(3.0) );
|
||||
|
||||
assertThat( session.createQuery("select avg(index eol.listOfNumbers) from EntityOfLists eol")
|
||||
.getSingleResult(), is(0.5) );
|
||||
assertThat( session.createQuery("select avg(element eol.listOfNumbers) from EntityOfLists eol")
|
||||
// assertThat( session.createQuery("select avg(index(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||
// .getSingleResult(), is(0.5) );
|
||||
assertThat( session.createQuery("select avg(element(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||
.getSingleResult(), is(1.5) );
|
||||
|
||||
assertThat( session.createQuery("select max(index eom.numberByNumber) from EntityOfMaps eom")
|
||||
// assertThat( session.createQuery("select max(index(eom.numberByNumber)) from EntityOfMaps eom group by eom")
|
||||
// .getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select max(element(eom.numberByNumber)) from EntityOfMaps eom group by eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
|
||||
// assertThat( session.createQuery("select sum(index(eom.numberByNumber)) from EntityOfMaps eom group by eom")
|
||||
// .getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select sum(element(eom.numberByNumber)) from EntityOfMaps eom group by eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
|
||||
// assertThat( session.createQuery("select avg(index(eom.numberByNumber)) from EntityOfMaps eom group by eom")
|
||||
// .getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select avg(element(eom.numberByNumber)) from EntityOfMaps eom group by eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxMinSumIndicesElements(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
assertThat( session.createQuery("select max(indices(eol.listOfNumbers)) from EntityOfLists eol")
|
||||
.getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select max(element eom.numberByNumber) from EntityOfMaps eom")
|
||||
assertThat( session.createQuery("select max(elements(eol.listOfNumbers)) from EntityOfLists eol")
|
||||
.getSingleResult(), is(2.0) );
|
||||
|
||||
assertThat( session.createQuery("select sum(indices(eol.listOfNumbers)) from EntityOfLists eol")
|
||||
.getSingleResult(), is(1) ); //TODO: should be Long
|
||||
assertThat( session.createQuery("select sum(elements(eol.listOfNumbers)) from EntityOfLists eol")
|
||||
.getSingleResult(), is(3.0) );
|
||||
|
||||
// assertThat( session.createQuery("select avg(indices(eol.listOfNumbers)) from EntityOfLists eol")
|
||||
// .getSingleResult(), is(0.5) ); //TODO: FIX!!
|
||||
assertThat( session.createQuery("select avg(elements(eol.listOfNumbers)) from EntityOfLists eol")
|
||||
.getSingleResult(), is(1.5) );
|
||||
|
||||
assertThat( session.createQuery("select max(indices(eom.numberByNumber)) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1) );
|
||||
assertThat( session.createQuery("select max(elements(eom.numberByNumber)) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
|
||||
assertThat( session.createQuery("select sum(index eom.numberByNumber) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1L) );
|
||||
assertThat( session.createQuery("select sum(element eom.numberByNumber) from EntityOfMaps eom")
|
||||
assertThat( session.createQuery("select sum(indices(eom.numberByNumber)) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1) ); //TODO: should be Long
|
||||
assertThat( session.createQuery("select sum(elements(eom.numberByNumber)) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
|
||||
assertThat( session.createQuery("select avg(index eom.numberByNumber) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
assertThat( session.createQuery("select avg(element eom.numberByNumber) from EntityOfMaps eom")
|
||||
assertThat( session.createQuery("select avg(indices(eom.numberByNumber)) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1) ); //TODO: should be Double
|
||||
assertThat( session.createQuery("select avg(elements(eom.numberByNumber)) from EntityOfMaps eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
}
|
||||
);
|
||||
|
@ -141,7 +217,6 @@ public class FunctionTests {
|
|||
assertThat( session.createQuery("select sum(element(l)) from EntityOfLists eol join eol.listOfNumbers l group by eol")
|
||||
.getSingleResult(), is(3.0) );
|
||||
|
||||
//TODO: why does this fail??
|
||||
assertThat( session.createQuery("select avg(index(l)) from EntityOfLists eol join eol.listOfNumbers l group by eol")
|
||||
.getSingleResult(), is(0.5) );
|
||||
assertThat( session.createQuery("select avg(element(l)) from EntityOfLists eol join eol.listOfNumbers l group by eol")
|
||||
|
@ -154,7 +229,7 @@ public class FunctionTests {
|
|||
|
||||
assertThat( session.createQuery("select sum(index(m)) from EntityOfMaps eom join eom.numberByNumber m group by eom")
|
||||
.getSingleResult(), is(1L) );
|
||||
assertThat( session.createQuery("select sum(element(m))from EntityOfMaps eom join eom.numberByNumber m group by eom")
|
||||
assertThat( session.createQuery("select sum(element(m)) from EntityOfMaps eom join eom.numberByNumber m group by eom")
|
||||
.getSingleResult(), is(1.0) );
|
||||
|
||||
assertThat( session.createQuery("select avg(index(m)) from EntityOfMaps eom join eom.numberByNumber m group by eom")
|
||||
|
|
Loading…
Reference in New Issue