improved path expressions and modeled them like operations

This commit is contained in:
Timo Westkämper 2008-03-29 00:22:09 +00:00
parent b802d653aa
commit b0bdb9aed2
9 changed files with 291 additions and 87 deletions

View File

@ -1,4 +1,4 @@
#Tue Mar 25 18:08:01 EET 2008
#Sat Mar 29 01:18:48 EET 2008
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.source=1.5

View File

@ -5,11 +5,14 @@
*/
package com.mysema.query.grammar;
import java.util.Collection;
import java.util.Date;
import com.mysema.query.Query;
import com.mysema.query.QueryBase;
import com.mysema.query.grammar.HqlOps.HqlPathType;
import com.mysema.query.grammar.HqlOps.OpHql;
import com.mysema.query.grammar.HqlOps.OpNumberAgg;
import com.mysema.query.grammar.HqlOps.OpQuant;
import com.mysema.query.grammar.Ops.Op;
import com.mysema.query.grammar.Types.*;
@ -21,7 +24,7 @@ import com.mysema.query.grammar.Types.*;
* @version $Id$
*/
public class HqlGrammar extends Grammar{
public static <D> Expr<D> all(CollectionType<D> col){
return new ExprQuantSimple<D>(OpQuant.ALL, col);
}
@ -36,36 +39,44 @@ public class HqlGrammar extends Grammar{
return new ExprQuantComparable<D>(OpQuant.ANY, col);
}
public static <A extends Comparable<A>> ExprComparable<A> avg(Expr<A> left){
return _number(OpNumberAgg.AVG, left);
}
public static <A extends Comparable<A>> ExprComparable<A> avg(PathCollection<A> left){
return new ExprQuantComparable<A>(OpQuant.AVG_IN_COL, left);
}
public static ExprComparable<Long> count(){
return new CountExpr(null);
}
}
public static ExprComparable<Long> count(Expr<?> expr){
return new CountExpr(expr);
}
public static ExprComparable<Date> current_date(){
return _comparable(OpHql.CURRENT_DATE);
}
}
public static ExprComparable<Date> current_time(){
return _comparable(OpHql.CURRENT_TIME);
}
public static ExprComparable<Date> current_timestamp(){
return _comparable(OpHql.CURRENT_TIMESTAMP);
}
public static ExprComparable<Date> day(Expr<Date> date){
return _comparable(OpHql.DAY, date);
}
public static ExprComparable<Date> day(Expr<Date> date){
return _comparable(OpHql.DAY, date);
}
public static <T> Expr<T> distinct(PathEntity<T> left){
return new DistinctPath<T>(left);
}
}
public static <T> Expr<T> distinct(PathNoEntity<T> left){
return new DistinctPath<T>(left);
}
}
public static <D> ExprBoolean exists(CollectionType<D> col){
return new ExprQuantBoolean<D>(OpQuant.EXISTS, col);
}
}
public static <A> SubQuery<A> from(ExprEntity<A> select){
return new SubQuery<A>(select).from(select);
@ -73,26 +84,72 @@ public class HqlGrammar extends Grammar{
public static ExprComparable<Date> hour(Expr<Date> date){
return _comparable(OpHql.HOUR, date);
}
public static PathComponentCollection<Integer> indices(PathCollection<?> col){
return new PathComponentCollection<Integer>(Integer.class, new PathMetadata<Collection<Integer>>(col, null, HqlPathType.LISTINDICES));
}
public static <K,V> PathComponentCollection<K> indices(PathMap<K,V> col){
return new PathComponentCollection<K>(col.getKeyType(), new PathMetadata<Collection<Integer>>(col, null, HqlPathType.LISTINDICES));
}
public static ExprBoolean isempty(PathComponentCollection<?> collection) {
return _boolean(OpHql.ISEMPTY, collection);
}
public static ExprBoolean isempty(PathEntityCollection<?> collection) {
return _boolean(OpHql.ISEMPTY, collection);
}
}
public static ExprBoolean isnotempty(PathComponentCollection<?> collection) {
return _boolean(OpHql.ISNOTEMPTY, collection);
}
public static ExprBoolean isnotempty(PathEntityCollection<?> collection) {
return _boolean(OpHql.ISNOTEMPTY, collection);
}
public static ExprComparable<Integer> maxindex(PathEntityCollection<?> collection) {
return _comparable(OpHql.MAXINDEX, collection);
public static <A extends Comparable<A>> ExprComparable<A> max(Expr<A> left){
return _number(OpNumberAgg.MAX, left);
}
public static <A extends Comparable<A>> ExprComparable<A> max(PathCollection<A> left){
return new ExprQuantComparable<A>(OpQuant.MAX_IN_COL, left);
}
public static <A> PathEntity<A> maxelement(PathEntityCollection<A> col) {
return new PathEntity<A>(col.getElementType(), new PathMetadata<A>(col, null, HqlPathType.MINELEMENT));
}
public static ExprComparable<Integer> minindex(PathEntityCollection<?> collection) {
return _comparable(OpHql.MININDEX, collection);
public static <A> PathComparable<Integer> maxindex(PathComponentCollection<A> col) {
return new PathComparable<Integer>(Integer.class, new PathMetadata<Integer>(col, null, HqlPathType.MAXINDEX));
}
public static <A> PathComparable<Integer> maxindex(PathEntityCollection<A> col) {
return new PathComparable<Integer>(Integer.class, new PathMetadata<Integer>(col, null, HqlPathType.MAXINDEX));
}
public static <A extends Comparable<A>> ExprComparable<A> min(Expr<A> left){
return _number(OpNumberAgg.MIN, left);
}
public static <A extends Comparable<A>> ExprComparable<A> min(PathCollection<A> left){
return new ExprQuantComparable<A>(OpQuant.MIN_IN_COL, left);
}
public static <A> PathEntity<A> minelement(PathEntityCollection<A> col) {
return new PathEntity<A>(col.getElementType(), new PathMetadata<A>(col, null, HqlPathType.MINELEMENT));
}
public static <A> PathComparable<Integer> minindex(PathComponentCollection<A> col) {
return new PathComparable<Integer>(Integer.class, new PathMetadata<Integer>(col, null, HqlPathType.MININDEX));
}
public static <A> PathComparable<Integer> minindex(PathEntityCollection<A> col) {
return new PathComparable<Integer>(Integer.class, new PathMetadata<Integer>(col, null, HqlPathType.MININDEX));
}
public static ExprComparable<Date> minute(Expr<Date> date){
return _comparable(OpHql.MINUTE, date);
}
}
public static ExprComparable<Date> month(Expr<Date> date){
return _comparable(OpHql.MONTH, date);

View File

@ -7,6 +7,9 @@ package com.mysema.query.grammar;
import java.util.*;
import com.mysema.query.grammar.PathMetadata.PathType;
import com.mysema.query.grammar.PathMetadata.PathTypeImpl;
/**
* Ops provides
*
@ -59,9 +62,10 @@ public class HqlOps extends Ops {
add(OpNumber.SUB, "%s - %s",12);
add(OpNumber.SQRT, "sqrt(%s)");
add(OpNumber.AVG, "avg(%s)");
add(OpNumber.MAX, "max(%s)");
add(OpNumber.MIN, "min(%s)");
// numeric aggregates
add(OpNumberAgg.AVG, "avg(%s)");
add(OpNumberAgg.MAX, "max(%s)");
add(OpNumberAgg.MIN, "min(%s)");
// various
add(Op.EQ, "%s = %s",18);
@ -71,7 +75,7 @@ public class HqlOps extends Ops {
add(Op.NOTIN, "%s not in %s");
add(Op.ISNULL, "%s is null",26);
add(Op.ISNOTNULL, "%s is not null",26);
add(Op.SIZE, "size(%s)");
// add(Op.SIZE, "size(%s)");
// string
add(OpString.CONCAT, "%s || %s",37);
@ -94,14 +98,32 @@ public class HqlOps extends Ops {
add(OpHql.DAY, "day(%s)");
add(OpHql.MONTH, "month(%s)");
add(OpHql.YEAR, "year(%s)");
add(OpHql.MAXINDEX, "maxindex(%s)");
add(OpHql.MININDEX, "minindex(%s)");
// quantified expressions
add(OpQuant.AVG_IN_COL, "avg(%s)");
add(OpQuant.MAX_IN_COL, "max(%s)");
add(OpQuant.MIN_IN_COL, "min(%s)");
add(OpQuant.ANY, "any %s");
add(OpQuant.ALL, "all %s");
add(OpQuant.EXISTS, "exists %s");
add(OpQuant.NOTEXISTS, "not exists %s");
// path types
for (PathType type : new PathType[]{PathType.LISTVALUE, PathType.LISTVALUE_CONSTANT, PathType.MAPVALUE, PathType.MAPVALUE_CONSTANT}){
add(type,"%s[%s]");
}
add(PathType.PROPERTY,"%s.%s"); // TODO : as string
add(PathType.SIZE,"%s.size");
add(PathType.VARIABLE,"%s"); // TODO : as string
// HQL types
add(HqlPathType.MINELEMENT, "minelement(%s)");
add(HqlPathType.MAXELEMENT, "max(%s");
add(HqlPathType.MININDEX, "minelement(%s)");
add(HqlPathType.MAXINDEX, "minelement(%s)");
add(HqlPathType.LISTINDICES, "indices(%s)");
add(HqlPathType.MAPINDICES, "indices(%s)");
}
private static void add(Op<?> op, String pattern){
@ -124,8 +146,8 @@ public class HqlOps extends Ops {
return -1;
}
}
public interface OpHql<RT>{
public interface OpHql{
Op<Date> CURRENT_DATE = new OpImpl<Date>();
Op<Date> CURRENT_TIME = new OpImpl<Date>();
Op<Date> CURRENT_TIMESTAMP = new OpImpl<Date>();
@ -133,8 +155,6 @@ public class HqlOps extends Ops {
Op<Date> HOUR = new OpImpl<Date>();
Op<Boolean> ISEMPTY = new OpImpl<Boolean>();
Op<Boolean> ISNOTEMPTY = new OpImpl<Boolean>();
Op<Object> MAXINDEX = new OpImpl<Object>();
Op<Object> MININDEX = new OpImpl<Object>();
Op<Date> MINUTE = new OpImpl<Date>();
Op<Date> MONTH = new OpImpl<Date>();
Op<Date> SECOND = new OpImpl<Date>();
@ -143,16 +163,34 @@ public class HqlOps extends Ops {
Op<Date> YEAR = new OpImpl<Date>();
}
@SuppressWarnings("unchecked")
public interface OpQuant<RT>{
public interface OpNumberAgg{
Op<Number> AVG = new OpImpl<Number>();
Op<Number> MAX = new OpImpl<Number>();
Op<Number> MIN = new OpImpl<Number>();
}
public interface OpQuant{
Op<Number> AVG_IN_COL = new OpImpl<Number>();
Op<Number> MAX_IN_COL = new OpImpl<Number>();
Op<Number> MIN_IN_COL = new OpImpl<Number>();
// some / any = true for any
// all = true for all
// exists = true is subselect matches
// not exists = true if subselect doesn't match
Op<?> ANY = new OpImpl();
Op<?> ALL = new OpImpl();
Op<?> EXISTS = new OpImpl();
Op<?> NOTEXISTS = new OpImpl();
Op<?> ANY = new OpImpl<Object>();
Op<?> ALL = new OpImpl<Object>();
Op<?> EXISTS = new OpImpl<Object>();
Op<?> NOTEXISTS = new OpImpl<Object>();
}
public interface HqlPathType{
PathType MINELEMENT = new PathTypeImpl();
PathType MAXELEMENT = new PathTypeImpl();
PathType MININDEX = new PathTypeImpl();
PathType MAXINDEX = new PathTypeImpl();
PathType LISTINDICES = new PathTypeImpl();
PathType MAPINDICES = new PathTypeImpl();
}
}

View File

@ -15,6 +15,8 @@ import com.mysema.query.grammar.HqlGrammar.*;
import com.mysema.query.grammar.Ops.Op;
import com.mysema.query.grammar.Types.*;
import com.mysema.query.grammar.PathMetadata.PathType;
/**
* HqlSerializer provides
*
@ -71,14 +73,12 @@ public class HqlSerializer extends VisitorAdapter<HqlSerializer>{
JoinExpression je = joins.get(i);
if (i > 0){
String sep = ", ";
if (je.getTarget() instanceof AliasToPath){
switch(je.getType()){
case FULLJOIN: sep = "\n full join "; break;
case INNERJOIN: sep = "\n inner join "; break;
case JOIN: sep = "\n join "; break;
case LEFTJOIN: sep = "\n left join "; break;
}
}
_append(sep);
}
// type specifier
@ -186,20 +186,26 @@ public class HqlSerializer extends VisitorAdapter<HqlSerializer>{
@Override
protected void visit(Path<?> path) {
PathType pathType = path.getMetadata().getPathType();
String parentAsString = null, exprAsString = null;
if (path.getMetadata().getParent() != null){
visit(path.getMetadata().getParent());
parentAsString = _toString((Expr<?>)path.getMetadata().getParent(),false);
}
if (pathType == PathType.PROPERTY || pathType == PathType.VARIABLE ||
pathType == PathType.LISTVALUE_CONSTANT){
exprAsString = path.getMetadata().getExpression().toString();
}else if (path.getMetadata().getExpression() != null){
exprAsString = _toString(path.getMetadata().getExpression(),false);
}
Expr<?> expr = path.getMetadata().getExpression();
switch(path.getMetadata().getType()){
case LISTACCESS :
case MAPACCESS : _append("[").handle(expr)._append("]"); break;
case LISTACCESSC : _append("[")._append(expr.toString())._append("]"); break;
case MAPACCESSC : _append("[").handle(expr)._append("]"); break;
case MAXELEMENT : handle(expr)._append(".maxelement()"); break;
case MINELEMENT : handle(expr)._append(".minelement()"); break;
case PROPERTY : _append(".")._append(expr.toString()); break;
case VARIABLE : _append(expr.toString()); break;
String pattern = HqlOps.getPattern(pathType);
if (parentAsString != null){
_append(String.format(pattern, parentAsString, exprAsString));
}else{
_append(String.format(pattern, exprAsString));
}
}
@Override

View File

@ -15,7 +15,10 @@ import com.mysema.query.grammar.hql.HqlParserTest;
* @version $Id$
*/
@RunWith(Suite.class)
@SuiteClasses({FeaturesTest.class,HqlIntegrationTest.class,HqlParserTest.class})
@SuiteClasses({
FeaturesTest.class,
HqlIntegrationTest.class,
HqlParserTest.class})
public class AllTests {
}

View File

@ -31,10 +31,12 @@ public interface Domain1Instances {
Cat qat = new Cat("qat");
Cat rival = new Cat("rival");
doofus d = new doofus("d");
Catalog catalog = new Catalog("catalog");
Customer cust = new Customer("cust");
doofus d = new doofus("d");
Foo foo = new Foo("foo");
Formula form = new Formula("form");
@ -59,7 +61,16 @@ public interface Domain1Instances {
Player player = new Player("player");
Price price = new Price("price");
Product prod = new Product("prod");
Product product = new Product("product");
Show show = new Show("show");
Status status = new Status("status");
StatusChange statusChange = new StatusChange("statusChange");
Store store = new Store("store");

View File

@ -38,8 +38,7 @@ public class FeaturesTest extends HqlQueryBase<FeaturesTest>{
public void testBasicStructure(){
assertNull(cat.getMetadata().getParent());
assertEquals(cat, cat.alive.getMetadata().getParent());
assertEquals("cat", cat.getMetadata().getExpression().toString());
assertEquals("cat", cat.getMetadata().getExpression().toString());
}
@Test
@ -114,13 +113,15 @@ public class FeaturesTest extends HqlQueryBase<FeaturesTest>{
@Test
public void testCollectionOperations(){
// HQL functions that take collection-valued path expressions: size(), minelement(), maxelement(), minindex(), maxindex(), along with the special elements() and indices functions which may be quantified using some, all, exists, any, in.
// size(cat.kittens);
// minelement(cat.kittens);
// maxelement(cat.kittens);
cat.kittens.size();
minelement(cat.kittens);
maxelement(cat.kittens);
minindex(cat.kittens);
maxindex(cat.kittens);
toString("cat.kittens[0]",cat.kittens(0));
toString("cat.kittens[0]",cat.kittens.get(0));
toString("cat.kittens[0]",cat.kittens.get(0));
// some, all, exists, any, in.
}
@Test

View File

@ -87,9 +87,9 @@ public class HqlDomain {
@Entity
public static class Customer {
@Id int id;
@ManyToOne Name name;
@ManyToOne Order currentOrder;
@Id int id;
@ManyToOne Name name;
}
@Entity
@ -112,6 +112,12 @@ public class HqlDomain {
}
@Entity
public static class doofus{
String gob;
@Id long id;
}
@Entity
public static class Employee {
@ManyToOne Company company;
@ -121,11 +127,11 @@ public class HqlDomain {
@Entity
public static class EvilType {
@ManyToOne @JoinColumn(name="_asc") EvilType asc;
@ManyToOne @JoinColumn(name="_desc") EvilType desc;
@Id int id;
@ManyToOne EvilType isnull, isnotnull, get, getType, getMetadata;
@ManyToOne EvilType toString, hashCode, getClass, notify, notifyAll, wait;
@ManyToOne @JoinColumn(name="_asc") EvilType asc;
@ManyToOne @JoinColumn(name="_desc") EvilType desc;
}
@DTO
@ -140,6 +146,7 @@ public class HqlDomain {
public static class Foo {
String bar;
@Id int id;
@CollectionOfElements List<String> names;
java.util.Date startDate;
public Foo(){}
public Foo(long l){}
@ -170,18 +177,18 @@ public class HqlDomain {
@Id long id;
}
@Entity
public static class NameList{
@Id long id;
@CollectionOfElements Collection<String> names;
}
@Entity
public static class Named {
@Id long id;
String name;
}
@Entity
public static class NameList{
@Id long id;
@CollectionOfElements Collection<String> names;
}
@Entity
public static class Nationality {
@ManyToOne Calendar calendar;
@ -204,7 +211,13 @@ public class HqlDomain {
@Entity
public static class Payment extends Item{
@ManyToOne Status currentStatus, status;
PaymentStatus name;
@OneToMany Collection<StatusChange> statusChanges;
}
public enum PaymentStatus{
AWAITING_APPROVAL
}
@Entity
@ -212,8 +225,8 @@ public class HqlDomain {
java.util.Date birthDay;
@Id long i;
@ManyToOne PersonId id;
@ManyToOne Nationality nationality;
String name;
@ManyToOne Nationality nationality;
}
@Entity
@ -237,11 +250,29 @@ public class HqlDomain {
}
@Entity
public static class Product {
public static class Product extends Item{
// @Id long id;
String name;
}
@Entity
public static class Show {
@CollectionOfElements Map<String,String> acts;
@Id int id;
}
@Entity
public static class Status {
@Id long id;
String name;
}
@Entity
public static class StatusChange {
@Id long id;
java.util.Date timeStamp;
}
@Entity
public static class Store {
@OneToMany List<Customer> customers;
@ -249,12 +280,6 @@ public class HqlDomain {
@ManyToOne Location location;
}
@Entity
public static class doofus{
@Id long id;
String gob;
}
@Entity
public static class User {
@ManyToOne Company company;

View File

@ -1,6 +1,10 @@
package com.mysema.query.grammar.hql;
import static com.mysema.query.grammar.Grammar.*;
import static com.mysema.query.grammar.Grammar.div;
import static com.mysema.query.grammar.Grammar.in;
import static com.mysema.query.grammar.Grammar.not;
import static com.mysema.query.grammar.Grammar.sqrt;
import static com.mysema.query.grammar.Grammar.sub;
import static com.mysema.query.grammar.HqlGrammar.*;
import static org.junit.Assert.assertEquals;
@ -14,7 +18,9 @@ import antlr.RecognitionException;
import antlr.TokenStreamException;
import antlr.collections.AST;
import com.mysema.query.Domain1;
import com.mysema.query.Domain1Dtos;
import com.mysema.query.Domain1.Catalog;
import com.mysema.query.grammar.HqlGrammar;
import com.mysema.query.grammar.HqlQueryBase;
import com.mysema.query.grammar.hql.HqlDomain.Color;
@ -185,9 +191,10 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
from(cat).where(cat.kittens.size().gt(0)).parse();
// parse( "from Order ord where maxindex(ord.items) > 100" );
from(ord).where(maxindex(ord.items).gt(100)).parse();
// parse( "from Order ord where minelement(ord.items) > 10000" );
// from(ord).where(ord.items.minelement().gt(10000)).parse();
//
// NOTE : Invalid query
// parse( "select mother from eg.Cat as mother, eg.Cat as kit\n"
// + "where kit in elements(foo.kittens)" );
select(mother).from(mother, kit).where(kit.in(mother.kittens)).parse();
@ -199,7 +206,7 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
// parse( "from eg.Player p where 3 > all elements(p.scores)" );
from(player).where(all(player.scores).lt(3)).parse();
// parse( "from eg.Show show where 'fizard' in indices(show.acts)" );
// TODO
from(show).where(in("fizard",indices(show.acts))).parse();
// parse( "from Order ord where ord.items[0].id = 1234" );
from(ord).where(ord.items(0).id.eq(1234l)).parse();
// parse( "select person from Person person, Calendar calendar\n"
@ -227,11 +234,16 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
// + "where prod.name = 'widget'\n"
// + "and store.location.name in ( 'Melbourne', 'Sydney' )\n"
// + "and prod = all elements(cust.currentOrder.lineItems)" );
// select(cust).from(prod, store).innerJoin(store.customers.as(cust))
// .where(prod.name.eq("widget")
// .and(store.location().name.in("Melbourne","Sydney"))
// .and(prod.eq(all(cust.currentOrder().lineItems)))
// ).parse();
select(cust).from(prod, store).innerJoin(store.customers.as(cust))
.where(prod.name.eq("widget")
.and(store.location().name.in("Melbourne","Sydney"))
.and(prod.eq(all(cust.currentOrder().lineItems)))
).parse();
prod.eq(new HqlDomain.Product());
prod.eq(new Domain1.Product("p"));
prod.eq(new Domain1.Item("p"));
}
@Test
@ -248,7 +260,7 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
select(cat.color, sum(cat.weight), count(cat)).from(cat).groupBy(cat.color).parse();
// parse( "select foo.id, avg( elements(foo.names) ), max( indices(foo.names) )\n"
// + "from eg.Foo foo group by foo.id" );
// TODO
select(foo.id, avg(foo.names), max(indices(foo.names))).from(foo).groupBy(foo.id);
// parse( "select cat.color, sum(cat.weight), count(cat)\n"
// + "from eg.Cat cat group by cat.color\n"
// + "having cat.color in (eg.Color.TABBY, eg.Color.BLACK)" );
@ -295,7 +307,24 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
// + "group by ord\n"
// + "having sum(price.amount) > :minAmount\n"
// + "order by sum(price.amount) desc" );
//
Catalog cat = new Catalog("cat");
select(ord.id, sum(price.amount), count(item))
.from(ord).join(ord.lineItems.as(item))
.join(item.product.as(product)).from(catalog)
.join(catalog.prices.as(price))
.where(not(ord.paid)
.and(ord.customer.eq(cust))
.and(price.product.eq(product))
.and(catalog.effectiveDate.lt(sysdate()))
.and(catalog.effectiveDate.gt(all(
HqlGrammar.select(catalog.effectiveDate).from(catalog)
.where(catalog.effectiveDate.lt(sysdate()))
))))
.groupBy(ord)
.having(sum(price.amount).gt(0l))
.orderBy(sum(price.amount).desc());
// parse( "select ord.id, sum(price.amount), count(item)\n"
// + "from Order as ord join ord.lineItems as item join item.product as product,\n"
// + "Catalog as catalog join catalog.prices as price\n"
@ -303,7 +332,17 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
// + "and price.product = product and catalog = :currentCatalog\n"
// + "group by ord having sum(price.amount) > :minAmount\n"
// + "order by sum(price.amount) desc" );
//
HqlDomain.Customer c1 = new HqlDomain.Customer();
HqlDomain.Catalog c2 = new HqlDomain.Catalog();
select(ord.id, sum(price.amount), count(item))
.from(ord).join(ord.lineItems.as(item)).join(item.product.as(product))
.from(catalog).join(catalog.prices.as(price))
.where(not(ord.paid).and(ord.customer.eq(c1))
.and(price.product.eq(product)).and(catalog.eq(c2)))
.groupBy(ord).having(sum(price.amount).gt(0l))
.orderBy(sum(price.amount).desc());
// parse( "select count(payment), status.name \n"
// + "from Payment as payment \n"
// + " join payment.currentStatus as status\n"
@ -319,6 +358,17 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
// + " )\n"
// + "group by status.name, status.sortOrder\n"
// + "order by status.sortOrder" );
// select(count(payment), status.name)
// .from(payment).join(payment.currentStatus.as(status))
// .join(payment.statusChanges.as(statusChange))
// .where(payment.status().name.ne(PaymentStatus.AWAITING_APPROVAL)
// .or(
// statusChange.timeStamp.eq(select(max(change.timestamp))
// .from(change).where(change.payment.eq(null)
// )))
// .groupBy(status.name.asc(), status.sortOrder.asc())
// .orderBy(status.sortOrder);
// parse( "select count(payment), status.name \n"
// + "from Payment as payment\n"
// + " join payment.currentStatus as status\n"
@ -326,12 +376,25 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
// + " or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser\n"
// + "group by status.name, status.sortOrder\n"
// + "order by status.sortOrder" );
HqlDomain.User currentUser = new HqlDomain.User();
// select(count(payment), status.name)
// .from(payment).join(payment.currentStatus.as(status))
// .where(payment.status().name.ne(PaymentStatus.AWAITING_APPROVAL)
// .or(payment.statusChanges(maxindex(payment.statusChanges)).user.ne(currentUser)))
// .groupBy(status.name, status.sortOrder)
// .orderBy(status.sortOrder);
// parse( "select account, payment\n"
// + "from Account as account\n"
// + " left outer join account.payments as payment\n"
// + "where :currentUser in elements(account.holder.users)\n"
// + " and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)\n"
// + "order by account.type.sortOrder, account.accountNumber, payment.dueDate" );
// select(account, payment).from(account)
// .leftJoin(account.payments.as(payment))
// .where(in(currentUser, account.holder().users).and())
// parse( "select account, payment\n"
// + "from Account as account\n"
// + " join account.holder.users as user\n"
@ -357,7 +420,7 @@ public class HqlParserTest extends HqlQueryBase<HqlParserTest> implements Domain
@Test
public void testFromWithJoin() throws Exception {
// parse( "FROM eg.mypackage.Cat qat, com.toadstool.Foo f join net.sf.blurb.Blurb" );
from(qat,foo).join(cust).parse();
// NOTE : invalid query!
// parse( "FROM eg.mypackage.Cat qat left join com.multijoin.JoinORama , com.toadstool.Foo f join net.sf.blurb.Blurb" );
// TODO
}