diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java index 074dc8d48..d8250b886 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java @@ -16,7 +16,6 @@ import com.mysema.query.QueryException; import com.mysema.query.QueryMetadata; import com.mysema.query.SearchResults; import com.mysema.query.support.ProjectableQuery; -import com.mysema.query.support.QueryMixin; import com.mysema.query.types.ArrayConstructorExpression; import com.mysema.query.types.CollectionExpression; import com.mysema.query.types.Expression; @@ -48,7 +47,7 @@ public abstract class AbstractColQuery> extends P @SuppressWarnings("unchecked") public AbstractColQuery(QueryMetadata metadata, QueryEngine queryEngine) { - super(new QueryMixin(metadata)); + super(new ColQueryMixin(metadata)); this.queryMixin.setSelf((Q) this); this.queryEngine = queryEngine; } diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryMixin.java b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryMixin.java new file mode 100644 index 000000000..c67a7858b --- /dev/null +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryMixin.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010 Mysema Ltd. + * All rights reserved. + * + */ +package com.mysema.query.collections; + +import com.mysema.query.BooleanBuilder; +import com.mysema.query.QueryMetadata; +import com.mysema.query.support.CollectionAnyVisitor; +import com.mysema.query.support.QueryMixin; +import com.mysema.query.types.CollectionExpression; +import com.mysema.query.types.Path; +import com.mysema.query.types.Predicate; +import com.mysema.query.types.template.BooleanTemplate; + +/** + * ColQueryMixin extends QueryMixin + * + * @author tiwe + * + * @param + */ +public class ColQueryMixin extends QueryMixin { + + public ColQueryMixin() {} + + public ColQueryMixin(QueryMetadata metadata) { + super(metadata); + } + + public ColQueryMixin(T self, QueryMetadata metadata) { + super(self, metadata); + } + + @Override + protected Predicate[] normalize(Predicate[] conditions, boolean where) { + for (int i = 0; i < conditions.length; i++){ + conditions[i] = normalize(conditions[i], where); + } + return conditions; + } + + @SuppressWarnings("unchecked") + private Predicate normalize(Predicate predicate, boolean where) { + if (predicate instanceof BooleanBuilder && ((BooleanBuilder)predicate).getValue() == null){ + return predicate; + }else{ + CollectionAnyVisitor.Context context = new CollectionAnyVisitor.Context(); + Predicate transformed = (Predicate) predicate.accept(CollectionAnyVisitor.DEFAULT, context); + for (int i = 0; i < context.anyPaths.size(); i++){ + innerJoin( + (CollectionExpression)context.anyPaths.get(i).getMetadata().getParent(), + (Path)context.replacements.get(i)); + on(BooleanTemplate.create("any")); + } + return transformed; + } + } +} diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryTemplates.java b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryTemplates.java index 95c6dcd97..146d03440 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryTemplates.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryTemplates.java @@ -63,6 +63,7 @@ public final class ColQueryTemplates extends JavaTemplates { add(PathType.LISTVALUE_CONSTANT, "{0}.get({1})"); add(PathType.ARRAYVALUE, "{0}[{1}]"); add(PathType.ARRAYVALUE_CONSTANT, "{0}[{1}]"); + add(PathType.COLLECTION_ANY, "{0}_any"); // coalesce add(Ops.COALESCE, functions + ".coalesce({0})"); diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultEvaluatorFactory.java b/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultEvaluatorFactory.java index 1187ffaf5..1d4bfd1ba 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultEvaluatorFactory.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/DefaultEvaluatorFactory.java @@ -34,7 +34,10 @@ import com.mysema.query.types.FactoryExpression; import com.mysema.query.types.Operation; import com.mysema.query.types.ParamExpression; import com.mysema.query.types.ParamNotSetException; +import com.mysema.query.types.PathType; import com.mysema.query.types.Predicate; +import com.mysema.query.types.Templates; +import com.mysema.query.types.ToStringVisitor; /** * DefaultEvaluatorFactory extends the EvaluatorFactory class to provide Java source @@ -52,7 +55,7 @@ public class DefaultEvaluatorFactory { public DefaultEvaluatorFactory(ColQueryTemplates templates){ // TODO : which ClassLoader to pick ?!? - this(templates, + this(templates, (URLClassLoader)DefaultEvaluatorFactory.class.getClassLoader(), ToolProvider.getSystemJavaCompiler()); } @@ -148,6 +151,8 @@ public class DefaultEvaluatorFactory { StringBuilder vars = new StringBuilder(); ColQuerySerializer ser = new ColQuerySerializer(templates); ser.append("java.util.List rv = new java.util.ArrayList();\n"); + + List anyJoinMatchers = new ArrayList(); // creating context for (JoinExpression join : joins){ @@ -164,8 +169,12 @@ public class DefaultEvaluatorFactory { sourceClasses.add(Iterable.class); }else if (join.getType() == JoinType.INNERJOIN){ - Operation alias = (Operation)join.getTarget(); - // TODO : handle join condition + Operation alias = (Operation)join.getTarget(); + if (join.getCondition() != null && join.getCondition().toString().equals("any")){ + String matcher = alias.getArg(1).toString() + "_matched"; + ser.append("boolean " + matcher + " = false;\n"); + anyJoinMatchers.add(matcher); + } ser.append("for ( " + typeName + " " + alias.getArg(1) + " : "); ser.handle(alias.getArg(0)); if (alias.getArg(0).getType().equals(Map.class)){ @@ -174,9 +183,6 @@ public class DefaultEvaluatorFactory { ser.append("){\n"); vars.append(alias.getArg(1)); -// }else if (join.getType() == JoinType.LEFTJOIN){ -// // TODO -// }else{ throw new IllegalArgumentException("Illegal join expression " + join); } @@ -184,7 +190,14 @@ public class DefaultEvaluatorFactory { // filter if (filter != null){ - ser.append("if (").handle(filter).append("){\n"); + ser.append("if ("); + for (String matcher : anyJoinMatchers){ + ser.append("!" + matcher + " && "); + } + ser.handle(filter).append("){\n"); + for (String matcher : anyJoinMatchers){ + ser.append(" "+ matcher + " = true;\n"); + } ser.append(" rv.add(new Object[]{"+vars+"});\n"); ser.append("}\n"); }else{ diff --git a/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java b/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java index e4a5a4d53..e715dd6f1 100644 --- a/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java +++ b/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java @@ -92,7 +92,8 @@ public class ColQueryStandardTest { @Test public void TupleProjection(){ - List tuples = MiniApi.from(cat, data).list(new QTuple(cat.name, cat.birthdate)); + List tuples = MiniApi.from(cat, data) + .list(new QTuple(cat.name, cat.birthdate)); for (Tuple tuple : tuples){ assertNotNull(tuple.get(cat.name)); assertNotNull(tuple.get(cat.birthdate)); @@ -102,7 +103,8 @@ public class ColQueryStandardTest { @SuppressWarnings("unchecked") @Test public void ArrayProjection(){ - List results = MiniApi.from(cat, data).list(new ArrayConstructorExpression(String[].class, cat.name)); + List results = MiniApi.from(cat, data) + .list(new ArrayConstructorExpression(String[].class, cat.name)); assertFalse(results.isEmpty()); for (String[] result : results){ assertNotNull(result[0]); @@ -111,7 +113,8 @@ public class ColQueryStandardTest { @Test public void ConstructorProjection(){ - List projections = MiniApi.from(cat, data).list(ConstructorExpression.create(Projection.class, cat.name, cat)); + List projections = MiniApi.from(cat, data) + .list(ConstructorExpression.create(Projection.class, cat.name, cat)); assertFalse(projections.isEmpty()); for (Projection projection : projections){ assertNotNull(projection); diff --git a/querydsl-collections/src/test/java/com/mysema/query/collections/Cat.java b/querydsl-collections/src/test/java/com/mysema/query/collections/Cat.java index 15b4685ee..0f4c6bb51 100644 --- a/querydsl-collections/src/test/java/com/mysema/query/collections/Cat.java +++ b/querydsl-collections/src/test/java/com/mysema/query/collections/Cat.java @@ -52,6 +52,11 @@ public class Cat extends Animal { this.kittensByName = Collections.singletonMap("Kitty", kitten); this.name = name; } + + public Cat(String name, String kittenName){ + this(name); + kittens.get(0).setName(kittenName); + } @QueryProjection public Cat(String name, int id) { diff --git a/querydsl-collections/src/test/java/com/mysema/query/collections/CollectionTest.java b/querydsl-collections/src/test/java/com/mysema/query/collections/CollectionTest.java new file mode 100644 index 000000000..603077fc3 --- /dev/null +++ b/querydsl-collections/src/test/java/com/mysema/query/collections/CollectionTest.java @@ -0,0 +1,56 @@ +package com.mysema.query.collections; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +public class CollectionTest { + + private final QCat cat = new QCat("cat"); + + private final QCat other = new QCat("other"); + + private List cats; + + @Before + public void setUp(){ + Cat cat1 = new Cat("1"); + cat1.setKittens(Arrays.asList(cat1)); + Cat cat2 = new Cat("2"); + cat2.setKittens(Arrays.asList(cat1, cat2)); + Cat cat3 = new Cat("3"); + cat3.setKittens(Arrays.asList(cat1, cat2, cat3)); + Cat cat4 = new Cat("4"); + cat4.setKittens(Arrays.asList(cat1, cat2, cat3, cat4)); + + cats = Arrays.asList(cat1, cat2, cat3, cat4); + } + + @Test + public void Join(){ + assertEquals("4", MiniApi.from(cat, cats).innerJoin(cat.kittens, other).where(other.name.eq("4")).uniqueResult(cat.name)); + } + + @Test + public void Any(){ + assertEquals("4", MiniApi.from(cat, cats).where(cat.kittens.any().name.eq("4")).uniqueResult(cat.name)); + } + + @Test + public void Any2(){ + assertEquals(4, MiniApi.from(cat, cats).where(cat.kittens.any().name.isNotNull()).count()); + } + + @Test + @Ignore + public void Any3(){ + // TODO : support multiple levels of any usage + assertEquals(4, MiniApi.from(cat, cats).where(cat.kittens.any().name.isNotNull(), cat.kittens.any().kittens.any().isNotNull()).count()); + } + +}