On 14/01/10 8:10 PM, Andrus Adamchik wrote:
On Jan 13, 2010, at 2:38 PM, Aristedes Maniatis wrote:
I've tried to explain the current usage better in the docs, given what
I now understand. Please let me know if I misunderstood something. I
had originally thought they were there as shortcuts to a path string.
Actually we may start using them that purpose as well in 3.1 when and if
we merge SelectQuery and EJBQLQuery into a new SuperSelectQuery. Now I
think I remember that a possibility of unifying expressions with EJBQL
was considered when designing the API for splits. Hence aliases were
suggested.
A way think of aliases is that they are markers within part of an
expression which give Cayenne hints about merging. Like a query cache
key, they are important only if you use the same key twice. But
because aliases are created when you create the Expression path
string, and then expanded when you construct the query, they can be
awkward to use. It would be nicer if the Expression carried not only
the idea of the key, but also how it expands and passed that onto the
query.
Expression e1 = ExpressionFactory.like("path1|paintings.gallery.name",
"foo");
Expression e2 = ExpressionFactory.like("path2|paintings.gallery.name",
"bar");
This puts the definition of the alias and the expansion in the same
logical place. It would also allow
Expression e2 = ExpressionFactory.like("path2|", "bar");
to use the alias previously defined. Or to have Cayenne construct an
arbitrary random, non-reusable alias:
Expression e1 = ExpressionFactory.like("|paintings.gallery.name", "foo");
Then you could even define an expression
Expression e1 = Expression.fromString("|paintings.gallery.name = foo
and |paintings.gallery.name = bar")
So the name before the pipe is an alias, right? Then how do we create
splits in the middle of the path?
paintings.|gallery.name
You could even this which uses an existing alias:
paintings.path2|.name
or this which makes a named alias:
paintings.path2|gallery.name
But the syntax is prone to confusion because it is hard to read. On the plus
side though, I can't imagine why anyone would want to use named aliases in this
way, so they are likely to be rare.
I guess we can have a more complex
notation:
Artist.PAINTINGS.dot(Painting.EXHIBITS).alias("X").dot(Exhibit.GALLERIES).eq("Y");
This is a long way from obvious for the most common use of the syntax:
combining AND queries across a to-many join. Sure named aliases might be useful
in themselves, maybe, for someone. I can't personally think of a reason. But
the following seems clearer to me:
Artist.PAINTINGS.join(Painting.EXHIBITS,
Expression.SPLIT_PATH).join(Exhibit.GALLERIES).like("Y")
My overall preference is to make the expression functions slightly more
SQL-like, with words like join and like. Avoiding abstract ideas like 'alias'
where they aren't relevant to the user is part of that same goal. In fact
'alias' has a specific SQL common meaning (table aliases) which is only going
to confuse new users.
If you really want to name an alias:
Artist.PAINTINGS.join(Painting.EXHIBITS,
"aliasX").join(Exhibit.GALLERIES).like("Y")
or setting the path, specifying the number of segments:
Artist.PAINTINGS.dot(Painting.EXHIBITS).dot(Exhibit.GALLERIES).alias("X", 2);
Maybe as an option, but I'd never use it. Consider the method:
performAQuery(Expression e) {
e1 = e.and(somequalifier);
performMyQuery(e1.alias("X", 2); // what did that just do? How long was the
path expression passed to the method?
}
(s/dot/join/ or whatever)
or the old way:
// this will probably require defining a special Expression subclass -
KeyValueCondition or something
// that ensures there's only a single path inside
ExpressionFactory.like("paintings.gallery.name", "foo").split("X", 2);
Just throw a runtime exception...
Or if you really wanted to, promote 'path expression' from being just a String
to having its own class. Then alias is just a special case of Path. Come to
think of it, what is the difference between your new Key typesafe class and
Path, other than a few dots...
So yeah, we are definitely making progress. One remaining case that is
still not accommodated is when subexpressions are created "in the
vacuum" with no knowledge of the overall query context. So we need to
take an existing (possibly nested) conditional expression and setup
correct splits for one or more of its paths. I guess some expression
transformer is needed here.
My guess is that 99% of use cases would be met with a parameter to pass into
the query which splits ALL paths. It would actually solve every instance of why
we started this conversation in the first splace.
Ari
--
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A