You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/12/25 15:31:33 UTC
[groovy] branch master updated: Cache the result of `partitionby`
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 ed88dab Cache the result of `partitionby`
ed88dab is described below
commit ed88dabbf1592d4ed6d3e0db3a6cb1f3d1389394
Author: Daniel Sun <su...@apache.org>
AuthorDate: Fri Dec 25 23:31:05 2020 +0800
Cache the result of `partitionby`
---
.../ginq/provider/collection/GinqAstWalker.groovy | 6 ++++-
.../provider/collection/runtime/Queryable.java | 21 +++++++++++++----
.../collection/runtime/QueryableCollection.java | 19 +++++++++++----
.../collection/runtime/WindowDefinition.java | 14 +++++++++++
.../collection/runtime/WindowDefinitionImpl.java | 27 ++++++++++++++++++++++
.../test/org/apache/groovy/ginq/GinqTest.groovy | 10 ++++++++
.../runtime/QueryableCollectionTest.groovy | 25 ++++++--------------
7 files changed, 94 insertions(+), 28 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 8516e37..5ad13c2 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
@@ -764,7 +764,11 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, SyntaxErrorReportable
argumentExpressionList << orderCtorCallExpression
}
- callX(new ClassExpression(WINDOW_DEFINITION_TYPE), 'of', args(argumentExpressionList))
+ callX(
+ callX(new ClassExpression(WINDOW_DEFINITION_TYPE), 'of', args(argumentExpressionList)),
+ 'setId',
+ new ConstantExpression(argumentListExpression.text)
+ )
}
private int windowQueryableNameSeq = 0
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 10c0618..202494f 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
@@ -46,6 +46,21 @@ public interface Queryable<T> {
Object NULL = new Object();
/**
+ * Represents the empty Queryable instance
+ */
+ Queryable EMPTY_QUERYABLE = from(new Object[0]);
+
+ /**
+ * Returns the empty Queryable instance
+ *
+ * @param <T> the type of element
+ * @return the empty Queryable instance
+ */
+ static <T> Queryable<T> emptyQueryable() {
+ return (Queryable<T>) EMPTY_QUERYABLE;
+ }
+
+ /**
* Factory method to create {@link Queryable} instance
*
* @param iterable iterable object, e.g. {@link List}
@@ -210,21 +225,19 @@ public interface Queryable<T> {
*
* @param classifier the classifier for group by
* @param having the filter condition
- * @param <K> the type of group key
* @return the result of group by
* @since 4.0.0
*/
- <K> Queryable<Tuple2<K, Queryable<T>>> groupBy(Function<? super T, ? extends K> classifier, Predicate<? super Tuple2<? extends K, Queryable<? extends T>>> having);
+ Queryable<Tuple2<?, Queryable<T>>> groupBy(Function<? super T, ?> classifier, Predicate<? super Tuple2<?, Queryable<? extends T>>> having);
/**
* Group by {@link Queryable} instance without {@code having} clause, similar to SQL's {@code group by}
*
* @param classifier the classifier for group by
- * @param <K> the type of group key
* @return the result of group by
* @since 4.0.0
*/
- default <K> Queryable<Tuple2<K, Queryable<T>>> groupBy(Function<? super T, ? extends K> classifier) {
+ default Queryable<Tuple2<?, Queryable<T>>> groupBy(Function<? super T, ?> classifier) {
return groupBy(classifier, null);
}
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 62df46b..c06eff1 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
@@ -38,6 +38,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -207,8 +208,8 @@ class QueryableCollection<T> implements Queryable<T>, Serializable {
}
@Override
- public <K> Queryable<Tuple2<K, Queryable<T>>> groupBy(Function<? super T, ? extends K> classifier, Predicate<? super Tuple2<? extends K, Queryable<? extends T>>> having) {
- Stream<Tuple2<K, Queryable<T>>> stream =
+ public Queryable<Tuple2<?, Queryable<T>>> groupBy(Function<? super T, ?> classifier, Predicate<? super Tuple2<?, Queryable<? extends T>>> having) {
+ Stream<Tuple2<?, Queryable<T>>> stream =
this.stream()
.collect(Collectors.groupingBy(classifier, Collectors.toList()))
.entrySet().stream()
@@ -486,11 +487,18 @@ class QueryableCollection<T> implements Queryable<T>, Serializable {
public <U extends Comparable<? super U>> Window<T> over(T currentRecord, WindowDefinition<T, U> windowDefinition) {
this.makeReusable();
Queryable<T> partition =
- this.groupBy(windowDefinition.partitionBy()) // TODO cache the group result
+ partitionCache.computeIfAbsent(windowDefinition, wd -> {
+ final Queryable<Tuple2<?, Queryable<T>>> q = this.groupBy(wd.partitionBy());
+ if (q instanceof QueryableCollection) {
+ ((QueryableCollection) q).makeReusable();
+ }
+ return q;
+ })
.where(e -> QueryableHelper.isEqual(e.getV1(), windowDefinition.partitionBy().apply(currentRecord)))
.select((e, q) -> e.getV2())
- .toList()
- .get(0);
+ .stream()
+ .findFirst()
+ .orElse(Queryable.emptyQueryable());
return new WindowImpl<>(currentRecord, partition, windowDefinition);
}
@@ -563,6 +571,7 @@ class QueryableCollection<T> implements Queryable<T>, Serializable {
return AsciiTableMaker.makeAsciiTable(this);
}
+ private final Map<WindowDefinition<T, ?>, Queryable<Tuple2<?, Queryable<T>>>> partitionCache = new ConcurrentHashMap<>(4);
private Stream<T> sourceStream;
private volatile Iterable<T> sourceIterable;
private final ReadWriteLock rwl = new ReentrantReadWriteLock();
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinition.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinition.java
index 96162d4..f05939f 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinition.java
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinition.java
@@ -124,4 +124,18 @@ public interface WindowDefinition<T, U extends Comparable<? super U>> {
default Tuple2<? extends U, ? extends U> range() {
return null;
}
+
+ /**
+ * Get the id of window definition
+ *
+ * @return the id of window definition
+ */
+ String getId();
+
+ /**
+ * Set the id of window definition
+ *
+ * @param id the id of window definition
+ */
+ WindowDefinition<T, U> setId(String id);
}
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinitionImpl.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinitionImpl.java
index 5fdf373..7d1c2fd 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinitionImpl.java
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowDefinitionImpl.java
@@ -20,6 +20,7 @@ package org.apache.groovy.ginq.provider.collection.runtime;
import groovy.lang.Tuple2;
+import java.util.Objects;
import java.util.function.Function;
/**
@@ -79,4 +80,30 @@ class WindowDefinitionImpl<T, U extends Comparable<? super U>> implements Window
public Tuple2<? extends U, ? extends U> range() {
return range;
}
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public WindowDefinition<T, U> setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof WindowDefinitionImpl)) return false;
+ WindowDefinitionImpl<?, ?> that = (WindowDefinitionImpl<?, ?>) o;
+ return Objects.equals(id, that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ private String id;
}
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 26aba07..cfccb5a 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
@@ -4895,6 +4895,16 @@ class GinqTest {
'''
}
+ @Test
+ void "testGinq - window - 23"() {
+ assertGinqScript '''
+ assert [[2, 3, 1], [1, 2, null], [3, null, 2]] == GQ {
+ from n in [2, 1, 3]
+ select n, (lead(n) over(orderby n)), (lag(n) over(orderby n))
+ }.toList()
+ '''
+ }
+
private static void assertGinqScript(String script) {
String deoptimizedScript = script.replaceAll(/\bGQ\s*[{]/, 'GQ(optimize:false) {')
List<String> scriptList = [deoptimizedScript, script]
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 716c99a..1a3a7b1 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
@@ -1010,33 +1010,25 @@ class QueryableCollectionTest {
}
@Test
+ @CompileDynamic
void testWindow1() {
def nums = [9, 3, 6]
- def windowDefinition = new WindowDefinition<Integer, Integer>() {
- @Override
- Queryable.Order<? super Integer, ? extends Integer> orderBy() {
- return new Queryable.Order<Integer, Integer>(e -> e, true)
- }
- }
+ def windowDefinition = WindowDefinition.of(new Queryable.Order<Integer, Integer>(e -> e, true))
def result = from(nums).over(6, windowDefinition).rowNumber()
assert 1 == result
}
@Test
+ @CompileDynamic
void testWindow2() {
def n9 = new Integer(9)
def n31 = new Integer(3)
def n32 = new Integer(3)
def n6 = new Integer(6)
def nums = [n9, n31, n32, n6]
- def windowDefinition = new WindowDefinition<Integer, Integer>() {
- @Override
- Queryable.Order<? super Integer, ? extends Integer> orderBy() {
- return new Queryable.Order<Integer, Integer>(e -> new NamedTuple<>([e, e + 1], ['e', 'e + 1']), true)
- }
- }
+ def windowDefinition = WindowDefinition.of(new Queryable.Order<Integer, Integer>(e -> new NamedTuple<>([e, e + 1], ['e', 'e + 1']), true))
assert 0 == from(nums).over(n31, windowDefinition).rowNumber()
assert 1 == from(nums).over(n32, windowDefinition).rowNumber()
@@ -1048,18 +1040,14 @@ class QueryableCollectionTest {
}
@Test
+ @CompileDynamic
void testWindow3() {
def n9 = new Integer(9)
def n31 = new Integer(3)
def n32 = new Integer(3)
def n6 = new Integer(6)
def nums = [n9, n31, n32, n6]
- def windowDefinition = new WindowDefinition<Integer, Integer>() {
- @Override
- Queryable.Order<? super Integer, ? extends Integer> orderBy() {
- return new Queryable.Order<Integer, Integer>(e -> new NamedTuple<>([e, e + 1], ['e', 'e + 1']), true)
- }
- }
+ def windowDefinition = WindowDefinition.of(new Queryable.Order<Integer, Integer>(e -> new NamedTuple<>([e, e + 1], ['e', 'e + 1']), true))
assert !from(nums).over(n31, windowDefinition).lag(e -> e)
assert 3 == from(nums).over(n32, windowDefinition).lag(e -> e)
@@ -1073,6 +1061,7 @@ class QueryableCollectionTest {
}
@Test
+ @CompileDynamic
void testWindow4() {
def n9 = new Integer(9)
def n31 = new Integer(3)