This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 5ddf871 Support built-in aggregate function `median`
5ddf871 is described below
commit 5ddf871fc03f07acbf2ff6f4a785d575daee934c
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Dec 13 18:24:03 2020 +0800
Support built-in aggregate function `median`
---
.../ginq/provider/collection/GinqAstWalker.groovy | 3 +-
.../provider/collection/runtime/Queryable.java | 9 ++++
.../collection/runtime/QueryableCollection.java | 28 +++++++++++-
.../groovy-ginq/src/spec/doc/ginq-userguide.adoc | 9 +++-
.../test/org/apache/groovy/ginq/GinqTest.groovy | 17 ++++++-
.../runtime/QueryableCollectionTest.groovy | 52 ++++++++++++++++++++++
6 files changed, 112 insertions(+), 6 deletions(-)
diff --git
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
index 2d0819e..bdb1678 100644
---
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
+++
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy
@@ -1077,8 +1077,9 @@ class GinqAstWalker implements
GinqAstVisitor<Expression>, SyntaxErrorReportable
private static final String FUNCTION_MAX = 'max'
private static final String FUNCTION_SUM = 'sum'
private static final String FUNCTION_AVG = 'avg'
+ private static final String FUNCTION_MEDIAN = 'median'
private static final String FUNCTION_AGG = 'agg'
- private static final List<String> AGG_FUNCTION_NAME_LIST =
[FUNCTION_COUNT, FUNCTION_MIN, FUNCTION_MAX, FUNCTION_SUM, FUNCTION_AVG,
FUNCTION_AGG]
+ private static final List<String> AGG_FUNCTION_NAME_LIST =
[FUNCTION_COUNT, FUNCTION_MIN, FUNCTION_MAX, FUNCTION_SUM, FUNCTION_AVG,
FUNCTION_MEDIAN, FUNCTION_AGG]
private static final String NAMEDRECORD_CLASS_NAME = NamedRecord.class.name
diff --git
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java
index a7c9608..7cc2bb8 100644
---
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java
+++
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/Queryable.java
@@ -375,6 +375,15 @@ public interface Queryable<T> {
<U extends Comparable<? super U>> U max(Function<? super T, ? extends U>
mapper);
/**
+ * Aggregate function {@code median}, similar to SQL's {@code median}
+ *
+ * @param mapper choose the field to median
+ * @return median result
+ * @since 4.0.0
+ */
+ BigDecimal median(Function<? super T, ? extends Number> mapper);
+
+ /**
* The most powerful aggregate function in GINQ, it will receive the
grouped result({@link Queryable} instance) and apply any processing
*
* @param mapper map the grouped result({@link Queryable} instance) to
aggregate result
diff --git
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java
index 17cd911..c3169e0 100644
---
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java
+++
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollection.java
@@ -58,6 +58,7 @@ import static
org.apache.groovy.ginq.provider.collection.runtime.Queryable.from;
@Internal
class QueryableCollection<T> implements Queryable<T>, Serializable {
private static final long serialVersionUID = -5067092453136522893L;
+ public static final BigDecimal BD_TWO = BigDecimal.valueOf(2);
private Iterable<T> sourceIterable;
private Stream<T> sourceStream;
@@ -314,7 +315,7 @@ class QueryableCollection<T> implements Queryable<T>,
Serializable {
@Override
public BigDecimal avg(Function<? super T, ? extends Number> mapper) {
- Object[] result = agg(q -> this.stream()
+ Object[] result = agg(q -> q.stream()
.map(mapper)
.filter(Objects::nonNull)
.map(NumberMath::toBigDecimal)
@@ -347,6 +348,31 @@ class QueryableCollection<T> implements Queryable<T>,
Serializable {
}
@Override
+ public BigDecimal median(Function<? super T, ? extends Number> mapper) {
+ List<BigDecimal> sortedNumList = agg(q -> q.stream()
+ .map(mapper)
+ .filter(Objects::nonNull)
+ .map(NumberMath::toBigDecimal)
+ .sorted()
+ .collect(Collectors.toList())
+ );
+
+ int size = sortedNumList.size();
+ if (0 == size) {
+ return null;
+ }
+
+ int index = size / 2;
+ BigDecimal num = sortedNumList.get(index);
+
+ if (0 == size % 2) {
+ return num.add(sortedNumList.get(index - 1)).divide(BD_TWO);
+ }
+
+ return num;
+ }
+
+ @Override
public <U> U agg(Function<? super Queryable<? extends T>, ? extends U>
mapper) {
return mapper.apply(this);
}
diff --git a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
index 78dcaf5..1d3b82d 100644
--- a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
+++ b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
@@ -281,9 +281,9 @@
include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_grouping_09,in
===== Aggregate Functions
GINQ supports some built-in aggregate functions, e.g.
-`count`, `min`, `max`, `sum`, `avg` and the most powerful function `agg`.
+`count`, `min`, `max`, `sum`, `avg`, `median` and the most powerful function
`agg`.
[NOTE]
-`count(...)`, `min(...)` and `max(...)` just operate on non-`null` values,
+`count(...)`, `min(...)`, `max(...)`, `avg(...)` and `median(...)` just
operate on non-`null` values,
and `count()` is similar to `count(*)` in SQL.
[source, sql]
----
@@ -312,6 +312,11 @@
include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_grouping_11,in
[source, sql]
----
+include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_grouping_12,indent=0]
+----
+
+[source, sql]
+----
include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_grouping_03,indent=0]
----
[NOTE]
diff --git
a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
index f8944ee..3ba1932 100644
---
a/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
+++
b/subprojects/groovy-ginq/src/spec/test/org/apache/groovy/ginq/GinqTest.groovy
@@ -2793,6 +2793,19 @@ class GinqTest {
}
@Test
+ void "testGinq - from groupby select - 30"() {
+ assertGinqScript '''
+ assert [[1, 1], [3, 3], [6, 6]] == GQ {
+// tag::ginq_grouping_12[]
+ from n in [1, 1, 3, 3, 6, 6, 6]
+ groupby n
+ select n, median(n)
+// end::ginq_grouping_12[]
+ }.toList()
+ '''
+ }
+
+ @Test
void "testGinq - from where groupby select - 1"() {
assertGinqScript '''
assert [[1, 2], [6, 3]] == GQ {
@@ -3792,9 +3805,9 @@ class GinqTest {
void "testGinq - agg function - 1"() {
assertGinqScript '''
// tag::ginq_aggfunction_01[]
- assert [[1, 3, 2, 6, 3, 3, 6]] == GQ {
+ assert [[1, 3, 2, 2, 6, 3, 3, 6]] == GQ {
from n in [1, 2, 3]
- select min(n), max(n), avg(n), sum(n), count(n), count(),
+ select min(n), max(n), avg(n), median(n), sum(n), count(n),
count(),
agg(_g.stream().map(r -> r.n).reduce(BigDecimal.ZERO,
BigDecimal::add))
}.toList()
// end::ginq_aggfunction_01[]
diff --git
a/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollectionTest.groovy
b/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollectionTest.groovy
index 93e64ac..7a9bfa7 100644
---
a/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollectionTest.groovy
+++
b/subprojects/groovy-ginq/src/test/groovy/org/apache/groovy/ginq/provider/collection/runtime/QueryableCollectionTest.groovy
@@ -825,6 +825,58 @@ class QueryableCollectionTest {
}
@Test
+ @CompileDynamic
+ void testGroupBySelect10() {
+ def nums = [1, 3, 2]
+ def result =
+ from(nums).groupBy(e -> 1)
+ .select(e ->
+ e.v2.median(n -> n)
+ )
+ .toList()
+ assert [2] == result
+ }
+
+ @Test
+ @CompileDynamic
+ void testGroupBySelect11() {
+ def nums = [1, 3, 2, 4]
+ def result =
+ from(nums).groupBy(e -> 1)
+ .select(e ->
+ e.v2.median(n -> n)
+ )
+ .toList()
+ assert [2.5] == result
+ }
+
+ @Test
+ @CompileDynamic
+ void testGroupBySelect12() {
+ def nums = [1]
+ def result =
+ from(nums).groupBy(e -> 1)
+ .select(e ->
+ e.v2.median(n -> n)
+ )
+ .toList()
+ assert [1] == result
+ }
+
+ @Test
+ @CompileDynamic
+ void testGroupBySelect13() {
+ def nums = []
+ def result =
+ from(nums).groupBy(e -> 1)
+ .select(e ->
+ e.v2.median(n -> n)
+ )
+ .toList()
+ assert [] == result
+ }
+
+ @Test
void testOrderBy() {
Person daniel = new Person('Daniel', 35)
Person peter = new Person('Peter', 10)