You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cayenne.apache.org by Andrus Adamchik <an...@objectstyle.org> on 2009/12/30 20:59:13 UTC

Type-safe qualifiers

While there's lots of things we need to figure out to create  
generified queries, here is one low-hanging fruit - type-safe  
qualifiers. The idea is borrowed from the WebObjects EOF extensions  
framework called Wonder:

http://webobjects.mdimension.com/hudson/job/Wonder53/javadoc/er/extensions/eof/ERXKey.html

The way it works, is that for each String property in a Persistent  
object, we generate an extra parameterized "key". E.g.:

public class _Artist extends CayenneDataObject {

	public static final String DATE_OF_BIRTH_PROPERTY = "dateOfBirth";
	public static final Key<Date> DATE_OF_BIRTH = new Key<Date>(
			DATE_OF_BIRTH_PROPERTY);

	public static final String NAME_PROPERTY = "name";
	public static final Key<String> NAME = new Key<String>(NAME_PROPERTY);

	public static final String PAINTINGS_PROPERTY = "paintings";
	public static final Key<Painting> PAINTINGS = new Key<Painting>(
			PAINTINGS_PROPERTY);
...

Key class is a builder of type-safe key-value expressions (see the  
link above for the type of methods that it has). I wrote a quick API- 
only prototype that is a replacement of ExpressionFactory. Here is an  
example of building the following Expression the old way and the new  
way:

   Expression: name='X' and (painting.name='Y' or painting.name='Z' or  
painting.name='A')

1. Current API:

Expression clause1 = ExpressionFactory.matchExp(Artist.NAME_PROPERTY,  
"X");
Expression clause2 =  
ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY + "." +  
Painting.NAME_PROPERTY, "Y");
Expression clause3 =  
ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY + "." +  
Painting.NAME_PROPERTY, "Z");
Expression clause4 =  
ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY + "." +  
Painting.NAME_PROPERTY, "A");

Expression clause23 = clause2.orExp(clause3);
Expression clause234 = clause23.orExp(clause4);
Expression qualifier = clause1.andExp(clause234);

2. New API:

Expression clause1 = Artist.NAME.eq("X");
Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
Expression clause3 = Artist.PAINTINGS.dot(Painting.NAME).eq("Z");
Expression clause4 = Artist.PAINTINGS.dot(Painting.NAME).eq("A");

Expression qualifier = Each.get(clause1, Any.get(clause2, clause3,  
clause4));

As you see the new API is much tighter, and the first part of it is  
completely type-safe (generated "keys" ensure that we are matching  
against the right type of value, even in a multi-step path). The last  
line uses 2 new Expression factories, "Each" and "Any", to quickly  
organize key-value expressions into a nested expression tree. I think  
this is as good as it can get within the constraints of the Java syntax.

I'd like to see if a similar approach can be extended to query  
building. Although that'll be a entirely different level of effort.  
For one thing it may force us to reconcile EJQBL and SelectQuery into  
a single query (SelectQuery is functionally a subset of EJBQLQuery.  
But SelectQuery "compilation" into SQL is fairly optimized, while  
EJBQLQuery-to-SQL conversion involves a much longer and slower  
pipeline). Plus of course there are other obstacles that we discussed  
before (such as a variety of possible result types).

Andrus






Re: Type-safe qualifiers (and queries)

Posted by Andrus Adamchik <an...@objectstyle.org>.
Yeah, seems like the same thing, but not parameterized properly.

Andrus

On Dec 31, 2009, at 6:38 AM, Mike Kienenberger wrote:

