[ 
https://issues.apache.org/jira/browse/GROOVY-9159?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16873699#comment-16873699
 ] 

Daniel Sun commented on GROOVY-9159:
------------------------------------

[~blackdrag] I saw your comment just now... The WITH-Clause was added, please 
see the 8th example :)

> [GEP] Support LINQ, aka GINQ
> ----------------------------
>
>                 Key: GROOVY-9159
>                 URL: https://issues.apache.org/jira/browse/GROOVY-9159
>             Project: Groovy
>          Issue Type: New Feature
>            Reporter: Daniel Sun
>            Priority: Major
>             Fix For: 4.x
>
>
> h2. *Ⅰ. Background*
> In order to make querying different types of data sources convenient, we need 
> a unified querying interface, i.e. GINQ
> h2. *Ⅱ. Solution*
> The basic rationale can be shown as follows:
>  *Groovy User* ==_writes GINQ code_==> *Parrot Parser* ==generates AST==> 
> *GINQ Engine* ==_translates AST to Stream-Like method invocations_==> 
> *Bytecode Writer*
> h3. {{translates AST to Stream-Like method invocations}} can be designed for 
> different cases:
> h4. 1) target objects are all collections
> translates AST to Java 8+ stream method invocations
> h4. 2) target objects are all DB related objects
> translates AST to *JOOQ* method invocations( [https://github.com/jOOQ/jOOQ] 
> ), which would be implemented as a {{GINQ provider}} in a seperate 
> sub-project(e.g. {{groovy-linq-jooq}}). _Note: *JOOQ* is licensed under 
> *APL2* too_( [https://github.com/jOOQ/jOOQ/blob/master/LICENSE] )
> h4. 3) target objects are XML, CSV, etc. related objects, or even mixed types 
> of objects
> We can treate the case as a special sub-case of case 1
> h3. *Note:*
> {color:#d04437}1. The exact syntax might be altered before introduction, 
> currently working on the general principle.{color}
>  2.GINQ will reuse most of standard SQL syntax, which can make the learning 
> curve smooth and avoid infringing the patent of Microsoft.
>  3. All GINQ related keywords are uppercase to avoid breaking existing source 
> code as possible as we can, e.g. {{FROM}}, {{WHERE}}, {{SELECT}}, etc.
>  4. In order to support type inference better, {{SELECT}} clause is placed at 
> the end of GINQ expression.
>  5. {{alias.VALUE}} is a virtual property and is used to reference the whole 
> record as value. It can be simplified as {{alias}}.
>  6. {{SELECT P1, P2 ... Pn}} will create a {{List}} of {{Tuple}} sub-class 
> instances when and only when {{n >= 2}}
> h2. *Ⅲ. EBNF*
> h3.   TBD
> h2. *Ⅳ. Examples*
> h3. 1. Filtering
> {code:java}
> @groovy.transform.EqualsAndHashCode
> class Person {
>       String name
>       int age
> }
> def persons = [new Person(name: 'Daniel', age: 35), new Person(name: 'Peter', 
> age: 10), new Person(name: 'Alice', age: 22)]
> def result =
>       FROM persons p
>       WHERE p.age > 15 && p.age <= 35
>       SELECT p.name
> assert ['Daniel', 'Alice'] == result
> result =
>       FROM persons p
>       WHERE p.age > 15 && p.age <= 35
>       SELECT p
> assert [new Person(name: 'Daniel', age: 35), new Person(name: 'Alice', age: 
> 22)] == result
> {code}
> {code:java}
> def numbers = [1, 2, 3]
> def result =
>       FROM numbers t
>       WHERE t <= 2
>       SELECT t
> assert [1, 2] == result
> {code}
> h3. 2. Joining
> {code:java}
> import static groovy.lang.Tuple.*
> @groovy.transform.EqualsAndHashCode
> class Person {
>       String name
>       int age
>       City city
> }
> @groovy.transform.EqualsAndHashCode
> class City {
>       String name
> }
> def persons = [new Person(name: 'Daniel', age: 35, city: new 
> City('Shanghai')), new Person(name: 'Peter', age: 10, city: new 
> City('Beijing')), new Person(name: 'Alice', age: 22, city: new 
> City('Hangzhou'))]
> def cities = [new City('Shanghai'), new City('Beijing'), new 
> City('Guangzhou')]
> // inner join
> def result =
>       FROM persons p INNER JOIN cities c
>       ON p.city.name == c.name
>       SELECT p.name
> assert ['Daniel', 'Peter'] == result
> result =
>       FROM persons p, cities c
>       WHERE p.city.name == c.name
>       SELECT p.name
> assert ['Daniel', 'Peter'] == result
> result =
>       FROM persons p, cities c
>       WHERE p.city == c
>       SELECT p.name
> assert ['Daniel', 'Peter'] == result
> // left outer join
> result =
>       FROM persons p LEFT JOIN cities c  //  same to LEFT OUTER JOIN
>       ON p.city.name == c.name
>       SELECT p.name, c.name
> assert [tuple('Daniel', 'Shanghai'), tuple('Peter', 'Beijing'), 
> tuple('Alice', null)] == result
> // right outer join
> result =
>       FROM persons p RIGHT JOIN cities c  //  same to RIGHT OUTER JOIN
>       ON p.city.name == c.name
>       SELECT p.name, c.name
> assert [tuple('Daniel', 'Shanghai'), tuple('Peter', 'Beijing'), tuple(null, 
> 'Guangzhou')] == result
> {code}
> h3. 3. Projection
> {code:java}
> import static groovy.lang.Tuple.*
> @groovy.transform.EqualsAndHashCode
> class Person {
>       String name
>       int age
> }
> def persons = [new Person(name: 'Daniel', age: 35), new Person(name: 'Peter', 
> age: 10), new Person(name: 'Alice', age: 22)]
> def result =
>       FROM persons p
>       SELECT p.name
> assert ['Daniel', 'Peter', 'Alice'] == result
> result =
>       FROM persons p
>       SELECT p.name, p.age
> assert [tuple('Daniel', 35), tuple('Peter', 10), tuple('Alice', 22)] == result
> result =
>       FROM persons p
>       SELECT [name: p.name, age: p.age]
> assert [ [name: 'Daniel', age: 35], [name: 'Peter', age: 10], [name: 'Alice', 
> age: 22] ] == result
> result =
>       FROM persons p
>       SELECT new Person(name: p.name, age: p.age)
> assert persons == result
> result =
>       FROM persons p
>       SELECT p.VALUE
> assert persons == result
> result =
>       FROM persons p
>       SELECT p
> assert persons == result
> {code}
> h3. 4. Grouping
> {code:java}
> import static groovy.lang.Tuple.*
> @groovy.transform.EqualsAndHashCode
> class Person {
>       String name
>       int age
>       String gender
> }
> def persons = [new Person(name: 'Daniel', age: 35, gender: 'Male'), new 
> Person(name: 'Peter', age: 10, gender: 'Male'), new Person(name: 'Alice', 
> age: 22, gender: 'Female')]
> def result =
>       FROM persons p
>       GROUP BY p.gender
>       SELECT p.gender, MAX(p.age)
> assert [tuple('Male', 35), tuple('Female', 22)] == result
> {code}
> h3. 5. Sorting
> {code:java}
> @groovy.transform.EqualsAndHashCode
> class Person {
>       String name
>       int age
> }
> def persons = [new Person(name: 'Daniel', age: 35), new Person(name: 'Peter', 
> age: 10), new Person(name: 'Alice', age: 22)]
> def result =
>       FROM persons p
>       ORDER BY p.age
>       SELECT p.name
> assert ['Peter', 'Alice', 'Daniel'] == result
> result =
>       FROM persons p
>       ORDER BY p.age desc
>       SELECT p.name
> assert ['Daniel', 'Alice', 'Peter'] == result
> {code}
> h3. 6. Pagination
> {code:java}
> def numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
> def result =
>       FROM numbers n
>       LIMIT 5 OFFSET 2
>       SELECT n
> assert [2, 3, 4, 5, 6] == result
> result =
>       FROM numbers n
>       LIMIT 5
>       SELECT n
> assert [0, 1, 2, 3, 4] == result
> {code}
> h3. 7. Nested Queries
> {code:java}
> def numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
> def result =
>       FROM (
>               FROM numbers n
>               WHERE n <= 5
>               SELECT n
>       ) v
>       LIMIT 5 OFFSET 2
>       SELECT v
> assert [2, 3, 4, 5] == result
> {code}
> h3. 8. WITH-Clause
> {code:java}
> def numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
> def result =
>       WITH v AS (
>               FROM numbers n
>               WHERE n <= 5
>               SELECT n
>       )
>       FROM  v
>       LIMIT 5 OFFSET 2
>       SELECT v
> assert [2, 3, 4, 5] == result
> {code}



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to