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 185e3f5  Tweak row number further
185e3f5 is described below

commit 185e3f56162202b433de7bc72b7af4918d0143c7
Author: Daniel Sun <[email protected]>
AuthorDate: Mon Dec 28 12:40:17 2020 +0800

    Tweak row number further
---
 .../ginq/provider/collection/GinqAstWalker.groovy  |  3 ++-
 .../provider/collection/runtime/Queryable.java     | 14 +++++++++++-
 .../collection/runtime/QueryableCollection.java    | 23 +++++++++++++++-----
 .../provider/collection/runtime/WindowImpl.java    | 25 ++++++++++++++++------
 .../test/org/apache/groovy/ginq/GinqTest.groovy    | 20 +++++++++++++++++
 5 files changed, 71 insertions(+), 14 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 aadab92..c2c0d68 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
@@ -737,7 +737,7 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, 
SyntaxErrorReportable
 
                                 def 
windowDefinitionFactoryMethodCallExpression = 
constructWindowDefinitionFactoryMethodCallExpression(expression, 
dataSourceExpression)
                                 Expression newObjectExpression = callX(wqVar, 
'over', args(
-                                        currentRecordVar,
+                                        callX(TUPLE_TYPE, 'tuple', 
args(currentRecordVar, callX(varX(rowNumberName), 'get'))),
                                         
windowDefinitionFactoryMethodCallExpression
                                 ))
                                 result = callX(
@@ -1371,6 +1371,7 @@ class GinqAstWalker implements 
GinqAstVisitor<Expression>, SyntaxErrorReportable
     private static final ClassNode WINDOW_DEFINITION_TYPE = 
makeWithoutCaching(WindowDefinition.class)
     private static final ClassNode ROWBOUND_TYPE = makeCached(RowBound.class)
     private static final ClassNode ATOMIC_LONG_TYPE = 
makeCached(AtomicLong.class)
+    private static final ClassNode TUPLE_TYPE = makeWithoutCaching(Tuple.class)
 
     private static final List<String> ORDER_OPTION_LIST = Arrays.asList('asc', 
'desc')
     private static final String FUNCTION_COUNT = 'count'
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 8021955..1279214 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
@@ -254,6 +254,18 @@ public interface Queryable<T> {
     <U extends Comparable<? super U>> Queryable<T> orderBy(Order<? super T, ? 
extends U>... orders);
 
     /**
+     * Sort {@link Queryable} instance, similar to SQL's {@code order by}
+     *
+     * @param orders the order rules for sorting
+     * @param <U> the type of field to sort
+     * @return the result of order by
+     * @since 4.0.0
+     */
+    default <U extends Comparable<? super U>> Queryable<T> orderBy(List<? 
extends Order<? super T, ? extends U>> orders) {
+        return orderBy(orders.toArray(Order.EMPTY_ARRAY));
+    }
+
+    /**
      * Paginate {@link Queryable} instance, similar to MySQL's {@code limit}
      *
      * @param offset the start position
@@ -450,7 +462,7 @@ public interface Queryable<T> {
      * @param <U> the type of window value
      * @return the window
      */
-    <U extends Comparable<? super U>> Window<T> over(T currentRecord, 
WindowDefinition<T, U> windowDefinition);
+    <U extends Comparable<? super U>> Window<T> over(Tuple2<T, Long> 
currentRecord, WindowDefinition<T, U> windowDefinition);
 
     /**
      * Represents an order rule
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 9088ea8..73130d7 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
@@ -18,6 +18,7 @@
  */
 package org.apache.groovy.ginq.provider.collection.runtime;
 
+import groovy.lang.Tuple;
 import groovy.lang.Tuple2;
 import groovy.transform.Internal;
 import org.apache.groovy.internal.util.Supplier;
@@ -70,6 +71,12 @@ class QueryableCollection<T> implements Queryable<T>, 
Serializable {
         this.sourceStream = sourceStream;
     }
 
+    protected List<Tuple2<T, Long>> listWithIndex;
+    QueryableCollection(Queryable<Tuple2<T, Long>> queryableWithIndex) {
+        this(queryableWithIndex.toList().stream().map(e -> 
e.getV1()).collect(Collectors.toList()));
+        this.listWithIndex = queryableWithIndex.toList();
+    }
+
     public Iterator<T> iterator() {
         readLock.lock();
         try {
@@ -484,17 +491,23 @@ class QueryableCollection<T> implements Queryable<T>, 
Serializable {
     }
 
     @Override
-    public <U extends Comparable<? super U>> Window<T> over(T currentRecord, 
WindowDefinition<T, U> windowDefinition) {
+    public <U extends Comparable<? super U>> Window<T> over(Tuple2<T, Long> 
currentRecord, WindowDefinition<T, U> windowDefinition) {
         this.makeReusable();
-        Queryable<T> partition =
+        Queryable<Tuple2<T, Long>> partition =
                 partitionCache.computeIfAbsent(windowDefinition, wd -> {
-                    final Queryable<Tuple2<?, Queryable<T>>> q = 
this.groupBy(wd.partitionBy());
+                    long[] rn = new long[] { 1L };
+                    List<Tuple2<T, Long>> listWithIndex =
+                            this.toList().stream()
+                                    .map(e -> Tuple.tuple(e, rn[0]++))
+                                    .collect(Collectors.toList());
+
+                    final Queryable<Tuple2<?, Queryable<Tuple2<T, Long>>>> q = 
from(listWithIndex).groupBy(wd.partitionBy().compose(e -> e.getV1()));
                     if (q instanceof QueryableCollection) {
                         ((QueryableCollection) q).makeReusable();
                     }
                     return q;
                 })
-                        .where(e -> Objects.equals(e.getV1(), 
windowDefinition.partitionBy().apply(currentRecord)))
+                        .where(e -> Objects.equals(e.getV1(), 
windowDefinition.partitionBy().apply(currentRecord.getV1())))
                         .select((e, q) -> e.getV2())
                         .stream()
                         .findFirst()
@@ -571,7 +584,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 final Map<WindowDefinition<T, ?>, Queryable<Tuple2<?, 
Queryable<Tuple2<T, Long>>>>> 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/WindowImpl.java
 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowImpl.java
index 27afed2..10b0670 100644
--- 
a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowImpl.java
+++ 
b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/WindowImpl.java
@@ -18,8 +18,11 @@
  */
 package org.apache.groovy.ginq.provider.collection.runtime;
 
+import groovy.lang.Tuple2;
+
 import java.util.List;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * Represents window which stores elements used by window functions
@@ -30,22 +33,22 @@ import java.util.function.Function;
  */
 class WindowImpl<T, U extends Comparable<? super U>> extends 
QueryableCollection<T> implements Window<T> {
     private static final long serialVersionUID = -3458969297047398621L;
-    private final T currentRecord;
+    private final Tuple2<T, Long> currentRecord;
     private final long index;
     private final WindowDefinition<T, U> windowDefinition;
     private final U value;
     private final Function<? super T, ? extends U> keyExtractor;
 
-    WindowImpl(T currentRecord, Queryable<T> partition, WindowDefinition<T, U> 
windowDefinition) {
-        
super(partition.orderBy(windowDefinition.orderBy().toArray(Order.EMPTY_ARRAY)).toList());
+    WindowImpl(Tuple2<T, Long> currentRecord, Queryable<Tuple2<T, Long>> 
partition, WindowDefinition<T, U> windowDefinition) {
+        super(partition.orderBy(composeOrders(windowDefinition)));
         this.currentRecord = currentRecord;
         this.windowDefinition = windowDefinition;
 
-        List<T> sortedList = this.toList();
+        List<Tuple2<T, Long>> sortedList = listWithIndex;
         final List<Order<? super T, ? extends U>> order = 
windowDefinition.orderBy();
         if (null != order && 1 == order.size()) {
             this.keyExtractor = order.get(0).getKeyExtractor();
-            this.value = keyExtractor.apply(currentRecord);
+            this.value = keyExtractor.apply(currentRecord.getV1());
         } else {
             this.keyExtractor = null;
             this.value = null;
@@ -53,7 +56,8 @@ class WindowImpl<T, U extends Comparable<? super U>> extends 
QueryableCollection
 
         int tmpIndex = -1;
         for (int i = 0, n = sortedList.size(); i < n; i++) {
-            if (currentRecord == sortedList.get(i)) {
+            final Tuple2<T, Long> record = sortedList.get(i);
+            if (currentRecord.getV1() == record.getV1() && 
currentRecord.getV2().equals(record.getV2())) {
                 tmpIndex = i;
                 break;
             }
@@ -62,6 +66,13 @@ class WindowImpl<T, U extends Comparable<? super U>> extends 
QueryableCollection
         this.index = tmpIndex;
     }
 
+    private static <T, U extends Comparable<? super U>> List<Order<? super 
Tuple2<T, Long>, ? extends U>> composeOrders(WindowDefinition<T, U> 
windowDefinition) {
+        List<Order<? super Tuple2<T, Long>, ? extends U>> result = 
windowDefinition.orderBy().stream()
+                .map(order -> new Order<Tuple2<T, Long>, U>(t -> 
order.getKeyExtractor().apply(t.getV1()), order.isAsc()))
+                .collect(Collectors.toList());
+        return result;
+    }
+
     @Override
     public long rowNumber() {
         return index;
@@ -71,7 +82,7 @@ class WindowImpl<T, U extends Comparable<? super U>> extends 
QueryableCollection
     public <V> V lead(Function<? super T, ? extends V> extractor, long lead, V 
def) {
         V field;
         if (0 == lead) {
-            field = extractor.apply(currentRecord);
+            field = extractor.apply(currentRecord.getV1());
         } else if (0 <= index + lead && index + lead < this.size()) {
             field = extractor.apply(this.toList().get((int) index + (int) 
lead));
         } else {
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 b62a14e..1a0b668 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
@@ -5203,6 +5203,26 @@ class GinqTest {
         '''
     }
 
+    @Test
+    void "testGinq - window - 47"() {
+        assertGinqScript '''
+            assert [[1, 0], [1, 1], [2, 2], [2, 3]] == GQ {
+                from n in [1, 1, 2, 2]
+                select n, (rowNumber() over(orderby n))
+            }.toList()
+        '''
+    }
+
+    @Test
+    void "testGinq - window - 48"() {
+        assertGinqScript '''
+            assert [['aa', 0], ['aa', 1], ['bb', 2], ['bb', 3]] == GQ {
+                from s in ['aa', 'aa', 'bb', 'bb']
+                select s, (rowNumber() over(orderby s))
+            }.toList()
+        '''
+    }
+
     private static void assertGinqScript(String script) {
         String deoptimizedScript = script.replaceAll(/\bGQ\s*[{]/, 
'GQ(optimize:false) {')
         List<String> scriptList = [deoptimizedScript, script]

Reply via email to