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()
+ '''
+ }
}