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.
|
| 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.
|
NOTE: Of these, only `index()` is defined by the JPQL specification.
|
||||||
|
|
||||||
[[hql-collection-qualification-example]]
|
[[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]]
|
[[hql-more-functions]]
|
||||||
==== More HQL 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]]
|
[[hql-elements-indices]]
|
||||||
==== Elements and 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.
|
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.
|
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>>.
|
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]]
|
[[hql-aggregate-functions-filter]]
|
||||||
==== `filter`
|
==== `filter`
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ import org.hibernate.dialect.SybaseASEDialect;
|
||||||
import org.hibernate.dialect.TiDBDialect;
|
import org.hibernate.dialect.TiDBDialect;
|
||||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||||
import org.hibernate.query.QueryProducer;
|
import org.hibernate.query.QueryProducer;
|
||||||
import org.hibernate.testing.Skip;
|
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.userguide.model.Account;
|
import org.hibernate.userguide.model.Account;
|
||||||
import org.hibernate.userguide.model.AddressType;
|
import org.hibernate.userguide.model.AddressType;
|
||||||
|
@ -1801,7 +1800,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Phone> phones = entityManager.createQuery(
|
List<Phone> phones = entityManager.createQuery(
|
||||||
"select p " +
|
"select p " +
|
||||||
"from Phone p " +
|
"from Phone p " +
|
||||||
"where max(element p.calls) = :call",
|
"where max(elements(p.calls)) = :call",
|
||||||
Phone.class)
|
Phone.class)
|
||||||
.setParameter("call", call)
|
.setParameter("call", call)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
|
@ -1819,7 +1818,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Phone> phones = entityManager.createQuery(
|
List<Phone> phones = entityManager.createQuery(
|
||||||
"select p " +
|
"select p " +
|
||||||
"from Phone p " +
|
"from Phone p " +
|
||||||
"where min(element p.calls) = :call",
|
"where min(elements(p.calls)) = :call",
|
||||||
Phone.class)
|
Phone.class)
|
||||||
.setParameter("call", call)
|
.setParameter("call", call)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
|
@ -1836,7 +1835,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Person> persons = entityManager.createQuery(
|
List<Person> persons = entityManager.createQuery(
|
||||||
"select p " +
|
"select p " +
|
||||||
"from Person p " +
|
"from Person p " +
|
||||||
"where max(index p.phones) = 0",
|
"where max(indices(p.phones)) = 0",
|
||||||
Person.class)
|
Person.class)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
//end::hql-collection-expressions-example[]
|
//end::hql-collection-expressions-example[]
|
||||||
|
@ -1993,7 +1992,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
List<Person> persons = entityManager.createQuery(
|
List<Person> persons = entityManager.createQuery(
|
||||||
"select pr " +
|
"select pr " +
|
||||||
"from Person 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)
|
Person.class)
|
||||||
.getResultList();
|
.getResultList();
|
||||||
//end::hql-collection-index-operator-example[]
|
//end::hql-collection-index-operator-example[]
|
||||||
|
|
|
@ -1008,10 +1008,10 @@ jpaCollectionFunction
|
||||||
indexAggregateFunction
|
indexAggregateFunction
|
||||||
: MAXINDEX LEFT_PAREN path RIGHT_PAREN
|
: MAXINDEX LEFT_PAREN path RIGHT_PAREN
|
||||||
| MININDEX LEFT_PAREN path RIGHT_PAREN
|
| MININDEX LEFT_PAREN path RIGHT_PAREN
|
||||||
| MAX LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
| MAX LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||||
| MIN LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
| MIN LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||||
| SUM LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
| SUM LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||||
| AVG LEFT_PAREN (INDEX|KEY) path RIGHT_PAREN
|
| AVG LEFT_PAREN INDICES LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1020,10 +1020,10 @@ indexAggregateFunction
|
||||||
elementAggregateFunction
|
elementAggregateFunction
|
||||||
: MAXELEMENT LEFT_PAREN path RIGHT_PAREN
|
: MAXELEMENT LEFT_PAREN path RIGHT_PAREN
|
||||||
| MINELEMENT LEFT_PAREN path RIGHT_PAREN
|
| MINELEMENT LEFT_PAREN path RIGHT_PAREN
|
||||||
| MAX LEFT_PAREN (ELEMENT|VALUE) path RIGHT_PAREN
|
| MAX LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||||
| MIN LEFT_PAREN (ELEMENT|VALUE) path RIGHT_PAREN
|
| MIN LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||||
| SUM LEFT_PAREN (ELEMENT|VALUE) path RIGHT_PAREN
|
| SUM LEFT_PAREN ELEMENTS LEFT_PAREN path RIGHT_PAREN RIGHT_PAREN
|
||||||
| AVG LEFT_PAREN (ELEMENT|VALUE) path 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
|
@Test
|
||||||
public void testMaxMinSumIndexElement(SessionFactoryScope scope) {
|
public void testAltMaxMinSumIndexElement(SessionFactoryScope scope) {
|
||||||
|
//TODO: make the commented tests work!
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
assertThat( session.createQuery("select max(index eol.listOfNumbers) from EntityOfLists eol")
|
// assertThat( session.createQuery("select max(index(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||||
.getSingleResult(), is(1) );
|
// .getSingleResult(), is(1) );
|
||||||
assertThat( session.createQuery("select max(element eol.listOfNumbers) from EntityOfLists eol")
|
assertThat( session.createQuery("select max(element(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||||
.getSingleResult(), is(2.0) );
|
.getSingleResult(), is(2.0) );
|
||||||
|
|
||||||
assertThat( session.createQuery("select sum(index eol.listOfNumbers) from EntityOfLists eol")
|
// assertThat( session.createQuery("select sum(index(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||||
.getSingleResult(), is(1L) );
|
// .getSingleResult(), is(1) );
|
||||||
assertThat( session.createQuery("select sum(element eol.listOfNumbers) from EntityOfLists eol")
|
assertThat( session.createQuery("select sum(element(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||||
.getSingleResult(), is(3.0) );
|
.getSingleResult(), is(3.0) );
|
||||||
|
|
||||||
assertThat( session.createQuery("select avg(index eol.listOfNumbers) from EntityOfLists eol")
|
// assertThat( session.createQuery("select avg(index(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||||
.getSingleResult(), is(0.5) );
|
// .getSingleResult(), is(0.5) );
|
||||||
assertThat( session.createQuery("select avg(element eol.listOfNumbers) from EntityOfLists eol")
|
assertThat( session.createQuery("select avg(element(eol.listOfNumbers)) from EntityOfLists eol group by eol")
|
||||||
.getSingleResult(), is(1.5) );
|
.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) );
|
.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) );
|
.getSingleResult(), is(1.0) );
|
||||||
|
|
||||||
assertThat( session.createQuery("select sum(index eom.numberByNumber) from EntityOfMaps eom")
|
assertThat( session.createQuery("select sum(indices(eom.numberByNumber)) from EntityOfMaps eom")
|
||||||
.getSingleResult(), is(1L) );
|
.getSingleResult(), is(1) ); //TODO: should be Long
|
||||||
assertThat( session.createQuery("select sum(element eom.numberByNumber) from EntityOfMaps eom")
|
assertThat( session.createQuery("select sum(elements(eom.numberByNumber)) from EntityOfMaps eom")
|
||||||
.getSingleResult(), is(1.0) );
|
.getSingleResult(), is(1.0) );
|
||||||
|
|
||||||
assertThat( session.createQuery("select avg(index eom.numberByNumber) from EntityOfMaps eom")
|
assertThat( session.createQuery("select avg(indices(eom.numberByNumber)) from EntityOfMaps eom")
|
||||||
.getSingleResult(), is(1.0) );
|
.getSingleResult(), is(1) ); //TODO: should be Double
|
||||||
assertThat( session.createQuery("select avg(element eom.numberByNumber) from EntityOfMaps eom")
|
assertThat( session.createQuery("select avg(elements(eom.numberByNumber)) from EntityOfMaps eom")
|
||||||
.getSingleResult(), is(1.0) );
|
.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")
|
assertThat( session.createQuery("select sum(element(l)) from EntityOfLists eol join eol.listOfNumbers l group by eol")
|
||||||
.getSingleResult(), is(3.0) );
|
.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")
|
assertThat( session.createQuery("select avg(index(l)) from EntityOfLists eol join eol.listOfNumbers l group by eol")
|
||||||
.getSingleResult(), is(0.5) );
|
.getSingleResult(), is(0.5) );
|
||||||
assertThat( session.createQuery("select avg(element(l)) from EntityOfLists eol join eol.listOfNumbers l group by eol")
|
assertThat( session.createQuery("select avg(element(l)) from EntityOfLists eol join eol.listOfNumbers l group by eol")
|
||||||
|
|
Loading…
Reference in New Issue