Skip to content

Commit ef82051

Browse files
wog48GitHub Enterprise
authored andcommitted
IN operator at navigation properties (#369)
* IN operator at navigation properties * Update dependency
1 parent b951f00 commit ef82051

File tree

10 files changed

+420
-289
lines changed

10 files changed

+420
-289
lines changed

additionalWords.directory

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,4 @@ xml
7575
x
7676
y
7777
esc
78+
Clob
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
package com.sap.olingo.jpa.processor.core.filter;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
7+
import jakarta.persistence.criteria.Expression;
8+
import jakarta.persistence.criteria.From;
9+
import jakarta.persistence.criteria.Path;
10+
import jakarta.persistence.criteria.Subquery;
11+
12+
import org.apache.olingo.commons.api.edm.EdmType;
13+
import org.apache.olingo.commons.api.http.HttpStatusCode;
14+
import org.apache.olingo.server.api.ODataApplicationException;
15+
import org.apache.olingo.server.api.uri.UriInfoResource;
16+
import org.apache.olingo.server.api.uri.UriResource;
17+
import org.apache.olingo.server.api.uri.UriResourceKind;
18+
import org.apache.olingo.server.api.uri.UriResourceProperty;
19+
import org.apache.olingo.server.api.uri.queryoption.ApplyOption;
20+
import org.apache.olingo.server.api.uri.queryoption.CountOption;
21+
import org.apache.olingo.server.api.uri.queryoption.CustomQueryOption;
22+
import org.apache.olingo.server.api.uri.queryoption.DeltaTokenOption;
23+
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
24+
import org.apache.olingo.server.api.uri.queryoption.FilterOption;
25+
import org.apache.olingo.server.api.uri.queryoption.FormatOption;
26+
import org.apache.olingo.server.api.uri.queryoption.IdOption;
27+
import org.apache.olingo.server.api.uri.queryoption.OrderByOption;
28+
import org.apache.olingo.server.api.uri.queryoption.SearchOption;
29+
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
30+
import org.apache.olingo.server.api.uri.queryoption.SkipOption;
31+
import org.apache.olingo.server.api.uri.queryoption.SkipTokenOption;
32+
import org.apache.olingo.server.api.uri.queryoption.TopOption;
33+
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
34+
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
35+
import org.apache.olingo.server.api.uri.queryoption.expression.Member;
36+
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
37+
import org.apache.olingo.server.api.uri.queryoption.expression.VisitableExpression;
38+
39+
import com.sap.olingo.jpa.processor.core.exception.ODataJPAFilterException;
40+
import com.sap.olingo.jpa.processor.core.exception.ODataJPAIllegalAccessException;
41+
import com.sap.olingo.jpa.processor.core.query.ExpressionUtility;
42+
import com.sap.olingo.jpa.processor.core.query.JPAAbstractQuery;
43+
import com.sap.olingo.jpa.processor.core.query.JPAAbstractSubQuery;
44+
import com.sap.olingo.jpa.processor.core.query.JPANavigationFilterQueryBuilder;
45+
import com.sap.olingo.jpa.processor.core.query.JPANavigationPropertyInfo;
46+
import com.sap.olingo.jpa.processor.core.query.JPANavigationPropertyInfoAccess;
47+
48+
/**
49+
* In case the query result shall be filtered on an attribute of navigation target a sub-select will be generated.
50+
*
51+
* @author Oliver Grande
52+
*
53+
*/
54+
abstract class JPAAbstractNavigationOperation extends JPAExistsOperation {
55+
56+
final BinaryOperatorKind operator;
57+
final JPAMemberOperator jpaMember;
58+
final MethodKind methodCall;
59+
private VisitableExpression expression;
60+
61+
JPAAbstractNavigationOperation(final JPAFilterComplierAccess jpaComplier, final MethodKind methodCall,
62+
final BinaryOperatorKind operator, final JPAMemberOperator jpaMember) {
63+
super(jpaComplier);
64+
this.operator = operator;
65+
this.methodCall = methodCall;
66+
this.jpaMember = jpaMember;
67+
}
68+
69+
@Override
70+
public Expression<Boolean> get() throws ODataApplicationException {
71+
try {
72+
final SubQueryItem existQuery = getExistsQuery();
73+
return ExpressionUtility.createSubQueryBasedExpression(existQuery.query(), existQuery.jpaPath(), converter.cb,
74+
expression);
75+
76+
} catch (final ODataJPAIllegalAccessException e) {
77+
throw new ODataJPAFilterException(e, HttpStatusCode.INTERNAL_SERVER_ERROR);
78+
}
79+
}
80+
81+
@Override
82+
public String getName() {
83+
return operator != null ? operator.name() : methodCall.name();
84+
}
85+
86+
@Override
87+
SubQueryItem getExistsQuery() throws ODataApplicationException, ODataJPAIllegalAccessException {
88+
final List<UriResource> allUriResourceParts = new ArrayList<>(uriResourceParts);
89+
allUriResourceParts.addAll(jpaMember.getMember().getResourcePath().getUriResourceParts());
90+
91+
// 1. Determine all relevant associations
92+
final List<JPANavigationPropertyInfo> navigationPathList = determineAssociations(sd, allUriResourceParts);
93+
JPAAbstractQuery parent = root;
94+
final List<JPAAbstractSubQuery> queryList = new ArrayList<>();
95+
96+
// 2. Create the queries and roots
97+
for (int i = navigationPathList.size() - 1; i >= 0; i--) {
98+
final JPANavigationPropertyInfoAccess navigationInfo = navigationPathList.get(i);
99+
if (i == 0) {
100+
expression = createExpression();
101+
queryList.add(new JPANavigationFilterQueryBuilder(converter.cb)
102+
.setOdata(odata)
103+
.setServiceDocument(sd)
104+
.setNavigationInfo(navigationInfo)
105+
.setParent(parent)
106+
.setEntityManager(em)
107+
.setExpression(expression)
108+
.setFrom(determineFrom(i, navigationPathList.size(), parent))
109+
.setParent(parent)
110+
.setClaimsProvider(claimsProvider)
111+
.setGroups(groups)
112+
.build());
113+
} else {
114+
queryList.add(new JPANavigationFilterQueryBuilder(converter.cb)
115+
.setOdata(odata)
116+
.setServiceDocument(sd)
117+
.setNavigationInfo(navigationInfo)
118+
.setParent(parent)
119+
.setEntityManager(em)
120+
.setFrom(determineFrom(i, navigationPathList.size(), parent))
121+
.setParent(parent)
122+
.setClaimsProvider(claimsProvider)
123+
.build());
124+
}
125+
parent = queryList.get(queryList.size() - 1);
126+
}
127+
// 3. Create select statements
128+
Subquery<List<Comparable<?>>> childQuery = null;
129+
List<Path<Comparable<?>>> inPath = Collections.emptyList();
130+
for (int i = queryList.size() - 1; i >= 0; i--) {
131+
childQuery = queryList.get(i).getSubQuery(childQuery, expression, inPath);
132+
inPath = queryList.get(i).getLeftPaths();
133+
}
134+
return new SubQueryItem(inPath, childQuery);
135+
}
136+
137+
Member getMember() {
138+
return new SubMember(jpaMember);
139+
}
140+
141+
abstract VisitableExpression createExpression() throws ODataJPAFilterException;
142+
143+
private From<?, ?> determineFrom(final int i, final int size, final JPAAbstractQuery parent) {
144+
return i == size - 1 ? from : parent.getRoot();
145+
}
146+
147+
private static class SubMember implements Member {
148+
private final JPAMemberOperator parentMember;
149+
150+
SubMember(final JPAMemberOperator parentMember) {
151+
super();
152+
this.parentMember = parentMember;
153+
}
154+
155+
@Override
156+
public <T> T accept(final ExpressionVisitor<T> visitor) throws ODataApplicationException {
157+
return null;
158+
}
159+
160+
@Override
161+
public UriInfoResource getResourcePath() {
162+
return new SubResource(parentMember);
163+
}
164+
165+
@Override
166+
public EdmType getStartTypeFilter() {
167+
return null;
168+
}
169+
170+
@Override
171+
public EdmType getType() {
172+
return null;
173+
}
174+
175+
@Override
176+
public boolean isCollection() {
177+
return false;
178+
}
179+
180+
}
181+
182+
private static class SubResource implements UriInfoResource {
183+
private final JPAMemberOperator parentMember;
184+
185+
public SubResource(final JPAMemberOperator member) {
186+
super();
187+
this.parentMember = member;
188+
}
189+
190+
@Override
191+
public ApplyOption getApplyOption() {
192+
return null;
193+
}
194+
195+
@Override
196+
public CountOption getCountOption() {
197+
return null;
198+
}
199+
200+
@Override
201+
public List<CustomQueryOption> getCustomQueryOptions() {
202+
return new ArrayList<>(0);
203+
}
204+
205+
@Override
206+
public DeltaTokenOption getDeltaTokenOption() {
207+
return null;
208+
}
209+
210+
@Override
211+
public ExpandOption getExpandOption() {
212+
return null;
213+
}
214+
215+
@Override
216+
public FilterOption getFilterOption() {
217+
return null;
218+
}
219+
220+
@Override
221+
public FormatOption getFormatOption() {
222+
return null;
223+
}
224+
225+
@Override
226+
public IdOption getIdOption() {
227+
return null;
228+
}
229+
230+
@Override
231+
public OrderByOption getOrderByOption() {
232+
return null;
233+
}
234+
235+
@Override
236+
public SearchOption getSearchOption() {
237+
return null;
238+
}
239+
240+
@Override
241+
public SelectOption getSelectOption() {
242+
return null;
243+
}
244+
245+
@Override
246+
public SkipOption getSkipOption() {
247+
return null;
248+
}
249+
250+
@Override
251+
public SkipTokenOption getSkipTokenOption() {
252+
return null;
253+
}
254+
255+
@Override
256+
public TopOption getTopOption() {
257+
return null;
258+
}
259+
260+
@Override
261+
public List<UriResource> getUriResourceParts() {
262+
final List<UriResource> result = new ArrayList<>();
263+
final List<UriResource> source = parentMember.getMember().getResourcePath().getUriResourceParts();
264+
for (int i = source.size() - 1; i > 0; i--) {
265+
if (source.get(i).getKind() == UriResourceKind.navigationProperty
266+
|| source.get(i).getKind() == UriResourceKind.entitySet
267+
|| (source.get(i) instanceof final UriResourceProperty resourceProperty
268+
&& resourceProperty.isCollection())) {
269+
break;
270+
}
271+
result.add(0, source.get(i));
272+
}
273+
return result;
274+
}
275+
276+
@Override
277+
public String getValueForAlias(final String alias) {
278+
return null;
279+
}
280+
281+
}
282+
283+
}

jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/filter/JPAFilterExpression.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.sap.olingo.jpa.processor.core.filter;
22

3+
import java.util.ArrayList;
4+
import java.util.List;
5+
36
import org.apache.olingo.server.api.ODataApplicationException;
47
import org.apache.olingo.server.api.uri.UriInfoResource;
58
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
@@ -12,21 +15,56 @@ public final class JPAFilterExpression implements JPAVisitableExpression {
1215
private final Literal literal;
1316
private final BinaryOperatorKind operator;
1417
private final Member member;
18+
private final List<Literal> literals;
1519

1620
public JPAFilterExpression(final Member member, final Literal literal, final BinaryOperatorKind operator) {
1721
super();
1822
this.literal = literal;
1923
this.operator = operator;
2024
this.member = member;
25+
this.literals = List.of();
26+
}
27+
28+
public JPAFilterExpression(final Member member, final List<Literal> literals, final BinaryOperatorKind operator) {
29+
super();
30+
this.literal = null;
31+
this.operator = operator;
32+
this.member = member;
33+
this.literals = literals;
2134
}
2235

2336
@Override
2437
public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
38+
2539
final T left = visitor.visitMember(member);
26-
final T right = visitor.visitLiteral(literal);
27-
return visitor.visitBinaryOperator(operator, left, right);
40+
if (literal != null) {
41+
final T right = visitor.visitLiteral(literal);
42+
return visitor.visitBinaryOperator(operator, left, right);
43+
} else {
44+
final List<T> right = new ArrayList<>(literals.size());
45+
for (final Literal l : literals)
46+
right.add(visitor.visitLiteral(l));
47+
return visitor.visitBinaryOperator(operator, left, right);
48+
}
49+
2850
}
2951

52+
// @Override
53+
// public <T> T accept(final ExpressionVisitor<T> visitor) throws ExpressionVisitException, ODataApplicationException {
54+
// T localLeft = this.left.accept(visitor);
55+
// if (this.right != null) {
56+
// T localRight = this.right.accept(visitor);
57+
// return visitor.visitBinaryOperator(operator, localLeft, localRight);
58+
// } else if (this.expressions != null) {
59+
// List<T> expressions = new ArrayList<>();
60+
// for (final Expression expression : this.expressions) {
61+
// expressions.add(expression.accept(visitor));
62+
// }
63+
// return visitor.visitBinaryOperator(operator, localLeft, expressions);
64+
// }
65+
// return null;
66+
// }
67+
3068
@Override
3169
public UriInfoResource getMember() {
3270
return member.getResourcePath();

0 commit comments

Comments
 (0)