Skip to content

Commit c2ab8af

Browse files
authored
Handle orderBy in nested queries (#22)
* Fixed orderBy in nested queries
1 parent c7ceaf8 commit c2ab8af

File tree

6 files changed

+88
-107
lines changed

6 files changed

+88
-107
lines changed

graphql-jpa-query-boot-starter/.project

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@
1616
</arguments>
1717
</buildCommand>
1818
<buildCommand>
19-
<name>org.eclipse.m2e.core.maven2Builder</name>
19+
<name>org.springframework.ide.eclipse.core.springbuilder</name>
2020
<arguments>
2121
</arguments>
2222
</buildCommand>
2323
<buildCommand>
24-
<name>org.springframework.ide.eclipse.core.springbuilder</name>
24+
<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
2525
<arguments>
2626
</arguments>
2727
</buildCommand>
2828
<buildCommand>
29-
<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
29+
<name>org.eclipse.m2e.core.maven2Builder</name>
3030
<arguments>
3131
</arguments>
3232
</buildCommand>

graphql-jpa-query-example/.project

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@
1616
</arguments>
1717
</buildCommand>
1818
<buildCommand>
19-
<name>org.eclipse.m2e.core.maven2Builder</name>
19+
<name>org.springframework.ide.eclipse.core.springbuilder</name>
2020
<arguments>
2121
</arguments>
2222
</buildCommand>
2323
<buildCommand>
24-
<name>org.springframework.ide.eclipse.core.springbuilder</name>
24+
<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
2525
<arguments>
2626
</arguments>
2727
</buildCommand>
2828
<buildCommand>
29-
<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
29+
<name>org.eclipse.m2e.core.maven2Builder</name>
3030
<arguments>
3131
</arguments>
3232
</buildCommand>

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@
1818

1919
import java.lang.reflect.Member;
2020
import java.lang.reflect.Method;
21-
import java.util.Arrays;
2221
import java.util.List;
2322
import java.util.Optional;
2423
import java.util.stream.Collectors;
2524

26-
import javax.persistence.EntityGraph;
2725
import javax.persistence.EntityManager;
2826
import javax.persistence.TypedQuery;
2927
import javax.persistence.criteria.CriteriaBuilder;
@@ -37,7 +35,7 @@
3735

3836
import graphql.language.Argument;
3937
import graphql.language.Field;
40-
import graphql.language.SelectionSet;
38+
import graphql.language.Selection;
4139
import graphql.schema.DataFetchingEnvironment;
4240

4341
/**
@@ -63,40 +61,47 @@ public Object get(DataFetchingEnvironment environment) {
6361
Object source = environment.getSource();
6462
Optional<Argument> whereArg = extractArgument(environment, field, GraphQLJpaSchemaBuilder.QUERY_WHERE_PARAM_NAME);
6563

66-
// Resolve collection query if where argument is present
67-
if(whereArg.isPresent()) {
68-
// // Create entity graph from selection
69-
// EntityGraph<?> entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field))));
70-
//
71-
// try {
72-
// Object result = getQuery(environment, field, true)
73-
// .setHint("javax.persistence.fetchgraph", entityGraph)
74-
// .getSingleResult();
75-
//
76-
// return getAttributeValue(result, attribute);
77-
// } catch (NoResultException e) {
78-
// }
79-
//
80-
// return Collections.emptyList();
64+
// Resolve collection query if where argument is present or any field in selection has orderBy argument
65+
if(whereArg.isPresent() || hasSelectionAnyOrderBy(field)) {
8166

82-
EntityGraph<?> entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field))));
67+
//EntityGraph<?> entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field))));
8368

8469
return getQuery(environment, field, true)
70+
//.setHint("javax.persistence.fetchgraph", entityGraph) // TODO: fix runtime exception
8571
.getResultList();
86-
87-
// .stream()
88-
// .map(it -> (Tuple) it)
89-
// .map(tuple -> tuple.get(attribute.getName()))
90-
// .collect(Collectors.toList());
9172
}
9273

9374
// Let hibernate resolve collection query
9475
return getAttributeValue(source, attribute);
95-
96-
// Must do this to resolve where and orderBy on child fields
97-
// return getQuery(environment, field, true).getResultList();
9876
}
9977

78+
private boolean hasSelectionAnyOrderBy(Field field) {
79+
80+
if(!hasSelectionSet(field)) return false;
81+
82+
// Loop through all of the fields being requested
83+
for(Selection selection : field.getSelectionSet().getSelections()) {
84+
if (selection instanceof Field) {
85+
Field selectedField = (Field) selection;
86+
87+
// "__typename" is part of the graphql introspection spec and has to be ignored by jpa
88+
if(!"__typename".equals(selectedField.getName())) {
89+
90+
// Optional orderBy argument
91+
Optional<Argument> orderBy = selectedField.getArguments().stream()
92+
.filter(this::isOrderByArgument)
93+
.findFirst();
94+
95+
if(orderBy.isPresent()) {
96+
return true;
97+
}
98+
}
99+
}
100+
}
101+
102+
return false;
103+
104+
}
100105
@SuppressWarnings( { "rawtypes", "unchecked" } )
101106
@Override
102107
protected TypedQuery<?> getQuery(DataFetchingEnvironment environment, Field field, boolean isDistinct) {
@@ -144,7 +149,7 @@ protected TypedQuery<?> getQuery(DataFetchingEnvironment environment, Field fiel
144149
* @see http://stackoverflow.com/questions/7077464/how-to-get-singularattribute-mapped-value-of-a-persistent-object
145150
*/
146151
@SuppressWarnings("unchecked")
147-
public <EntityType,FieldType> FieldType getAttributeValue(EntityType entity, SingularAttribute<EntityType, FieldType> field) {
152+
public <EntityType, FieldType> FieldType getAttributeValue(EntityType entity, SingularAttribute<EntityType, FieldType> field) {
148153
try {
149154
Member member = field.getJavaMember();
150155
if (member instanceof Method) {
@@ -167,7 +172,7 @@ public <EntityType,FieldType> FieldType getAttributeValue(EntityType entity, Sin
167172
* @see http://stackoverflow.com/questions/7077464/how-to-get-singularattribute-mapped-value-of-a-persistent-object
168173
*/
169174
@SuppressWarnings("unchecked")
170-
public <EntityType,FieldType> FieldType getAttributeValue(EntityType entity, PluralAttribute<EntityType, ?, FieldType> field) {
175+
public <EntityType, FieldType> FieldType getAttributeValue(EntityType entity, PluralAttribute<EntityType, ?, FieldType> field) {
171176
try {
172177
Member member = field.getJavaMember();
173178
if (member instanceof Method) {

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryDataFetcher.java

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,52 +41,6 @@
4141
import graphql.schema.DataFetchingEnvironmentImpl;
4242
import graphql.schema.GraphQLObjectType;
4343

44-
/*
45-
query Q($ids: [Long]!, $status: String!) {
46-
ProcessInstancesQuery (
47-
page:{start:1, limit: 10}
48-
distinct: true
49-
where: {
50-
OR: {
51-
processInstanceId: {
52-
IN: $ids
53-
}
54-
status: {
55-
LIKE: $status
56-
}
57-
}
58-
lastModified:{
59-
OR: {
60-
AND: {
61-
GT: "9/01/17 0:00 AM"
62-
LT: "10/01/17 0:00 AM"
63-
}
64-
IS_NULL: true
65-
}
66-
}
67-
}
68-
) {
69-
total
70-
pages
71-
select {
72-
processInstanceId(orderBy:ASC)
73-
processDefinitionId
74-
status
75-
tasks {
76-
id
77-
assignee,
78-
name
79-
variables {
80-
id
81-
name
82-
type,
83-
value
84-
}
85-
}
86-
}
87-
}
88-
}
89-
*/
9044
/**
9145
* JPA Query DataFetcher implementation that fetches entities with page and where criteria expressions
9246
*

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import javax.persistence.criteria.Predicate;
4646
import javax.persistence.criteria.Root;
4747
import javax.persistence.metamodel.Attribute;
48-
import javax.persistence.metamodel.Bindable.BindableType;
4948
import javax.persistence.metamodel.EntityType;
5049
import javax.persistence.metamodel.PluralAttribute;
5150
import javax.persistence.metamodel.SingularAttribute;
@@ -150,7 +149,7 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q
150149
Optional<Argument> orderByArgument = selectedField.getArguments().stream()
151150
.filter(this::isOrderByArgument)
152151
.findFirst();
153-
152+
154153
if (orderByArgument.isPresent()) {
155154
if ("DESC".equals(((EnumValue) orderByArgument.get().getValue()).getName()))
156155
query.orderBy(cb.desc(fieldPath));
@@ -175,24 +174,7 @@ protected final List<Argument> getFieldArguments(Field field, CriteriaQuery<?> q
175174
}
176175
}
177176
} else {
178-
// // If this a collection attribute then we try eagerly fetch causing eager left join
179-
// // Workaround fieldPath.getModel() is always null for PluralAttribute Hibernate implementation
180-
// if(fieldPath instanceof PluralAttributePath) {
181-
// PluralAttribute<?,?,?> attribute = ((PluralAttributePath<?>) fieldPath).getAttribute();
182-
//
183-
// // Foreign side is many and there are no filter arguments
184-
// if((attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY
185-
// || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_MANY)
186-
// && selectedField.getArguments().isEmpty()
187-
// ) {
188-
// // Must use query.distinct(true) for fetch left join
189-
// query.distinct(true);
190-
// // Eagerly fetch all collection elements in parent query
191-
// from.fetch(attribute.getName(), JoinType.LEFT);
192-
// } else {
193-
// // Do nothing
194-
// }
195-
// }
177+
// Do nothing
196178
}
197179
}
198180
}
@@ -303,11 +285,6 @@ protected Predicate getPredicate(CriteriaBuilder cb, Root<?> from, From<?,?> pat
303285
}
304286
}
305287

306-
private boolean isOuterJoin(From<?,?> from, String name) {
307-
BindableType bindableType = from.get(name).getModel().getBindableType();
308-
return (bindableType == BindableType.PLURAL_ATTRIBUTE) ? true : false;
309-
}
310-
311288
@SuppressWarnings( "unchecked" )
312289
private <R extends Value> R getValue(Argument argument) {
313290
return (R) argument.getValue();
@@ -416,7 +393,8 @@ private Predicate getFieldPredicate(String fieldName, CriteriaBuilder cb, From<?
416393

417394
}
418395

419-
private PredicateFilter getPredicateFilter(ObjectField objectField, DataFetchingEnvironment environment, Argument argument) {
396+
@SuppressWarnings("serial")
397+
private PredicateFilter getPredicateFilter(ObjectField objectField, DataFetchingEnvironment environment, Argument argument) {
420398
EnumSet<PredicateFilter.Criteria> options =
421399
EnumSet.of(PredicateFilter.Criteria.valueOf(argument.getName()));
422400

graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,50 @@ public void queryOrderByFields() {
313313
assertThat(result.toString()).isEqualTo(expected);
314314
}
315315

316+
@Test
317+
public void queryOrderByFieldsNested() {
318+
//given:
319+
String query = "query { Humans(where: {id: {EQ: \"1000\"}}) { select {name(orderBy: DESC) homePlanet friends { name(orderBy:DESC) } } } }";
320+
321+
String expected = "{Humans={select=["
322+
+ "{name=Luke Skywalker, homePlanet=Tatooine, "
323+
+ "friends=["
324+
+ "{name=R2-D2}, "
325+
+ "{name=Leia Organa}, "
326+
+ "{name=Han Solo}, "
327+
+ "{name=C-3PO}"
328+
+ "]"
329+
+ "}"
330+
+ "]}}";
331+
332+
//when:
333+
Object result = executor.execute(query).getData();
334+
335+
//then:
336+
assertThat(result.toString()).isEqualTo(expected);
337+
}
338+
339+
@Test
340+
public void queryOrderByDefaultId() {
341+
//given:
342+
String query = "query { Humans { select { id } } }";
343+
344+
String expected = "{Humans={select=["
345+
+ "{id=1000}, "
346+
+ "{id=1001}, "
347+
+ "{id=1002}, "
348+
+ "{id=1003}, "
349+
+ "{id=1004}"
350+
+ "]}}";
351+
352+
//when:
353+
Object result = executor.execute(query).getData();
354+
355+
//then:
356+
assertThat(result.toString()).isEqualTo(expected);
357+
}
358+
359+
316360
@Test
317361
public void queryByCollectionOfEnumsAtRootLevel() {
318362
//given:

0 commit comments

Comments
 (0)