[groovy] branch master updated: Minor tweak: support `exists` in GINQ

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[groovy] branch master updated: Minor tweak: support `exists` in GINQ

Daniel.Sun
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 fefc78b  Minor tweak: support `exists` in GINQ
fefc78b is described below

commit fefc78b2c7c6a3fbafedbe9f23faf4055386d791
Author: Daniel Sun <[hidden email]>
AuthorDate: Sun Nov 22 01:12:39 2020 +0800

    Minor tweak: support `exists` in GINQ
---
 .../org/apache/groovy/ginq/dsl/GinqAstBuilder.java | 13 ++++++++-
 .../ginq/provider/collection/GinqAstWalker.groovy  | 33 +++++++++++++++------
 .../provider/collection/runtime/Queryable.java     |  9 ++++++
 .../groovy-ginq/src/spec/doc/ginq-userguide.adoc   | 12 ++++++++
 .../test/org/apache/groovy/ginq/GinqTest.groovy    | 34 ++++++++++++++++++++++
 5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java
index 8125f3a..9439215 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/dsl/GinqAstBuilder.java
@@ -79,7 +79,7 @@ public class GinqAstBuilder extends CodeVisitorSupport implements SyntaxErrorRep
 
     private static final Set<String> KEYWORD_SET = new HashSet<>(Arrays.asList(
             "from", "innerjoin", "leftjoin", "rightjoin", "fulljoin", "crossjoin",
-            "where", "on", "having", "groupby", "orderby", "limit", "select"));
+            "where", "on", "having", "exists", "groupby", "orderby", "limit", "select"));
 
     @Override
     public void visitMethodCallExpression(MethodCallExpression call) {
@@ -178,6 +178,17 @@ public class GinqAstBuilder extends CodeVisitorSupport implements SyntaxErrorRep
             return;
         }
 
+        if ("exists".equals(methodName)) {
+            if (null != latestGinqExpression) {
+                ArgumentListExpression argumentListExpression = (ArgumentListExpression) call.getArguments();
+                if (argumentListExpression.getExpressions().isEmpty() && isSelectMethodCallExpression(call.getObjectExpression())) {
+                    call.setObjectExpression(latestGinqExpression);
+                    // use the nested ginq and clear it
+                    latestGinqExpression = null;
+                }
+            }
+        }
+
         if ("groupby".equals(methodName)) {
             GroupExpression groupExpression = new GroupExpression(call.getArguments());
             groupExpression.setSourcePosition(call.getMethod());
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 5be87a5..3bc7f9c 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
@@ -269,16 +269,31 @@ class GinqAstWalker implements GinqAstVisitor<Expression>, SyntaxErrorReportable
         Expression fromMethodCallExpression = whereExpression.getNodeMetaData(__METHOD_CALL_RECEIVER)
         Expression filterExpr = whereExpression.getFilterExpr()
 
-        filterExpr = filterExpr.transformExpression(new ExpressionTransformer() {
-            @Override
-            Expression transform(Expression expression) {
-                if (expression instanceof AbstractGinqExpression) {
-                    return callX((Expression) GinqAstWalker.this.visit((AbstractGinqExpression) expression), "toList")
-                }
+        // construct the `ListExpression` instance to transform `filterExpr` in the same time
+        filterExpr = ((ListExpression) new ListExpression(Collections.singletonList(filterExpr)).transformExpression(
+                new ExpressionTransformer() {
+                    @Override
+                    Expression transform(Expression expression) {
+                        if (expression instanceof AbstractGinqExpression) {
+                            def ginqExpression = GinqAstWalker.this.visit((AbstractGinqExpression) expression)
+                            return ginqExpression
+                        }
 
-                return expression.transformExpression(this)
-            }
-        })
+                        if (expression instanceof BinaryExpression) {
+                            if (expression.operation.type == Types.KEYWORD_IN) {
+                                if (expression.rightExpression instanceof AbstractGinqExpression) {
+                                    expression.rightExpression =
+                                            callX(GinqAstWalker.this.visit((AbstractGinqExpression) expression.rightExpression),
+                                                    "toList")
+                                    return expression
+                                }
+                            }
+                        }
+
+                        return expression.transformExpression(this)
+                    }
+                }
+        )).getExpression(0)
 
         def whereMethodCallExpression = callXWithLambda(fromMethodCallExpression, "where", dataSourceExpression, filterExpr)
         whereMethodCallExpression.setSourcePosition(whereExpression)
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 e9d71f6..29f9b0e 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
@@ -214,6 +214,15 @@ public interface Queryable<T> {
     <U> Queryable<U> select(Function<? super T, ? extends U> mapper);
 
     /**
+     * Check if the result is empty, similar to SQL's {@code exists}
+     *
+     * @return the result of checking, {@code true} if result is not empty, otherwise {@code false}
+     */
+    default boolean exists() {
+        return stream().count() > 0;
+    }
+
+    /**
      * Eliminate duplicated records, similar to SQL's {@code distinct}
      *
      * @return the distinct result
diff --git a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
index 27deb0d..a7aec83 100644
--- a/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
+++ b/subprojects/groovy-ginq/src/spec/doc/ginq-userguide.adoc
@@ -107,6 +107,18 @@ include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_projection_01,
 include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_filtering_01,indent=0]
 ----
 
+===== Exists
+[source, sql]
+----
+include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_filtering_02,indent=0]
+----
+
+===== Not Exists
+[source, sql]
+----
+include::../test/org/apache/groovy/ginq/GinqTest.groovy[tags=ginq_filtering_03,indent=0]
+----
+
 ==== Joining
 [source, sql]
 ----
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 599e4a6..8a36357 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
@@ -2527,4 +2527,38 @@ class GinqTest {
             }.toList()
         '''
     }
+
+    @Test
+    void "testGinq - exists - 1"() {
+        assertScript '''
+            assert [2, 3] == GQ {
+// tag::ginq_filtering_02[]
+                from n in [1, 2, 3]
+                where (
+                    from m in [2, 3]
+                    where m == n
+                    select m
+                ).exists()
+                select n
+// end::ginq_filtering_02[]
+            }.toList()
+        '''
+    }
+
+    @Test
+    void "testGinq - not exists - 1"() {
+        assertScript '''
+            assert [1] == GQ {
+// tag::ginq_filtering_03[]
+                from n in [1, 2, 3]
+                where !(
+                    from m in [2, 3]
+                    where m == n
+                    select m
+                ).exists()
+                select n
+// end::ginq_filtering_03[]
+            }.toList()
+        '''
+    }
 }

Apache Groovy committer & PMC member

Blog: http://blog.sunlan.me
Twitter: @daniel_sun