> Maybe worth imitating how JPA 2.0 does it?  I've only glanced at it in
> the past, but it seems similar.  (Eclipselink is the reference
> implementation.)
>
> http://wiki.eclipse.org/Introduction_to_EclipseLink_Expressions_%28ELUG%29
>
> http://wiki.eclipse.org/EclipseLink/Examples/JPA/ORMQueries
>
> http://www.eclipse.org/eclipselink/api/1.1/org/eclipse/persistence/expressions/ExpressionBuilder.html
>
> There's also something called a Criteria api now in JPA 2.0.   I only
> looked at it (in the spec) as I was typing this message.
>
> http://jcp.org/aboutJava/communityprocess/pfd/jsr317/index2.html
> Chapter 6
>
> Not sure exactly how it's different -- maybe it's just a specification
> of the expression builder syntax.
>
>
>
> On Wed, Dec 30, 2009 at 4:54 PM, Andrus Adamchik <andrus@objectstyle.org 
> > wrote:
>>
>> On Dec 30, 2009, at 9:59 PM, Andrus Adamchik wrote:
>>
>>> I'd like to see if a similar approach can be extended to query  
>>> building.
>>> Although that'll be a entirely different level of effort.
>>
>> Or maybe it is not that hopeless and we can use a set of user- 
>> facing query
>> "builders" without affecting our backend implementations. Just  
>> prototyped a
>> builder that can create EJBQL/SelectQuery with parameterized result  
>> type
>> that can be tied to ObjectContext.performQuery()...
>>
>> public interface Query<T> ...
>> public class Select<T> implements Query<T>...
>>
>> Here is now we can build correctly parameterized queries for  
>> different
>> result types. Also shows other EJBQL features, like subqueries.  
>> While it
>> adds some more terse API compared to SelectQuery, parameterization  
>> is the
>> main point here. I guess this is a good way to make EJBQLQuery  
>> finally
>> usable:
>>
>> void query_newAPI_EntityResult() {
>>
>>        Select<Artist> query = Select.entity(Artist.class);
>>        query.setDistinct();
>>
>>        Select<Painting> subquery = Select.entity(Painting.class);
>>        subquery.setWhere(Painting.NAME.like("X%"));
>>
>>        Expression clause1 = Artist.NAME.eq("X");
>>        Expression clause2 =  
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>>        Expression clause3 = Exists.in(subquery);
>>        query.setWhere(All.of(clause1, clause2, clause3));
>>
>>        query.addOrderByAscending(Artist.NAME);
>>         
>> query.addOrderByAscending(Artist.PAINTINGS.dot(Painting.NAME));
>>
>>        query.addDisjointPrefetch(Artist.PAINTINGS);
>> }
>>
>> void query_newAPI_ScalarResult() {
>>
>>        Select<Date> query = Select.scalar(Artist.class,
>> Artist.DATE_OF_BIRTH);
>>
>>        Expression clause1 = Artist.NAME.eq("X");
>>        Expression clause2 =  
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>>        query.setWhere(Any.of(clause1, clause2));
>> }
>>
>> void query_newAPI_MixedResult() {
>>
>>        // fetching Artist entity plus paintings count
>>        Select<Object[]> query = Select.mixed(Artist.class,
>>                        new Key<Artist>(""),  
>> Artist.PAINTINGS.count());
>>
>>        Expression clause1 = Artist.NAME.eq("X");
>>        Expression clause2 =  
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>>        query.setWhere(Any.of(clause1, clause2));
>> }
>>
>> void query_newAPI_GenericObject() {
>>
>>        Select<Persistent> query = Select.generic(Persistent.class,
>>                        "MyGenericEntity");
>>
>>        Expression clause1 = new Key<String>("name").eq("X");
>>        Expression clause2 = new Key<Integer>("age").eq(5);
>>        query.setWhere(All.of(clause1, clause2));
>> }
>>
>> void query_newAPI_DataRow() {
>>
>>        Select<DataRow> query = Select.dataRow(Artist.class);
>>
>>        Expression clause1 = Artist.NAME.eq("X");
>>        Expression clause2 =  
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>>        query.setWhere(All.of(clause1, clause2));
>> }
>>
>>
>>
>> Andrus
>>
>


Re: Type-safe qualifiers (and queries)

Posted by Mike Kienenberger <mk...@gmail.com>.
Maybe worth imitating how JPA 2.0 does it?  I've only glanced at it in
the past, but it seems similar.  (Eclipselink is the reference
implementation.)

