Re: Ginq enhancement?
Daniel Sun <[email protected]> Sat, 21 Jun 2025 14:43:14 -0000
| Newsgroups | gmane.comp.lang.groovy.user |
|---|---|
| Message-ID | <pony-af540fbe-00e7-4f32-b699-cc9acdc14075-users@groovy.apache.org> |
Hi Per,
GINQ is based on object, but SQL is based on table. We can get metadata=
for table via database at compile time, but it's hard to get metadata for ob=
ject by a dynamic language at compile time.
Why do we need metadata? Because we use the metadata to process semanti=
c analysis and construct `NamedRecord` which requires the names.
As for `e.*`, `e` actually represents the wrapped result of `e.*`, for =
example,
```
GQ {
from e in employees
leftjoin mcid in identifiers on e.mainSsn =3D=3D mcid.lei
select e, mcid // the wrapped result for e.*, mcid.*
}
```
BTW, I tried to find the `e.*` usage in LINQ of C#, it is not supported=
either.
Cheers,
Daniel Sun
On 2025/06/20 22:29:13 Per Nyfelt wrote:
> Hi Daniel,
>=20
> I was obviously not phrasing my question well. Let me give it another go:
>=20
> Given two list of objects:
>=20
> import java.time.*
>=20
> class Employee {
> =C2=A0 String mainSsn
> =C2=A0 String coSsn
> =C2=A0 String firstName
> =C2=A0 BigDecimal salary
> =C2=A0 LocalDate startDate
> }
>=20
> class Identifier {
> =C2=A0 String customerId
> =C2=A0 String lei
> }
>=20
>=20
> List employees =3D [
> =C2=A0 new Employee(mainSsn: '111', firstName: 'Rick', salary: 623.3,=20
> startDate: LocalDate.parse('2012-01-01')),
> =C2=A0 new Employee(mainSsn: '222', coSsn: '444', firstName: 'Dan', salary=
:=20
> 515.2, startDate: LocalDate.parse('2013-09-23')),
> =C2=A0 new Employee(mainSsn: '555', coSsn: '555', firstName: 'Michelle',=20
> salary: 611.0, startDate: LocalDate.parse('2014-11-15')),
> ]
>=20
> List identifiers =3D [
> =C2=A0 new Identifier(customerId: '2', lei: '111'),
> =C2=A0 new Identifier(customerId: '3', lei: '222'),
> =C2=A0 new Identifier(customerId: '4', lei: '333'),
> =C2=A0 new Identifier(customerId: '5', lei: '444'),
> =C2=A0 new Identifier(customerId: '6', lei: '555'),
> ]
>=20
> // To add customerId to employees I can do:
> def result =3D GQ {
> =C2=A0 from e in employees
> =C2=A0 leftjoin mcid in identifiers on e.mainSsn =3D=3D mcid.lei
> =C2=A0 leftjoin cocid in identifiers on e.coSsn =3D=3D cocid.lei
> =C2=A0 select e.mainSsn, e.coSsn, e.firstName, e.salary, e.startDate,=20
> mcid?.customerId as mainCustomerId, cocid?.customerId as coCustomerId
> }
>=20
> and get this nice output:
>=20
> +---------+-------+-----------+--------+------------+----------------+-----=
---------+
> | mainSsn | coSsn | firstName | salary | startDate=C2=A0 | mainCustomerId |=
=20
> coCustomerId |
> +---------+-------+-----------+--------+------------+----------------+-----=
---------+
> | 111=C2=A0=C2=A0=C2=A0=C2=A0 |=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 | Rick=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 | 623.3=C2=A0 | 2012-01-01 | 2=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=20
> |=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 |
> | 222=C2=A0=C2=A0=C2=A0=C2=A0 | 444=C2=A0=C2=A0 | Dan=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 | 515.2=C2=A0 | 2013-09-23 | 3=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |=20
> 5=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |
> | 555=C2=A0=C2=A0=C2=A0=C2=A0 | 555=C2=A0=C2=A0 | Michelle=C2=A0 | 611.0=C2=
=A0 | 2014-11-15 | 6=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0 |=20
> 6=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |
> +---------+-------+-----------+--------+------------+----------------+-----=
---------+
>=20
> // But this is rather tedious and i would want something that is=20
> equivalent to the .* construct in SQL. I.e:
> def result =3D GQ {
> =C2=A0 from e in employees
> =C2=A0 leftjoin mcid in identifiers on e.mainSsn =3D=3D mcid.lei
> =C2=A0 leftjoin cocid in identifiers on e.coSsn =3D=3D cocid.lei
> =C2=A0 select e.*, mcid?.customerId as mainCustomerId, cocid?.customerId a=
s=20
> coCustomerId
> }
>=20
> This does not work but if there was some way to convert e to "whatever=20
> construct that is needed to make it expand all the properties", lets=20
> call it expand(). The I could do
>=20
> def result =3D GQ {
> =C2=A0 from e in employees
> =C2=A0 leftjoin mcid in identifiers on e.mainSsn =3D=3D mcid.lei
> =C2=A0 leftjoin cocid in identifiers on e.coSsn =3D=3D cocid.lei
> =C2=A0 select expand(e), mcid?.customerId as mainCustomerId,=20
> cocid?.customerId as coCustomerId
> }
>=20
> is there (or could there be) a type (class) that expand(e) could return=20
> so it would give me the same result as when i explicitly specify each field?
>=20
> Regards,
>=20
> Per
>=20
> On 6/20/25 15:47, Daniel Sun wrote:
> > Hi Per,
> >
> > You can find the `leftjoin` usage here: https://github.com/apache/groovy/=
blob/b046d1b2bcbbddd59ea3d6abdf5de24a671ce51a/subprojects/groovy-ginq/src/spe=
c/test/org/apache/groovy/ginq/GinqTest.groovy#L763
> >
> > Cheers,
> > Daniel Sun
> >
> > On 2025/06/17 20:46:35 Per Nyfelt wrote:
> >> Hi,
> >>
> >> I ran into a a ginq "issue" today.
> >>
> >> I have two list of rows where each key is the column name (actually
> >> List<Row> but you can think about it as a List<Map>)=C2=A0 collections t=
hat
> >> looks like this:
> >>
> >> employees: 3 obs * 5 variables
> >> mainSsn=C2=A0=C2=A0=C2=A0 coSsn=C2=A0=C2=A0=C2=A0 firstName=C2=A0=C2=A0=
=C2=A0 salary=C2=A0=C2=A0=C2=A0 startDate
> >> 111=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 Rick=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 623.3 2012-01-01
> >> 222=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 444=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Dan=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0=C2=A0=C2=A0 515.2=C2=A0=C2=A0=C2=A0 2013-09-=
23
> >> 333=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 555 =C2=A0 =C2=A0=C2=A0=C2=A0 Michelle =C2=A0 =C2=A0=C2=A0=C2=A0 611.0=C2=
=A0=C2=A0=C2=A0 2014-11-15
> >>
> >> eln: 5 obs * 2 variables
> >> customerId=C2=A0=C2=A0=C2=A0 lei
> >> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 2=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 111
> >> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 3=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 222
> >> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 4=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 333
> >> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 5=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 444
> >> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 6=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 555
> >>
> >> I want to join employees with eln and add customerIs columns matching
> >> mainSsn and CoSsn. Thinking SQL, I wanted to do something like this
> >>
> >> def result =3DGQ {
> >> from tin employees
> >> leftjoin mcidin elnon t.mainSsn =3D=3D mcid.lei leftjoin cocidin eln=
on t.coSsn =3D=3D cocid.lei select t.*, mcid?.customerId as'mainCustomerId', =
cocid?.customerId as'coCustomerId' }
> >>
> >> but this is not supported in ginq and I could not find any signs of supp=
ort for wildcards in the docs.
> >>
> >> I found that could do this:
> >> def result =3D GQ {
> >> from t in table
> >> leftjoin mcid in ssnCustomerId on t.mainSsn =3D=3D mcid.lei
> >> leftjoin cocid in ssnCustomerId on t.coSsn =3D=3D cocid.lei
> >> select t + mcid?.customerId+ cocid?.customerId
> >> }
> >>
> >> Which gives me a List of Lists but then i loose the column names. i.e.
> >> merged: 3 obs * 7 variables
> >> c1 c2 c3 c4 c5 c6 c7
> >> 111 444 Rick 623.3 2012-01-01 2 5
> >> 222 Dan 515.2 2013-09-23 3 null
> >> 333 555 Michelle 611.0 2014-11-15 4 6
> >>
> >> Instead i had to do this:
> >>
> >> def result =3DGQ {
> >> from tin employees
> >> leftjoin mcidin elnon t.mainSsn =3D=3D mcid.lei leftjoin cocidin eln=
on t.coSsn =3D=3D cocid.lei select t.toMap() + [mainCustomerId: mcid?.custome=
rId] + [coCustomerId: cocid?.customerId]
> >> }
> >>
> >> (This relies on the fact that a matrix Row has a toMap() method)
> >>
> >> ginq result content:
> >> [{mainSsn=3D111, coSsn=3D, firstName=3DRick, salary=3D623.3,
> >> startDate=3D2012-01-01, mainCustomerId=3D2, coCustomerId=3Dnull},
> >> {mainSsn=3D222, coSsn=3D444, firstName=3DDan, salary=3D515.2,
> >> startDate=3D2013-09-23, mainCustomerId=3D3, coCustomerId=3D5}, {mainSsn=
=3D333,
> >> coSsn=3D555, firstName=3DMichelle, salary=3D611.0, startDate=3D2014-11-1=
5,
> >> mainCustomerId=3D4, coCustomerId=3D6}]
> >>
> >> Which is could then easily transform back into a list of rows (actually
> >> a matrix but you can think of it as a List<Map>
> >>
> >> result matrix content:
> >> merged: 3 obs * 7 variables
> >> mainSsn=C2=A0=C2=A0=C2=A0 coSsn=C2=A0=C2=A0=C2=A0 firstName=C2=A0=C2=A0=
=C2=A0 salary=C2=A0=C2=A0=C2=A0 startDate mainCustomerId
> >> coCustomerId
> >> 111=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 Rick=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=
=A0 =C2=A0 623.3
> >> 2012-01-01=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 2 null
> >> 222=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 444=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Dan=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0=C2=A0 515.2
> >> 2013-09-23=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 3 =C2=A0=C2=A0 5
> >> 333=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 555=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Michelle=C2=A0=C2=A0=C2=A0=C2=A0 =
=C2=A0 611.0 2014-11-15
> >> =C2=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =
4 =C2=A0=C2=A0=C2=A0 6
> >>
> >> Has there been any discussions about supporting=C2=A0 breaking up the se=
lect
> >> objects into parts using wildcards and deemed it not feasible to
> >> implement or did nobody have this problem before?
> >>
> >> Something like this could perhaps be an alternative to wildcard syntax:
> >>
> >> def result =3DGQ {
> >> from tin employees
> >> leftjoin mcidin elnon t.mainSsn =3D=3D mcid.lei leftjoin cocidin eln=
on t.coSsn =3D=3D cocid.lei select toMap(t) + [mainCustomerId: mcid?.customer=
Id,coCustomerId: cocid?.customerId])
> >> }
> >>
> >> what do you think?
> >>
> >> Best regards,
> >>
> >> Per
> >>
>=20