EXISTS and EXISTS use case

Ricardo Parada <[email protected]> Sun, 28 Sep 2025 16:46:53 -0400
Newsgroups gmane.comp.java.cayenne.user
Message-ID <[email protected]>
--Apple-Mail=_B53CF4E7-C128-4175-A9D7-92CFAD98696D
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=utf-8

Hello,=20

I have another use case.  For example, let=E2=80=99s say Artist =
"Leonardo da Vinci" has these paintings:

    * Mona Lisa
    * The Last Supper
    * Vitruvian Man

I need to build a qualifier / expression to obtain the artists with a =
painting that starts with the letter V *and* another painting that =
starts with the letter T.  Artist Leonardo da Vinci would meet such =
criteria.

In EOF, I can do it as follows:
=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=
=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=
=94=E2=80=94=E2=80=94

EOQualifier qual =3D =
Artist.PAINTINGS.exists(Painting.NAME.like(=E2=80=9CV*=E2=80=9D))
    .and(Artist.PAINTINGS.exists(Painting.NAME.like(=E2=80=9CT*=E2=80=9D))=
);

var fetchSpec =3D new ERXFetchSpecification<Artist>(=E2=80=9CArtist=E2=80=9D=
, qual, null);
NSArray<Artist> artists =3D fetchSpec.fetchObjects(ec);

The qualifier qual works in memory and when fetching.

In Cayenne, I=E2=80=99m doing it as follows:
=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=
=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=94=E2=80=
=94=E2=80=94=E2=80=94

Expression exp1 =3D exp(=E2=80=9Cpaintings.name like 'V%=E2=80=99=E2=80=9D=
);
Expression exp2 =3D exp(=E2=80=9Cpaintings.name like =E2=80=99T%=E2=80=99=E2=
=80=9D);

List<Artist> artists =3D ObjectSelect
    .query(Artist.class)
    .where(exp1.exists().andExp(exp2.exists()))
    .select(oc);

which generates the following SQL (formatted by me for readability):

INFO: --- transaction started.
INFO: SELECT t0.DATE_OF_BIRTH, t0.NAME, t0.ID FROM ARTIST t0=20
      WHERE
          EXISTS (
              SELECT t1.ID FROM PAINTING t1=20
              WHERE t1.NAME LIKE ? AND ( t1.ARTIST_ID =3D t0.ID )
          )=20
          AND=20
          EXISTS (
              SELECT t2.ID FROM PAINTING t2=20
              WHERE t2.NAME LIKE ? AND ( t2.ARTIST_ID =3D t0.ID )
          )=20
      [bind: 1->NAME:'M%', 2->NAME:'V%']
INFO: =3D=3D=3D returned 1 row. - took 360 ms.
INFO: +++ transaction committed.

It would be nice if cayenne would allow building an Expression that uses =
exists when fetching and have the same expression work for filtering =
objects in memory.

Has that been considered? For example, the API would be nicer if you =
could do this:

Expression exp =3D Artist.PAINTINGS.exists(Painting.NAME.like(=E2=80=9CM%=E2=
=80=9D))
	.andExp(Artist.PAINTINGS.exists(Painting.NAME.like(=E2=80=9CV%=E2=80=
=9D)));

List<Artist> artists =3D ObjectSelect
    .query(Artist.class)
    .where(exp)
    .select(oc);

If that does not fit the current design then how about EXISTS =
expressions working for filtering objects in memory?

For example:

Expression exp =3D =
Artist.PAINTINGS.dot(Painting.NAME).like(=E2=80=9CM%=E2=80=9D).exists()
   .andExp(Artist.PAINTINGS.dot(Painting.NAME).like(=E2=80=9CV%=E2=80=9D).=
exists());

// Fetch from database
List<Artist> artists =3D ObjectSelect
    .query(Artist.class)
    .where(exp)
    .select(oc);

// Or in memory filtering
List<Artist> artists =3D exp.filterObjects(allArtists);

Thank you,
Ricardo Parada





--Apple-Mail=_B53CF4E7-C128-4175-A9D7-92CFAD98696D--