http://wiki.eclipse.org/Introduction_to_EclipseLink_Expressions_%28ELUG%29

http://wiki.eclipse.org/EclipseLink/Examples/JPA/ORMQueries

http://www.eclipse.org/eclipselink/api/1.1/org/eclipse/persistence/expressions/ExpressionBuilder.html

There's also something called a Criteria api now in JPA 2.0.   I only
looked at it (in the spec) as I was typing this message.

http://jcp.org/aboutJava/communityprocess/pfd/jsr317/index2.html
Chapter 6

Not sure exactly how it's different -- maybe it's just a specification
of the expression builder syntax.



On Wed, Dec 30, 2009 at 4:54 PM, Andrus Adamchik <an...@objectstyle.org> wrote:
>
> On Dec 30, 2009, at 9:59 PM, Andrus Adamchik wrote:
>
>> I'd like to see if a similar approach can be extended to query building.
>> Although that'll be a entirely different level of effort.
>
> Or maybe it is not that hopeless and we can use a set of user-facing query
> "builders" without affecting our backend implementations. Just prototyped a
> builder that can create EJBQL/SelectQuery with parameterized result type
> that can be tied to ObjectContext.performQuery()...
>
> public interface Query<T> ...
> public class Select<T> implements Query<T>...
>
> Here is now we can build correctly parameterized queries for different
> result types. Also shows other EJBQL features, like subqueries. While it
> adds some more terse API compared to SelectQuery, parameterization is the
> main point here. I guess this is a good way to make EJBQLQuery finally
> usable:
>
> void query_newAPI_EntityResult() {
>
>        Select<Artist> query = Select.entity(Artist.class);
>        query.setDistinct();
>
>        Select<Painting> subquery = Select.entity(Painting.class);
>        subquery.setWhere(Painting.NAME.like("X%"));
>
>        Expression clause1 = Artist.NAME.eq("X");
>        Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>        Expression clause3 = Exists.in(subquery);
>        query.setWhere(All.of(clause1, clause2, clause3));
>
>        query.addOrderByAscending(Artist.NAME);
>        query.addOrderByAscending(Artist.PAINTINGS.dot(Painting.NAME));
>
>        query.addDisjointPrefetch(Artist.PAINTINGS);
> }
>
> void query_newAPI_ScalarResult() {
>
>        Select<Date> query = Select.scalar(Artist.class,
> Artist.DATE_OF_BIRTH);
>
>        Expression clause1 = Artist.NAME.eq("X");
>        Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>        query.setWhere(Any.of(clause1, clause2));
> }
>
> void query_newAPI_MixedResult() {
>
>        // fetching Artist entity plus paintings count
>        Select<Object[]> query = Select.mixed(Artist.class,
>                        new Key<Artist>(""), Artist.PAINTINGS.count());
>
>        Expression clause1 = Artist.NAME.eq("X");
>        Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>        query.setWhere(Any.of(clause1, clause2));
> }
>
> void query_newAPI_GenericObject() {
>
>        Select<Persistent> query = Select.generic(Persistent.class,
>                        "MyGenericEntity");
>
>        Expression clause1 = new Key<String>("name").eq("X");
>        Expression clause2 = new Key<Integer>("age").eq(5);
>        query.setWhere(All.of(clause1, clause2));
> }
>
> void query_newAPI_DataRow() {
>
>        Select<DataRow> query = Select.dataRow(Artist.class);
>
>        Expression clause1 = Artist.NAME.eq("X");
>        Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>        query.setWhere(All.of(clause1, clause2));
> }
>
>
>
> Andrus
>

Re: Type-safe qualifiers (and queries)

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 30, 2009, at 9:59 PM, Andrus Adamchik wrote:

> I'd like to see if a similar approach can be extended to query  
> building. Although that'll be a entirely different level of effort.

