[ 
https://issues.apache.org/jira/browse/GROOVY-9159?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Daniel Sun updated GROOVY-9159:
-------------------------------
    Description: 
h2. Background

In order to make querying different types of data sources convenient, we need a 
single standard querying interface, i.e. GINQ
h2. Solution

GINQ will reuse most of standard SQL syntax, which can make the learning curve 
smooth and avoid infringing the patent of Microsoft.

 
 The basic rationale can be shown as follows:
 *User* ==_writes GINQ code_==> *GINQ Engine* ==_translates to Stream-Like 
method invocations_==> *Bytecode Writer*


 *Note:*
 {color:#d04437}1. The exact syntax might be altered before introduction, 
currently working on the general principle.{color}
 2. All GINQ related keywords are uppercase to avoid breaking existing source 
code as possible as we can, e.g. {{FROM}}, {{WHERE}}, {{SELECT}}, etc.
 3. In order to support type inference better, {{SELECT}} clause is placed at 
the end of GINQ expression.
 4. {{alias.VALUE}} is a virtual property and is used to reference the whole 
record as value. It can be simplified as {{alias}}.
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 AND p.age <= 35
        SELECT p.name

assert ['Daniel', 'Alice'] == result

result =
        FROM persons p
        WHERE p.age > 15 AND p.age <= 35
        SELECT p.VALUE

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.VALUE <= 2
        SELECT t.VALUE

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.VALUE
        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 p.VALUE

assert [2, 3, 4, 5, 6] == result

result =
        FROM numbers n
        LIMIT 5
        SELECT p.VALUE

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.VALUE <= 5
                SELECT n.VALUE
        ) v
        LIMIT 5 OFFSET 2
        SELECT v.VALUE

assert [2, 3, 4, 5] == result
{code}

  was:
h2. Background

In order to make querying different types of data sources convenient, we need a 
single standard querying interface, i.e. GINQ
h2. Solution

GINQ will reuse most of standard SQL syntax, which can make the learning curve 
smooth and avoid infringing the patent of Microsoft.

 
 The basic rationale can be shown as follows:
 *User* ==_writes GINQ code_==> *GINQ Engine* ==_translates to Stream-Like 
method invocations_==> *Bytecode Writer*

 
 Note:
 {color:#d04437}1. The exact syntax might be altered before introduction, 
currently working on the general principle.{color}
 2. All GINQ related keywords are uppercase to avoid breaking existing source 
code as possible as we can, e.g. {{FROM}}, {{WHERE}}, {{SELECT}}, etc.
 3. In order to support type inference better, {{SELECT}} clause is placed at 
the end of GINQ expression.
 4. {{alias.VALUE}} is a virtual property and is used to reference the whole 
record as value. It can be simplified as {{alias}}.
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 AND p.age <= 35
        SELECT p.name

assert ['Daniel', 'Alice'] == result

result =
        FROM persons p
        WHERE p.age > 15 AND p.age <= 35
        SELECT p.VALUE

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.VALUE <= 2
        SELECT t.VALUE

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.VALUE
        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 p.VALUE

assert [2, 3, 4, 5, 6] == result

result =
        FROM numbers n
        LIMIT 5
        SELECT p.VALUE

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.VALUE <= 5
                SELECT n.VALUE
        ) v
        LIMIT 5 OFFSET 2
        SELECT v.VALUE

assert [2, 3, 4, 5] == result
{code}


> [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 single standard querying interface, i.e. GINQ
> h2. Solution
> GINQ will reuse most of standard SQL syntax, which can make the learning 
> curve smooth and avoid infringing the patent of Microsoft.
>  
>  The basic rationale can be shown as follows:
>  *User* ==_writes GINQ code_==> *GINQ Engine* ==_translates to Stream-Like 
> method invocations_==> *Bytecode Writer*
>  *Note:*
>  {color:#d04437}1. The exact syntax might be altered before introduction, 
> currently working on the general principle.{color}
>  2. All GINQ related keywords are uppercase to avoid breaking existing source 
> code as possible as we can, e.g. {{FROM}}, {{WHERE}}, {{SELECT}}, etc.
>  3. In order to support type inference better, {{SELECT}} clause is placed at 
> the end of GINQ expression.
>  4. {{alias.VALUE}} is a virtual property and is used to reference the whole 
> record as value. It can be simplified as {{alias}}.
> 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 AND p.age <= 35
>       SELECT p.name
> assert ['Daniel', 'Alice'] == result
> result =
>       FROM persons p
>       WHERE p.age > 15 AND p.age <= 35
>       SELECT p.VALUE
> 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.VALUE <= 2
>       SELECT t.VALUE
> 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.VALUE
>       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 p.VALUE
> assert [2, 3, 4, 5, 6] == result
> result =
>       FROM numbers n
>       LIMIT 5
>       SELECT p.VALUE
> 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.VALUE <= 5
>               SELECT n.VALUE
>       ) v
>       LIMIT 5 OFFSET 2
>       SELECT v.VALUE
> assert [2, 3, 4, 5] == result
> {code}



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

Reply via email to