Skip to content

Commit d29956f

Browse files
committed
Querydsl support. (#1330)
Closes #1288.
1 parent 9d449fa commit d29956f

32 files changed

+3245
-137
lines changed

pom.xml

+41
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
<jodatime>2.10.13</jodatime>
2626
<jakarta.validation>3.0.1</jakarta.validation>
2727
<hibernate.validator>7.0.1.Final</hibernate.validator>
28+
<apt>1.1.3</apt>
29+
<querydsl>5.0.0</querydsl>
30+
<mysema.querydsl>3.7.4</mysema.querydsl>
2831
</properties>
2932

3033
<dependencyManagement>
@@ -40,6 +43,13 @@
4043
</dependencyManagement>
4144

4245
<dependencies>
46+
47+
<dependency>
48+
<groupId>com.querydsl</groupId>
49+
<artifactId>querydsl-apt</artifactId>
50+
<version>${querydsl}</version>
51+
</dependency>
52+
4353
<dependency>
4454
<groupId>org.springframework</groupId>
4555
<artifactId>spring-context-support</artifactId>
@@ -135,6 +145,13 @@
135145
<optional>true</optional>
136146
</dependency>
137147

148+
<dependency>
149+
<groupId>javax.el</groupId>
150+
<artifactId>javax.el-api</artifactId>
151+
<version>3.0.0</version>
152+
<scope>test</scope>
153+
</dependency>
154+
138155
<dependency>
139156
<groupId>org.glassfish</groupId>
140157
<artifactId>jakarta.el</artifactId>
@@ -283,6 +300,30 @@
283300
<groupId>org.asciidoctor</groupId>
284301
<artifactId>asciidoctor-maven-plugin</artifactId>
285302
</plugin>
303+
<plugin>
304+
<groupId>com.mysema.maven</groupId>
305+
<artifactId>apt-maven-plugin</artifactId>
306+
<version>${apt}</version>
307+
<dependencies>
308+
<dependency>
309+
<groupId>com.querydsl</groupId>
310+
<artifactId>querydsl-apt</artifactId>
311+
<version>${querydsl}</version>
312+
</dependency>
313+
</dependencies>
314+
<executions>
315+
<execution>
316+
<phase>generate-test-sources</phase>
317+
<goals>
318+
<goal>test-process</goal>
319+
</goals>
320+
<configuration>
321+
<outputDirectory>target/generated-test-sources</outputDirectory>
322+
<processor>org.springframework.data.couchbase.repository.support.CouchbaseAnnotationProcessor</processor>
323+
</configuration>
324+
</execution>
325+
</executions>
326+
</plugin>
286327
</plugins>
287328
</build>
288329
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.querydsl.couchbase.document;
17+
18+
import java.util.Collection;
19+
import java.util.HashMap;
20+
import java.util.Iterator;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
import org.jetbrains.annotations.Nullable;
25+
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
26+
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
27+
28+
import com.querydsl.core.DefaultQueryMetadata;
29+
import com.querydsl.core.JoinExpression;
30+
import com.querydsl.core.QueryMetadata;
31+
import com.querydsl.core.QueryModifiers;
32+
import com.querydsl.core.SimpleQuery;
33+
import com.querydsl.core.support.QueryMixin;
34+
import com.querydsl.core.types.Expression;
35+
import com.querydsl.core.types.ExpressionUtils;
36+
import com.querydsl.core.types.FactoryExpression;
37+
import com.querydsl.core.types.Operation;
38+
import com.querydsl.core.types.OrderSpecifier;
39+
import com.querydsl.core.types.ParamExpression;
40+
import com.querydsl.core.types.Path;
41+
import com.querydsl.core.types.Predicate;
42+
43+
/**
44+
* renamed from AbstractCouchbaseQuery to AbstractCouchbaseQueryDSL to avoid confusion with the AbstractCouchbaseQuery
45+
* that is in the package com.querydsl.couchbase
46+
*
47+
* @author Michael Reiche
48+
*/
49+
50+
public abstract class AbstractCouchbaseQueryDSL<Q extends AbstractCouchbaseQueryDSL<Q>> implements SimpleQuery<Q> {
51+
private final CouchbaseDocumentSerializer serializer;
52+
private final QueryMixin<Q> queryMixin;// = new QueryMixin(this, new DefaultQueryMetadata(), false);
53+
// TODO private ReadPreference readPreference;
54+
55+
public AbstractCouchbaseQueryDSL(CouchbaseDocumentSerializer serializer) {
56+
this.serializer = serializer;
57+
@SuppressWarnings("unchecked") // Q is this plus subclass
58+
Q query = (Q) this;
59+
this.queryMixin = new QueryMixin<Q>(query, new DefaultQueryMetadata(), false);
60+
}
61+
62+
/**
63+
* mongodb uses createQuery(Predicate filter) where the serializer creates the 'query' <br>
64+
* and then uses the result to create a BasicQuery with queryObject = result <br>
65+
* Couchbase Query has a 'criteria' which is a <br>
66+
* List<QueryCriteriaDefinition> criteria <br>
67+
* so we could create a List&lt;QueryCriteriaDefinition&gt; or an uber QueryCriteria that combines <br>
68+
* all the sub QueryDefinitions in the filter.
69+
*/
70+
protected QueryCriteriaDefinition createCriteria(Predicate predicate) {
71+
// mongodb uses createQuery(Predicate filter) where the serializer creates the 'queryObject' of the BasicQuery
72+
return predicate != null ? (QueryCriteriaDefinition) this.serializer.handle(predicate) : null;
73+
}
74+
75+
// TODO - need later
76+
// public <T> JoinBuilder<Q, T> join(Path<T> ref, Path<T> target) {
77+
// return new JoinBuilder(this.queryMixin, ref, target);
78+
// }
79+
80+
// public <T> JoinBuilder<Q, T> join(CollectionPathBase<?, T, ?> ref, Path<T> target) {
81+
// return new JoinBuilder(this.queryMixin, ref, target);
82+
// }
83+
84+
// public <T> AnyEmbeddedBuilder<Q> anyEmbedded(Path<? extends Collection<T>> collection, Path<T> target) {
85+
// return new AnyEmbeddedBuilder(this.queryMixin, collection);
86+
// }
87+
88+
@Nullable
89+
protected Predicate createFilter(QueryMetadata metadata) {
90+
Predicate filter;
91+
if (!metadata.getJoins().isEmpty()) {
92+
filter = ExpressionUtils.allOf(new Predicate[] { metadata.getWhere(), this.createJoinFilter(metadata) });
93+
} else {
94+
filter = metadata.getWhere();
95+
}
96+
return filter;
97+
}
98+
99+
@Nullable
100+
protected Predicate createJoinFilter(QueryMetadata metadata) {
101+
Map<Expression<?>, Predicate> predicates = new HashMap();
102+
List<JoinExpression> joins = metadata.getJoins();
103+
104+
for (int i = joins.size() - 1; i >= 0; --i) {
105+
JoinExpression join = (JoinExpression) joins.get(i);
106+
Path<?> source = (Path) ((Operation) join.getTarget()).getArg(0);
107+
Path<?> target = (Path) ((Operation) join.getTarget()).getArg(1);
108+
Predicate extraFilters = (Predicate) predicates.get(target.getRoot());
109+
Predicate filter = ExpressionUtils.allOf(new Predicate[] { join.getCondition(), extraFilters });
110+
List<? extends Object> ids = this.getIds(target.getType(), filter);
111+
if (ids.isEmpty()) {
112+
throw new AbstractCouchbaseQueryDSL.NoResults();
113+
}
114+
115+
Path<?> path = ExpressionUtils.path(String.class, source, "$id");
116+
predicates.merge(source.getRoot(),
117+
ExpressionUtils.in(path, (Collection) ids/* TODO was just ids without casting to Collection */),
118+
ExpressionUtils::and);
119+
}
120+
121+
Path<?> source = (Path) ((Operation) ((JoinExpression) joins.get(0)).getTarget()).getArg(0);
122+
return predicates.get(source.getRoot());
123+
}
124+
125+
private Predicate allOf(Collection<Predicate> predicates) {
126+
return predicates != null ? ExpressionUtils.allOf(predicates) : null;
127+
}
128+
129+
protected abstract List<Object> getIds(Class<?> var1, Predicate var2);
130+
131+
public Q distinct() {
132+
return this.queryMixin.distinct();
133+
}
134+
135+
public Q where(Predicate e) {
136+
return this.queryMixin.where(e);
137+
}
138+
139+
public Q where(Predicate... e) {
140+
return this.queryMixin.where(e);
141+
}
142+
143+
public Q limit(long limit) {
144+
return this.queryMixin.limit(limit);
145+
}
146+
147+
public Q offset(long offset) {
148+
return this.queryMixin.offset(offset);
149+
}
150+
151+
public Q restrict(QueryModifiers modifiers) {
152+
return this.queryMixin.restrict(modifiers);
153+
}
154+
155+
public Q orderBy(OrderSpecifier<?> o) {
156+
return this.queryMixin.orderBy(o);
157+
}
158+
159+
public Q orderBy(OrderSpecifier<?>... o) {
160+
return this.queryMixin.orderBy(o);
161+
}
162+
163+
public <T> Q set(ParamExpression<T> param, T value) {
164+
return this.queryMixin.set(param, value);
165+
}
166+
167+
protected Map<String, String> createProjection(Expression<?> projection) {
168+
if (projection instanceof FactoryExpression) {
169+
Map<String, String> obj = new HashMap();
170+
Iterator var3 = ((FactoryExpression) projection).getArgs().iterator();
171+
172+
while (var3.hasNext()) {
173+
Object expr = var3.next();
174+
if (expr instanceof Expression) {
175+
obj.put(expr.toString(), (String) this.serializer.handle((Expression) expr));
176+
}
177+
}
178+
return obj;
179+
} else {
180+
return null;
181+
}
182+
}
183+
184+
protected CouchbaseDocument createQuery(@Nullable Predicate predicate) {
185+
return predicate != null ? (CouchbaseDocument) this.serializer.handle(predicate) : new CouchbaseDocument();
186+
}
187+
188+
// public void setReadPreference(ReadPreference readPreference) {
189+
// this.readPreference = readPreference;
190+
// }
191+
192+
protected QueryMixin<Q> getQueryMixin() {
193+
return this.queryMixin;
194+
}
195+
196+
protected CouchbaseDocumentSerializer getSerializer() {
197+
return this.serializer;
198+
}
199+
200+
// protected ReadPreference getReadPreference() {
201+
// return this.readPreference;
202+
// }
203+
204+
public CouchbaseDocument asDocument() {
205+
return this.createQuery(this.queryMixin.getMetadata().getWhere());
206+
}
207+
208+
public String toString() {
209+
return this.asDocument().toString();
210+
}
211+
212+
static class NoResults extends RuntimeException {
213+
NoResults() {}
214+
}
215+
}

0 commit comments

Comments
 (0)