Or maybe it is not that hopeless and we can use a set of user-facing  
query "builders" without affecting our backend implementations. Just  
prototyped a builder that can create EJBQL/SelectQuery with  
parameterized result type that can be tied to  
ObjectContext.performQuery()...

public interface Query<T> ...
public class Select<T> implements Query<T>...

Here is now we can build correctly parameterized queries for different  
result types. Also shows other EJBQL features, like subqueries. While  
it adds some more terse API compared to SelectQuery, parameterization  
is the main point here. I guess this is a good way to make EJBQLQuery  
finally usable:

void query_newAPI_EntityResult() {

	Select<Artist> query = Select.entity(Artist.class);
	query.setDistinct();

	Select<Painting> subquery = Select.entity(Painting.class);
	subquery.setWhere(Painting.NAME.like("X%"));

	Expression clause1 = Artist.NAME.eq("X");
	Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
	Expression clause3 = Exists.in(subquery);
	query.setWhere(All.of(clause1, clause2, clause3));

	query.addOrderByAscending(Artist.NAME);
	query.addOrderByAscending(Artist.PAINTINGS.dot(Painting.NAME));

	query.addDisjointPrefetch(Artist.PAINTINGS);
}

void query_newAPI_ScalarResult() {

	Select<Date> query = Select.scalar(Artist.class, Artist.DATE_OF_BIRTH);

	Expression clause1 = Artist.NAME.eq("X");
	Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
	query.setWhere(Any.of(clause1, clause2));
}

void query_newAPI_MixedResult() {

	// fetching Artist entity plus paintings count
	Select<Object[]> query = Select.mixed(Artist.class,
			new Key<Artist>(""), Artist.PAINTINGS.count());

	Expression clause1 = Artist.NAME.eq("X");
	Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
	query.setWhere(Any.of(clause1, clause2));
}

void query_newAPI_GenericObject() {

	Select<Persistent> query = Select.generic(Persistent.class,
			"MyGenericEntity");

	Expression clause1 = new Key<String>("name").eq("X");
	Expression clause2 = new Key<Integer>("age").eq(5);
	query.setWhere(All.of(clause1, clause2));
}

void query_newAPI_DataRow() {

	Select<DataRow> query = Select.dataRow(Artist.class);

	Expression clause1 = Artist.NAME.eq("X");
	Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
	query.setWhere(All.of(clause1, clause2));
}



Andrus

Re: Type-safe qualifiers

Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 31/12/09 7:04 PM, Andrus Adamchik wrote:
> I like "dot" and "eq" because they have fewer letters, making things
> more tight. (also we used the term "path", not "join", so I guess
> there's really no precedent here). But good point about outer joins.
> Will definitely need to address that aspect.

I don't want to bikeshed on the details here when the overall idea is so nice, but just to finish this part of the conversation....

* 'dot' isn't very clear to me. I don't immediately make the connection that 'dot' == '.' == 'path' == 'join'. And maybe it just my maths background, but the first thing I thought of was 'dot product' in vector maths. [1]

* I liked 'join' mainly because anyone who knows SQL will understand instantly what that means, and 'outerJoin' is also immediately obvious.

* 'outerDot' sounds really silly :-)



Ari


[1] http://en.wikipedia.org/wiki/Dot_product

-- 
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: Type-safe qualifiers

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 31, 2009, at 10:04 AM, Andrus Adamchik wrote:

> On Dec 31, 2009, at 7:13 AM, Aristedes Maniatis wrote:
>> How does Key relate to Attribute and Relationship? Is it an  
>> interface that both implement?
>
> Unrelated at all, except for the matching name. I thought of using  
> Attributes/Relationships for this purpose, but it is not possible to  
> parameterize them as they are abstract metadata. Their "compiled"  
> counterparts (Property/ArcProperty) could've potentially been used  
> as "keys", but those can't be statically bound to Persistent objects  
> in compile time, as they are generated in runtime.

Also I guess Key is a "path", so logically it is not a single  
attribute or relationship, but potentially a chain of those. The  
closest thing in Cayenne 3.0 is a "path" Expression. So maybe rename  
it from Key to Path and subclass from Expression (??). Anyways, that's  
some details to figure out once we are comfortable with the general  
direction...

