diff --git a/graphql-jpa-query-boot-starter/.project b/graphql-jpa-query-boot-starter/.project index 58102b1fd..e092c11eb 100644 --- a/graphql-jpa-query-boot-starter/.project +++ b/graphql-jpa-query-boot-starter/.project @@ -16,17 +16,17 @@ - org.eclipse.m2e.core.maven2Builder + org.springframework.ide.eclipse.core.springbuilder - org.springframework.ide.eclipse.core.springbuilder + org.springframework.ide.eclipse.boot.validation.springbootbuilder - org.springframework.ide.eclipse.boot.validation.springbootbuilder + org.eclipse.m2e.core.maven2Builder diff --git a/graphql-jpa-query-example/.project b/graphql-jpa-query-example/.project index ece02844d..5ba341ede 100644 --- a/graphql-jpa-query-example/.project +++ b/graphql-jpa-query-example/.project @@ -16,17 +16,17 @@ - org.eclipse.m2e.core.maven2Builder + org.springframework.ide.eclipse.core.springbuilder - org.springframework.ide.eclipse.core.springbuilder + org.springframework.ide.eclipse.boot.validation.springbootbuilder - org.springframework.ide.eclipse.boot.validation.springbootbuilder + org.eclipse.m2e.core.maven2Builder diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java index 37d99e5af..693f1b611 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaOneToManyDataFetcher.java @@ -18,12 +18,10 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; @@ -37,7 +35,7 @@ import graphql.language.Argument; import graphql.language.Field; -import graphql.language.SelectionSet; +import graphql.language.Selection; import graphql.schema.DataFetchingEnvironment; /** @@ -63,40 +61,47 @@ public Object get(DataFetchingEnvironment environment) { Object source = environment.getSource(); Optional whereArg = extractArgument(environment, field, GraphQLJpaSchemaBuilder.QUERY_WHERE_PARAM_NAME); - // Resolve collection query if where argument is present - if(whereArg.isPresent()) { -// // Create entity graph from selection -// EntityGraph entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field)))); -// -// try { -// Object result = getQuery(environment, field, true) -// .setHint("javax.persistence.fetchgraph", entityGraph) -// .getSingleResult(); -// -// return getAttributeValue(result, attribute); -// } catch (NoResultException e) { -// } -// -// return Collections.emptyList(); + // Resolve collection query if where argument is present or any field in selection has orderBy argument + if(whereArg.isPresent() || hasSelectionAnyOrderBy(field)) { - EntityGraph entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field)))); + //EntityGraph entityGraph = buildEntityGraph(new Field("select", new SelectionSet(Arrays.asList(field)))); return getQuery(environment, field, true) + //.setHint("javax.persistence.fetchgraph", entityGraph) // TODO: fix runtime exception .getResultList(); - -// .stream() -// .map(it -> (Tuple) it) -// .map(tuple -> tuple.get(attribute.getName())) -// .collect(Collectors.toList()); } // Let hibernate resolve collection query return getAttributeValue(source, attribute); - - // Must do this to resolve where and orderBy on child fields -// return getQuery(environment, field, true).getResultList(); } + private boolean hasSelectionAnyOrderBy(Field field) { + + if(!hasSelectionSet(field)) return false; + + // Loop through all of the fields being requested + for(Selection selection : field.getSelectionSet().getSelections()) { + if (selection instanceof Field) { + Field selectedField = (Field) selection; + + // "__typename" is part of the graphql introspection spec and has to be ignored by jpa + if(!"__typename".equals(selectedField.getName())) { + + // Optional orderBy argument + Optional orderBy = selectedField.getArguments().stream() + .filter(this::isOrderByArgument) + .findFirst(); + + if(orderBy.isPresent()) { + return true; + } + } + } + } + + return false; + + } @SuppressWarnings( { "rawtypes", "unchecked" } ) @Override protected TypedQuery getQuery(DataFetchingEnvironment environment, Field field, boolean isDistinct) { @@ -144,7 +149,7 @@ protected TypedQuery getQuery(DataFetchingEnvironment environment, Field fiel * @see http://stackoverflow.com/questions/7077464/how-to-get-singularattribute-mapped-value-of-a-persistent-object */ @SuppressWarnings("unchecked") - public FieldType getAttributeValue(EntityType entity, SingularAttribute field) { + public FieldType getAttributeValue(EntityType entity, SingularAttribute field) { try { Member member = field.getJavaMember(); if (member instanceof Method) { @@ -167,7 +172,7 @@ public FieldType getAttributeValue(EntityType entity, Sin * @see http://stackoverflow.com/questions/7077464/how-to-get-singularattribute-mapped-value-of-a-persistent-object */ @SuppressWarnings("unchecked") - public FieldType getAttributeValue(EntityType entity, PluralAttribute field) { + public FieldType getAttributeValue(EntityType entity, PluralAttribute field) { try { Member member = field.getJavaMember(); if (member instanceof Method) { diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryDataFetcher.java index 5b94eb36a..bec37d72c 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryDataFetcher.java @@ -41,52 +41,6 @@ import graphql.schema.DataFetchingEnvironmentImpl; import graphql.schema.GraphQLObjectType; -/* -query Q($ids: [Long]!, $status: String!) { - ProcessInstancesQuery ( - page:{start:1, limit: 10} - distinct: true - where: { - OR: { - processInstanceId: { - IN: $ids - } - status: { - LIKE: $status - } - } - lastModified:{ - OR: { - AND: { - GT: "9/01/17 0:00 AM" - LT: "10/01/17 0:00 AM" - } - IS_NULL: true - } - } - } - ) { - total - pages - select { - processInstanceId(orderBy:ASC) - processDefinitionId - status - tasks { - id - assignee, - name - variables { - id - name - type, - value - } - } - } - } -} - */ /** * JPA Query DataFetcher implementation that fetches entities with page and where criteria expressions * diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java index aacfd43b1..623903402 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java @@ -45,7 +45,6 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.Bindable.BindableType; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.PluralAttribute; import javax.persistence.metamodel.SingularAttribute; @@ -150,7 +149,7 @@ protected final List getFieldArguments(Field field, CriteriaQuery q Optional orderByArgument = selectedField.getArguments().stream() .filter(this::isOrderByArgument) .findFirst(); - + if (orderByArgument.isPresent()) { if ("DESC".equals(((EnumValue) orderByArgument.get().getValue()).getName())) query.orderBy(cb.desc(fieldPath)); @@ -175,24 +174,7 @@ protected final List getFieldArguments(Field field, CriteriaQuery q } } } else { -// // If this a collection attribute then we try eagerly fetch causing eager left join -// // Workaround fieldPath.getModel() is always null for PluralAttribute Hibernate implementation -// if(fieldPath instanceof PluralAttributePath) { -// PluralAttribute attribute = ((PluralAttributePath) fieldPath).getAttribute(); -// -// // Foreign side is many and there are no filter arguments -// if((attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ONE_TO_MANY -// || attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.MANY_TO_MANY) -// && selectedField.getArguments().isEmpty() -// ) { -// // Must use query.distinct(true) for fetch left join -// query.distinct(true); -// // Eagerly fetch all collection elements in parent query -// from.fetch(attribute.getName(), JoinType.LEFT); -// } else { -// // Do nothing -// } -// } + // Do nothing } } } @@ -303,11 +285,6 @@ protected Predicate getPredicate(CriteriaBuilder cb, Root from, From pat } } - private boolean isOuterJoin(From from, String name) { - BindableType bindableType = from.get(name).getModel().getBindableType(); - return (bindableType == BindableType.PLURAL_ATTRIBUTE) ? true : false; - } - @SuppressWarnings( "unchecked" ) private R getValue(Argument argument) { return (R) argument.getValue(); @@ -416,7 +393,8 @@ private Predicate getFieldPredicate(String fieldName, CriteriaBuilder cb, From options = EnumSet.of(PredicateFilter.Criteria.valueOf(argument.getName())); diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java index 50e992b71..88de67a79 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/StarwarsQueryExecutorTests.java @@ -313,6 +313,50 @@ public void queryOrderByFields() { assertThat(result.toString()).isEqualTo(expected); } + @Test + public void queryOrderByFieldsNested() { + //given: + String query = "query { Humans(where: {id: {EQ: \"1000\"}}) { select {name(orderBy: DESC) homePlanet friends { name(orderBy:DESC) } } } }"; + + String expected = "{Humans={select=[" + + "{name=Luke Skywalker, homePlanet=Tatooine, " + + "friends=[" + + "{name=R2-D2}, " + + "{name=Leia Organa}, " + + "{name=Han Solo}, " + + "{name=C-3PO}" + + "]" + + "}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryOrderByDefaultId() { + //given: + String query = "query { Humans { select { id } } }"; + + String expected = "{Humans={select=[" + + "{id=1000}, " + + "{id=1001}, " + + "{id=1002}, " + + "{id=1003}, " + + "{id=1004}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test public void queryByCollectionOfEnumsAtRootLevel() { //given: