Hi all,

Another use case we have is as follows.  We have an expression for the child 
entity.  For example:

Expression paintingExpression = exp("name like 'G%' or name like 'A%'");

Now, I want to use that to fetch the artists that have paintings that match 
that expression.  I have to prefix each key with “paitings." in order to use it 
to fetch those artists.  The expression would then become “paintings.name like 
'G%' or paintings.name like 'A%’”.

Is there something in Cayenne already to convert an expression by prefixing 
every key path in the expression?  

For example, not knowing much about Cayenne, I wrote the following utility 
method that seems to do the job:

public static <T> Expression prefix(BaseProperty<T> rel, Expression 
subExpression) {
    return
        subExpression.transform(o -> {
            if (o instanceof CayennePath path) {
                // e.g. Turn name path into paintings.name
                return rel.getPath().dot(path);
            }
            return o;
        });
}

Then I use it as follows:

Expression paintingsExpr = exp("name like 'G%' or name like 'A%'”);

// This returns expression for "paintings.name like 'G%' or paintings.name like 
'A%'"
Expression expr = prefix(Artist.PAINTINGS, paintingsExpr);

// Artists with paintings starting with letter G or A
List<Artist> someArtists = ObjectSelect
        .query(Artist.class)
        .where(expr.exists())
        .select(context);

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 
      WHERE EXISTS (
          SELECT t1.ID FROM PAINTING t1 
          WHERE ( t1.NAME LIKE ? OR t1.NAME LIKE ? ) 
          AND ( t1.ARTIST_ID = t0.ID )
      ) 
      [bind: 1->NAME:'G%', 2->NAME:'A%']
INFO: === returned 1 row. - took 1 ms.
INFO: +++ transaction committed.

Summary
Am I reinventing the wheel by writing this prefix() utility method? Does 
Cayenne have something like that already?

Thank you


Reply via email to