From 571a84e42aa3e528564829fdee6a590372190642 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Sat, 13 Jan 2018 14:22:20 -0800 Subject: [PATCH] Fix: 'NE' is generating 'LIKE' statement instead of notEqual for Strings --- .../schema/impl/JpaPredicateBuilder.java | 71 +++++---- .../schema/StarwarsQueryExecutorTests.java | 136 ++++++++++++------ 2 files changed, 124 insertions(+), 83 deletions(-) diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java index 41c1fa7d0..fa7180f02 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java @@ -86,15 +86,15 @@ protected Predicate addOrNull(Path root, Predicate p) { * @param filter * @return */ - protected Predicate getStringPredicate(Path root, PredicateFilter filter) { + protected Predicate getStringPredicate(Path root, PredicateFilter filter) { Expression fieldValue; // list or arrays only for in and not in Predicate arrayValuePredicate = mayBeArrayValuePredicate(root, filter); - + if(arrayValuePredicate == null) { String compareValue = filter.getValue().toString(); - + if (filter.getCriterias().contains(PredicateFilter.Criteria.IN)) { CriteriaBuilder.In in = cb.in(root); return in.value(compareValue); @@ -102,14 +102,19 @@ protected Predicate getStringPredicate(Path root, PredicateFilter filter) { if (filter.getCriterias().contains(PredicateFilter.Criteria.NIN)) { return cb.not(root.in(compareValue)); } - + if (filter.getCriterias().contains(PredicateFilter.Criteria.CASE)) { - fieldValue = (Path) root; + fieldValue = root; } else { - fieldValue = cb.lower((Path) root); + fieldValue = cb.lower(root); compareValue = compareValue.toLowerCase(); } + + if (filter.getCriterias().contains(PredicateFilter.Criteria.NE)) { + return cb.notEqual(fieldValue, compareValue); + } + if (filter.getCriterias().contains(PredicateFilter.Criteria.LIKE)) { compareValue = "%" + compareValue + "%"; } @@ -125,7 +130,7 @@ else if (filter.getCriterias().contains(PredicateFilter.Criteria.EXACT)) { } return cb.like(fieldValue, compareValue); } - + return arrayValuePredicate; } @@ -148,8 +153,8 @@ protected Predicate getBooleanPredicate(Path root, PredicateFilter filter) { protected Predicate getIntegerPredicate(Path root, PredicateFilter filter) { // list or arrays only for in and not in - Predicate arrayValuePredicate = mayBeArrayValuePredicate(root, filter); - + Predicate arrayValuePredicate = mayBeArrayValuePredicate(root, filter); + if (arrayValuePredicate == null && filter.getValue() != null && filter.getValue() instanceof Number) { if (filter.getCriterias().contains(PredicateFilter.Criteria.IN)) { CriteriaBuilder.In in = cb.in(root); @@ -171,9 +176,9 @@ protected Predicate getIntegerPredicate(Path root, PredicateFi return cb.ge(root, (Number) filter.getValue()); } if (filter.getCriterias().contains(PredicateFilter.Criteria.NE)) { - return cb.notEqual(root, (Number) filter.getValue()); + return cb.notEqual(root, filter.getValue()); } - return cb.equal(root, (Number) filter.getValue()); + return cb.equal(root, filter.getValue()); } return arrayValuePredicate; } @@ -193,7 +198,7 @@ protected Predicate mayBeArrayValuePredicate(Path path, PredicateFilter filte return cb.not(path.in((Object[]) filter.getValue())); } } else if ((filter.getValue() instanceof Collection)) { - if (!filter.getCriterias().contains(PredicateFilter.Criteria.NE) + if (!filter.getCriterias().contains(PredicateFilter.Criteria.NE) && !filter.getCriterias().contains(PredicateFilter.Criteria.NIN)) { CriteriaBuilder.In in = cb.in(path); for(Object n : (Collection) filter.getValue()) { @@ -205,11 +210,11 @@ protected Predicate mayBeArrayValuePredicate(Path path, PredicateFilter filte return cb.not(path.in((Collection) filter.getValue())); } } - + return null; - + } - + protected Predicate getFloatingPointPredicate(Path root, PredicateFilter filter) { if (filter.getValue() != null && filter.getValue() instanceof Number) { if (filter.getCriterias().contains(PredicateFilter.Criteria.LT)) { @@ -222,10 +227,10 @@ protected Predicate getFloatingPointPredicate(Path root, Predi return cb.ge(root, (Number) filter.getValue()); } if (filter.getCriterias().contains(PredicateFilter.Criteria.EQ)) { - return cb.equal(root, (Number) filter.getValue()); + return cb.equal(root, filter.getValue()); } if (filter.getCriterias().contains(PredicateFilter.Criteria.NE)) { - return cb.notEqual(root, (Number) filter.getValue()); + return cb.notEqual(root, filter.getValue()); } // LE or default return cb.le(root, (Number) filter.getValue()); @@ -246,10 +251,10 @@ protected Predicate getDatePredicate(Path root, PredicateFilter return cb.greaterThanOrEqualTo(root, (Date) filter.getValue()); } if (filter.getCriterias().contains(PredicateFilter.Criteria.EQ)) { - return cb.equal(root, (Date) filter.getValue()); + return cb.equal(root, filter.getValue()); } if (filter.getCriterias().contains(PredicateFilter.Criteria.NE)) { - return cb.notEqual(root, (Date) filter.getValue()); + return cb.notEqual(root, filter.getValue()); } // LE or default return cb.lessThanOrEqualTo(root, (Date) filter.getValue()); @@ -257,11 +262,12 @@ protected Predicate getDatePredicate(Path root, PredicateFilter return null; } + @SuppressWarnings("unchecked") private Predicate getTypedPredicate(From from, Path field, PredicateFilter filter) { Class type = field.getJavaType(); Object value = filter.getValue(); Set criterias = filter.getCriterias(); - + if(value == null) { return cb.disjunction(); } @@ -270,25 +276,14 @@ private Predicate getTypedPredicate(From from, Path field, PredicateFilt return (boolean) value ? cb.isNull(field) : cb.isNotNull(field); } else if (criterias.contains(Criteria.NOT_NULL)) { return (boolean) value ? cb.isNotNull(field) : cb.isNull(field) ; - } - -// // Unwrap arrays and lists of 1 into single value -// if(value.getClass().isArray() && ((Object [])value).length == 1) { -// value = ((Object [])value)[0]; -// type = value.getClass(); -// } -// -// if(value instanceof List && ((List)value).size() == 1) { -// value = ((List)value).get(0); -// type = value.getClass(); -// } - + } + PredicateFilter predicateFilter = new PredicateFilter(filter.getField(), value, criterias); - + if (type.isPrimitive()) type = JpaQueryBuilder.PRIMITIVES_TO_WRAPPERS.get(type); if (type.equals(String.class)) { - return getStringPredicate(field, filter); + return getStringPredicate((Path)field, filter); } else if (type.equals(Long.class) || type.equals(BigInteger.class) @@ -307,12 +302,12 @@ else if (type.equals(java.util.Date.class)) { } else if (type.equals(Boolean.class)) { return getBooleanPredicate(field, predicateFilter); - } + } else if(Collection.class.isAssignableFrom(type)) { if(field.getModel() == null) - return from.join(filter.getField()).in(value); + return from.join(filter.getField()).in(value); } - + throw new IllegalArgumentException("Unsupported field type " + type + " for field " + predicateFilter.getField()); } 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 88de67a79..cdadbad90 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 @@ -26,6 +26,8 @@ import javax.persistence.Query; import javax.transaction.Transactional; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; +import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -34,9 +36,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.test.context.junit4.SpringRunner; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; - @RunWith(SpringRunner.class) @SpringBootTest public class StarwarsQueryExecutorTests { @@ -50,15 +49,15 @@ public GraphQLExecutor graphQLExecutor(final GraphQLSchemaBuilder graphQLSchemaB @Bean public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManager entityManager) { - + return new GraphQLJpaSchemaBuilder(entityManager) .name("Starwars") .description("Starwars JPA test schema"); } - + } - - + + @Autowired private GraphQLJpaExecutor executor; @@ -68,21 +67,21 @@ public GraphQLSchemaBuilder graphQLSchemaBuilder(final EntityManager entityManag @Test public void contextLoads() { } - + @Test @Transactional public void JPASampleTester() { // given: Query query = em.createQuery("select h, h.friends from Human h"); - + // when: List result = query.getResultList(); - + // then: assertThat(result).isNotEmpty(); assertThat(result).hasSize(13); } - + @Test public void getsNamesOfAllDroids() { //given: @@ -116,7 +115,7 @@ public void queryManyToOneJoinById() { //given: String query = "query { Humans(where: {id: {EQ: \"1000\"}}) { select { name, homePlanet, favoriteDroid { name } } } }"; - + String expected = "{Humans={select=[{name=Luke Skywalker, homePlanet=Tatooine, favoriteDroid={name=C-3PO}}]}}"; //when: @@ -134,7 +133,7 @@ public void queryManyToOneJoinByIdWithVariables() { Map variables = new HashMap() {{ put("id", "2001"); }}; - + String expected = "{Humans={select=[{name=Darth Vader, homePlanet=Tatooine, favoriteDroid={name=R2-D2}}]}}"; //when: @@ -143,18 +142,18 @@ public void queryManyToOneJoinByIdWithVariables() { //then: assertThat(result.toString()).isEqualTo(expected); } - + @Test public void queryOneToManyJoinByID() { //given: String query = "query { Humans(where:{id:{EQ: \"1000\"}}) { select {name, homePlanet, friends { name } } }}"; - + String expected = "{Humans={select=[" + "{name=Luke Skywalker, homePlanet=Tatooine, friends=[{name=C-3PO}, {name=Leia Organa}, {name=R2-D2}, {name=Han Solo}]}" + "]}}"; - - + + //when: Object result = executor.execute(query).getData(); @@ -170,7 +169,7 @@ public void queryWithParameter() { Map variables = new HashMap() {{ put("id", "1001"); }}; - + String expected = "{Humans={select=[{name=Darth Vader, homePlanet=Tatooine}]}}"; //when: @@ -187,7 +186,7 @@ public void queryWithIdArgument() { Map variables = new HashMap() {{ put("id", "1001"); }}; - + String expected = "{Human={name=Darth Vader, homePlanet=Tatooine}}"; //when: @@ -196,12 +195,12 @@ public void queryWithIdArgument() { //then: assertThat(result.toString()).isEqualTo(expected); } - + @Test public void queryWithAlias() { //given: String query = "query { luke: Human(id: \"1000\") { name, homePlanet } leia: Human(id: \"1003\") { name } }"; - + String expected = "{luke={name=Luke Skywalker, homePlanet=Tatooine}, leia={name=Leia Organa}}"; //when: @@ -210,13 +209,13 @@ public void queryWithAlias() { //then: assertThat(result.toString()).isEqualTo(expected); } - + @Test public void queryAllowsUseFragmentToAvoidDuplicatingContent() { //given: String query = "query UseFragment { luke: Human(id: \"1000\") { ...HumanFragment } leia: Human(id: \"1003\") { ...HumanFragment } }" +"fragment HumanFragment on Human { name, homePlanet }"; - + String expected = "{luke={name=Luke Skywalker, homePlanet=Tatooine}, leia={name=Leia Organa, homePlanet=Alderaan}}"; //when: @@ -231,20 +230,20 @@ public void queryDeepNesting() { //given: String query = "query { Droid(id: \"2001\") { name, friends { name, appearsIn, friends { name } } } }"; - + String expected = "{Droid={name=R2-D2, friends=[" + "{name=Leia Organa, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO}, {name=R2-D2}, {name=Han Solo}, {name=Luke Skywalker}]}, " + "{name=Han Solo, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=R2-D2}, {name=Leia Organa}, {name=Luke Skywalker}]}, " + "{name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO}, {name=R2-D2}, {name=Leia Organa}, {name=Han Solo}]}" - + "]}}"; - + + "]}}"; + //when: Object result = executor.execute(query).getData(); //then: assertThat(result.toString()).isEqualTo(expected); } - + // Cannot simultaneously fetch multiple bags #2 // @Test public void queryDeepNestingPlural() { @@ -256,20 +255,20 @@ public void queryDeepNestingPlural() { + "{name=Han Solo, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=R2-D2}, {name=Leia Organa}, {name=Luke Skywalker}]}, " + "{name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS], friends=[{name=C-3PO}, {name=R2-D2}, {name=Leia Organa}, {name=Han Solo}]}]}" + "]}}"; - + //when: Object result = executor.execute(query).getData(); //then: assertThat(result.toString()).isEqualTo(expected); } - - + + @Test public void queryWhereRoot() { //given: String query = "query { Humans( page: { start: 1, limit: 2 }) { pages, total, select { name } } }"; - + String expected = "{Humans={pages=3, total=5, select=[{name=Luke Skywalker}, {name=Darth Vader}]}}"; //when: @@ -283,7 +282,7 @@ public void queryWhereRoot() { public void queryPaginationWithoutRecords() { //given: String query = "query { Humans ( page: { start: 1, limit: 2 }) { pages, total } }"; - + String expected = "{Humans={pages=3, total=5}}"; //when: @@ -297,7 +296,7 @@ public void queryPaginationWithoutRecords() { public void queryOrderByFields() { //given: String query = "query { Humans { select {name(orderBy: DESC) homePlanet } } }"; - + String expected = "{Humans={select=[" + "{name=Wilhuff Tarkin, homePlanet=null}, " + "{name=Luke Skywalker, homePlanet=Tatooine}, " @@ -317,7 +316,7 @@ public void queryOrderByFields() { 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=[" @@ -340,7 +339,7 @@ public void queryOrderByFieldsNested() { public void queryOrderByDefaultId() { //given: String query = "query { Humans { select { id } } }"; - + String expected = "{Humans={select=[" + "{id=1000}, " + "{id=1001}, " @@ -355,19 +354,19 @@ public void queryOrderByDefaultId() { //then: assertThat(result.toString()).isEqualTo(expected); } - - + + @Test public void queryByCollectionOfEnumsAtRootLevel() { //given: String query = "query { Humans ( where: { appearsIn: {IN: [THE_FORCE_AWAKENS]}}) { select { name appearsIn } } }"; - - + + String expected = "{Humans={select=[" + "{name=Luke Skywalker, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS]}, " + "{name=Han Solo, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS]}, " + "{name=Leia Organa, appearsIn=[A_NEW_HOPE, EMPIRE_STRIKES_BACK, RETURN_OF_THE_JEDI, THE_FORCE_AWAKENS]}" - + "]}}"; + + "]}}"; //when: Object result = executor.execute(query).getData(); @@ -380,7 +379,7 @@ public void queryByCollectionOfEnumsAtRootLevel() { public void queryByRestrictingSubObject() { //given: String query = "query { Humans { select { name gender(where:{ code: {EQ: \"Male\"}}) { description } } } }"; - + String expected = "{Humans={select=[" + "{name=Luke Skywalker, gender={description=Male}}, " + "{name=Darth Vader, gender={description=Male}}, " @@ -399,7 +398,7 @@ public void queryByRestrictingSubObject() { public void queryForSearchingByIntTypeSequenceField() { //given: String query = "query { CodeLists(where:{sequence:{EQ: 2}}) { select {id description active type sequence } } }"; - + String expected = "{CodeLists={select=[{id=1, description=Female, active=true, type=org.crygier.graphql.model.starwars.Gender, sequence=2}]}}"; //when: @@ -413,7 +412,7 @@ public void queryForSearchingByIntTypeSequenceField() { public void queryForSearchingByIntTypeSequenceInWhereField() { //given: String query = "query { CodeLists(where: {sequence: {EQ: 2}}) { select { id description active type sequence } } }"; - + String expected = "{CodeLists={select=[{id=1, description=Female, active=true, type=org.crygier.graphql.model.starwars.Gender, sequence=2}]}}"; //when: @@ -422,12 +421,12 @@ public void queryForSearchingByIntTypeSequenceInWhereField() { //then: assertThat(result.toString()).isEqualTo(expected); } - + @Test public void queryForSearchingByBooleanTypeActiveField() { //given: String query = "query { CodeLists(where: { active: {EQ:true}}) { select {id description active type sequence } } }"; - + String expected = "{CodeLists={select=[" + "{id=0, description=Male, active=true, type=org.crygier.graphql.model.starwars.Gender, sequence=1}, " + "{id=1, description=Female, active=true, type=org.crygier.graphql.model.starwars.Gender, sequence=2}" @@ -440,4 +439,51 @@ public void queryForSearchingByBooleanTypeActiveField() { assertThat(result.toString()).isEqualTo(expected); } + @Test + public void queryForSearchingByBooleanTypeActiveFieldNotEqual() { + //given: + String query = "query { CodeLists(where: { active: {NE:true}}) { select {id description active type sequence } } }"; + + String expected = "{CodeLists={select=[]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryForSearchingByStringTypeDescriptionFieldNotEqual() { + //given: + String query = "query { CodeLists(where: { description: {NE:\"Male\"}}) { select {id description active type sequence } } }"; + + String expected = "{CodeLists={select=[" + + "{id=1, description=Female, active=true, type=org.crygier.graphql.model.starwars.Gender, sequence=2}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + + @Test + public void queryForSearchingByIntTypeSequenceInWhereFieldNotEqual() { + //given: + String query = "query { CodeLists(where: {sequence: {NE: 2}}) { select { id description active type sequence } } }"; + + String expected = "{CodeLists={select=[" + + "{id=0, description=Male, active=true, type=org.crygier.graphql.model.starwars.Gender, sequence=1}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + }