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 <[email protected]>
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()
+        '''
+    }
 }

Reply via email to