Andrus


Re: Type-safe qualifiers

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 31, 2009, at 7:13 AM, Aristedes Maniatis wrote:
> How does Key relate to Attribute and Relationship? Is it an  
> interface that both implement?

Unrelated at all, except for the matching name. I thought of using  
Attributes/Relationships for this purpose, but it is not possible to  
parameterize them as they are abstract metadata. Their "compiled"  
counterparts (Property/ArcProperty) could've potentially been used as  
"keys", but those can't be statically bound to Persistent objects in  
compile time, as they are generated in runtime.


> 2. New API:
>>
>> Expression clause1 = Artist.NAME.eq("X");
>> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>
> Would this be more in keeping with existing usage:
>
>  Artist.PAINTINGS.join(Painting.NAME).match("Y");
>
> and
>
>  Artist.PAINTINGS.outerJoin(Painting.NAME).match("Y");
>
> to emulate the "|" we have now.

I like "dot" and "eq" because they have fewer letters, making things  
more tight. (also we used the term "path", not "join", so I guess  
there's really no precedent here). But good point about outer joins.  
Will definitely need to address that aspect.

> At first glance Artist.PAINTINGS looks like it should be  
> <Collection<Painting>>. But that breaks the type-safeness. We can  
> tell when a key represents a Relationship (<? extends  
> PersistentObject>) rather than an Attribute. But would we ever want  
> to know when it represents a to-many relation rather than a to-one?

I had this question as well when first looking at Wonder. For the  
expression building stuff seems like it is ok to not make a  
distinction between to-one and to-many. However if we stumble on the  
case where we need to, we can simply subclass Key. (similar to  
Property/ArcProperty).

>  Expression qualifier = clause1.and(clause2.or(clause3).or(clause4));

I like this one. I guess we'll go with it.

Andrus

Re: Type-safe qualifiers

Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 31/12/09 6:59 AM, Andrus Adamchik wrote:
> While there's lots of things we need to figure out to create generified
> queries, here is one low-hanging fruit - type-safe qualifiers. The idea
> is borrowed from the WebObjects EOF extensions framework called Wonder:
>
> http://webobjects.mdimension.com/hudson/job/Wonder53/javadoc/er/extensions/eof/ERXKey.html
>
>
> The way it works, is that for each String property in a Persistent
> object, we generate an extra parameterized "key". E.g.:
>
> public class _Artist extends CayenneDataObject {
>
> public static final String DATE_OF_BIRTH_PROPERTY = "dateOfBirth";
> public static final Key<Date> DATE_OF_BIRTH = new Key<Date>(
> DATE_OF_BIRTH_PROPERTY);

What a shame String is final, so Key can't just extend String. Oh well.

In the long term, perhaps the old property will go away, so perhaps this is a better first step:

   public static final Key<Date> DATE_OF_BIRTH = new Key<Date>("dateOfBirth");

   @deprecated
   public static final String DATE_OF_BIRTH_PROPERTY = DATE_OF_BIRTH.toString();


How does Key relate to Attribute and Relationship? Is it an interface that both implement?


> 2. New API:
>
> Expression clause1 = Artist.NAME.eq("X");
> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");

Would this be more in keeping with existing usage:

   Artist.PAINTINGS.join(Painting.NAME).match("Y");

and

   Artist.PAINTINGS.outerJoin(Painting.NAME).match("Y");

to emulate the "|" we have now.


> Expression clause3 = Artist.PAINTINGS.dot(Painting.NAME).eq("Z");
> Expression clause4 = Artist.PAINTINGS.dot(Painting.NAME).eq("A");
>
> Expression qualifier = Each.get(clause1, Any.get(clause2, clause3,
> clause4));
>
> As you see the new API is much tighter, and the first part of it is
> completely type-safe (generated "keys" ensure that we are matching
> against the right type of value, even in a multi-step path).

At first glance Artist.PAINTINGS looks like it should be <Collection<Painting>>. But that breaks the type-safeness. We can tell when a key represents a Relationship (<? extends PersistentObject>) rather than an Attribute. But would we ever want to know when it represents a to-many relation rather than a to-one?


>  The last
> line uses 2 new Expression factories, "Each" and "Any", to quickly
> organize key-value expressions into a nested expression tree. I think
> this is as good as it can get within the constraints of the Java syntax.

Any and Each are a bit obscure. You aren't going to find them by reading the Javadoc for Expression. What do they give over the existing orExp/andExp syntax?

   Expression qualifier = Each.get(clause1, Any.get(clause2, clause3, clause4));

   Expression qualifier = clause1.andExp(clause2.orExp(clause3).orExp(clause4));

Intuitively I don't equate "each" as meaning "all" or "and". Or even simpler:

   Expression qualifier = clause1.and(clause2.or(clause3).or(clause4));


Ari


-- 

-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C  5EFA EF6A 7D2E 3E49 102A

Re: Type-safe qualifiers

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 31, 2009, at 11:24 AM, Andrey Razumovsky wrote:

> 1. Instead of:
> Expression clause2 =  
> ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY +
> "." + Painting.NAME_PROPERTY, "Y");
> I prefer using just
> Expression clause2 =  
> ExpressionFactory.matchExp("paintings.paintingName",
> "Y");
> Yes, I don't like using constants, to me bottom example seems more  
> readable,
> short and quicker to write (especially if there are a lot of items  
> in the
> path). As far as I understood, this will not be allowed in new API (?)

Looks like your schema doesn't change too often :-)

Anyways, we can still allow that in the new API. And even preserve  
"declarative" type-safety:

Expression clause2 = new Key<String>("paintings.paintingName").eq("Y");


> 2. I wrote a library which automatically builds Cayenne expressions  
> from
> values user specified in grid filter of Web (GWT) client - path is  
> coming
> from client. With new API, shall I have something like
> CayenneDataObject.getKey(String) method to obtain any key in uniform  
> way?
> (even with that, I will have more work to do to convert path to new  
> API,
> maybe we need some method like Key Cayenne.getNestedKey(Class clazz,  
> String
> path)?

See the example above (new Key<?>(...)). Capability-wise the new API  
will allow everything ExpressionFactory does.

> 3. I have examples of intentionally not following type-safing for
> expressions (these are real examples)
>
>  a. EF.matchExp("artist", "200") - quick comparison of relation  
> object's id
>  b. EF.lessExp("date", "2010-01-01 10:00:00.000") - replacement of  
> date (or
> int, double, whatever) with just string
>
> Will those be allowed somehow in new API?

I guess there are two ways about it.

1. new Key<Object>(..)
2. Provide methods that do String to object conversions for common  
cases of dates and numbers, using explicit or implicit formatters E.g.

    new Key<Date>("dob").after("2010-01-01 10:00:00.000", "YYYY-mm- 
dd...");
    new Key<Integer>("age").eq("200");

Among other things this will make some drivers (like PostgreSQL)  
happy, as they don't allow fuzzy types in PreparedStatement bind  
parameters.

>
> Happy New Year!!!

Happy New Year to everybody :-)

Andrus



Re: Type-safe qualifiers

Posted by Andrey Razumovsky <ra...@gmail.com>.
Type-safing for expressions is cool, but I have few questions about its
profitness.
Just a couple examples.

1. Instead of:
Expression clause2 = ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY +
"." + Painting.NAME_PROPERTY, "Y");
I prefer using just
Expression clause2 = ExpressionFactory.matchExp("paintings.paintingName",
"Y");
Yes, I don't like using constants, to me bottom example seems more readable,
short and quicker to write (especially if there are a lot of items in the
path). As far as I understood, this will not be allowed in new API (?)

2. I wrote a library which automatically builds Cayenne expressions from
values user specified in grid filter of Web (GWT) client - path is coming
from client. With new API, shall I have something like
CayenneDataObject.getKey(String) method to obtain any key in uniform way?
(even with that, I will have more work to do to convert path to new API,
maybe we need some method like Key Cayenne.getNestedKey(Class clazz, String
path)?

3. I have examples of intentionally not following type-safing for
expressions (these are real examples)

  a. EF.matchExp("artist", "200") - quick comparison of relation object's id
  b. EF.lessExp("date", "2010-01-01 10:00:00.000") - replacement of date (or
int, double, whatever) with just string

Will those be allowed somehow in new API?

Happy New Year!!!

2009/12/30 Andrus Adamchik <an...@objectstyle.org>

> While there's lots of things we need to figure out to create generified
> queries, here is one low-hanging fruit - type-safe qualifiers. The idea is
> borrowed from the WebObjects EOF extensions framework called Wonder:
>
>
> http://webobjects.mdimension.com/hudson/job/Wonder53/javadoc/er/extensions/eof/ERXKey.html
>
> The way it works, is that for each String property in a Persistent object,
> we generate an extra parameterized "key". E.g.:
>
> public class _Artist extends CayenneDataObject {
>
>        public static final String DATE_OF_BIRTH_PROPERTY = "dateOfBirth";
>        public static final Key<Date> DATE_OF_BIRTH = new Key<Date>(
>                        DATE_OF_BIRTH_PROPERTY);
>
>        public static final String NAME_PROPERTY = "name";
>        public static final Key<String> NAME = new
> Key<String>(NAME_PROPERTY);
>
>        public static final String PAINTINGS_PROPERTY = "paintings";
>        public static final Key<Painting> PAINTINGS = new Key<Painting>(
>                        PAINTINGS_PROPERTY);
> ...
>
> Key class is a builder of type-safe key-value expressions (see the link
> above for the type of methods that it has). I wrote a quick API-only
> prototype that is a replacement of ExpressionFactory. Here is an example of
> building the following Expression the old way and the new way:
>
>  Expression: name='X' and (painting.name='Y' or painting.name='Z' or
> painting.name='A')
>
> 1. Current API:
>
> Expression clause1 = ExpressionFactory.matchExp(Artist.NAME_PROPERTY, "X");
> Expression clause2 = ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY +
> "." + Painting.NAME_PROPERTY, "Y");
> Expression clause3 = ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY +
> "." + Painting.NAME_PROPERTY, "Z");
> Expression clause4 = ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY +
> "." + Painting.NAME_PROPERTY, "A");
>
> Expression clause23 = clause2.orExp(clause3);
> Expression clause234 = clause23.orExp(clause4);
> Expression qualifier = clause1.andExp(clause234);
>
> 2. New API:
>
> Expression clause1 = Artist.NAME.eq("X");
> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
> Expression clause3 = Artist.PAINTINGS.dot(Painting.NAME).eq("Z");
> Expression clause4 = Artist.PAINTINGS.dot(Painting.NAME).eq("A");
>
> Expression qualifier = Each.get(clause1, Any.get(clause2, clause3,
> clause4));
>
> As you see the new API is much tighter, and the first part of it is
> completely type-safe (generated "keys" ensure that we are matching against
> the right type of value, even in a multi-step path). The last line uses 2
> new Expression factories, "Each" and "Any", to quickly organize key-value
> expressions into a nested expression tree. I think this is as good as it can
> get within the constraints of the Java syntax.
>
> I'd like to see if a similar approach can be extended to query building.
> Although that'll be a entirely different level of effort. For one thing it
> may force us to reconcile EJQBL and SelectQuery into a single query
> (SelectQuery is functionally a subset of EJBQLQuery. But SelectQuery
> "compilation" into SQL is fairly optimized, while EJBQLQuery-to-SQL
> conversion involves a much longer and slower pipeline). Plus of course there
> are other obstacles that we discussed before (such as a variety of possible
> result types).
>
> Andrus
>
>
>
>
>
>


-- 
Andrey