diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java index 61b8f921a..772ac3fb0 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java @@ -37,16 +37,12 @@ import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.introproventures.graphql.jpa.query.annotation.GraphQLDescription; import com.introproventures.graphql.jpa.query.annotation.GraphQLIgnore; import com.introproventures.graphql.jpa.query.schema.GraphQLSchemaBuilder; import com.introproventures.graphql.jpa.query.schema.JavaScalars; import com.introproventures.graphql.jpa.query.schema.NamingStrategy; import com.introproventures.graphql.jpa.query.schema.impl.PredicateFilter.Criteria; - import graphql.Assert; import graphql.Scalars; import graphql.schema.Coercing; @@ -64,6 +60,8 @@ import graphql.schema.GraphQLType; import graphql.schema.GraphQLTypeReference; import graphql.schema.PropertyDataFetcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * JPA specific schema builder implementation of {code #GraphQLSchemaBuilder} interface @@ -378,7 +376,19 @@ private GraphQLInputType getWhereAttributeType(Attribute attribute) { .description("Not In criteria") .type(new GraphQLList(getAttributeType(attribute))) .build() - ); + ) + .field(GraphQLInputObjectField.newInputObjectField() + .name(Criteria.BETWEEN.name()) + .description("Between criteria") + .type(new GraphQLList(getAttributeType(attribute))) + .build() + ) + .field(GraphQLInputObjectField.newInputObjectField() + .name(Criteria.NOT_BETWEEN.name()) + .description("Not Between criteria") + .type(new GraphQLList(getAttributeType(attribute))) + .build() + ); GraphQLInputType answer = builder.build(); 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 f2d594327..13cf27236 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 @@ -90,7 +90,7 @@ protected Predicate addOrNull(Path root, Predicate p) { protected Predicate getStringPredicate(Path root, PredicateFilter filter) { Expression fieldValue; - // list or arrays only for in and not in + // list or arrays only for in and not in, between and not between Predicate arrayValuePredicate = mayBeArrayValuePredicate(root, filter); if(arrayValuePredicate == null) { @@ -199,10 +199,22 @@ protected Predicate mayBeArrayValuePredicate(Path path, PredicateFilter filte } else if (filter.getCriterias().contains(PredicateFilter.Criteria.NE) || filter.getCriterias().contains(PredicateFilter.Criteria.NIN)) { return cb.not(path.in((Object[]) filter.getValue())); + } else if (!filter.getCriterias().contains(PredicateFilter.Criteria.NE) + && (filter.getCriterias().contains(Criteria.BETWEEN) || filter.getCriterias().contains(Criteria.NOT_BETWEEN))) { + + Object[] values = (Object[]) filter.getValue(); + if (values.length == 2) { + Expression name = path.get(filter.getField()); + Predicate between = cb.between(name, cb.literal((String) values[0]), cb.literal((String) values[1])); + if (filter.getCriterias().contains(Criteria.BETWEEN)) + return between; + return cb.not(between); + } } } else if ((filter.getValue() instanceof Collection)) { if (!filter.getCriterias().contains(PredicateFilter.Criteria.NE) - && !filter.getCriterias().contains(PredicateFilter.Criteria.NIN)) { + && !filter.getCriterias().contains(PredicateFilter.Criteria.NIN) && + !(filter.getCriterias().contains(Criteria.NOT_BETWEEN) || filter.getCriterias().contains(Criteria.BETWEEN))) { CriteriaBuilder.In in = cb.in(path); for(Object n : (Collection) filter.getValue()) { in.value(n); @@ -211,6 +223,21 @@ protected Predicate mayBeArrayValuePredicate(Path path, PredicateFilter filte } else if (filter.getCriterias().contains(PredicateFilter.Criteria.NE) || filter.getCriterias().contains(PredicateFilter.Criteria.NIN)) { return cb.not(path.in((Collection) filter.getValue())); + } else if (!filter.getCriterias().contains(PredicateFilter.Criteria.NE) + && (filter.getCriterias().contains(Criteria.NOT_BETWEEN) || filter.getCriterias().contains(Criteria.BETWEEN))) { + Expression name = (Expression) path; + Collection collection = (Collection) filter.getValue(); + if (collection.size() == 2) { + Object[] values = collection.toArray(); + Expression fromValue = cb.literal(values[0]); + Expression toValue = cb.literal(values[1]); + Predicate between = cb.between(name, fromValue, toValue); + if (filter.getCriterias().contains(Criteria.BETWEEN)) { + return between; + } + + return cb.not(between); + } } } @@ -261,6 +288,27 @@ protected Predicate getDatePredicate(Path root, PredicateFilter } // LE or default return cb.lessThanOrEqualTo(root, (Date) filter.getValue()); + } else if (filter.getValue().getClass().isArray() || filter.getValue() instanceof Collection) { + if (!filter.getCriterias().contains(PredicateFilter.Criteria.NE) + && (filter.getCriterias().contains(Criteria.BETWEEN) || filter.getCriterias().contains(Criteria.NOT_BETWEEN))) { + + Object[] values; + if (filter.getValue().getClass().isArray()) { + values = (Object[]) filter.getValue(); + } else { + values = ((Collection) filter.getValue()).toArray(); + } + + if (values.length == 2) { + Expression name = (Expression) root; + Expression fromDate = cb.literal((Date) values[0]); + Expression toDate = cb.literal((Date) values[1]); + Predicate between = cb.between(name, fromDate, toDate); + if (filter.getCriterias().contains(Criteria.BETWEEN)) + return between; + return cb.not(between); + } + } } return null; } diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java index c0d99cfe5..b50db8eb6 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/PredicateFilter.java @@ -95,6 +95,14 @@ public enum Criteria { * Not In condition */ NIN, + /** + * Between condition + */ + BETWEEN, + /** + * Not Between condition + */ + NOT_BETWEEN } private final String field; diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java index a8874558c..7732c8def 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/GraphQLExecutorTests.java @@ -22,6 +22,8 @@ import javax.persistence.EntityManager; +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; @@ -33,9 +35,6 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.Assert; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaExecutor; -import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; - @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment=WebEnvironment.NONE) @TestPropertySource({"classpath:hibernate.properties"}) @@ -389,7 +388,76 @@ public void queryForEntityWithEmeddableTypeAndWhere() { // then assertThat(result.toString()).isEqualTo(expected); } - + + @Test + public void queryWithNumericBetweenPredicate() { + //given: + String query = "query { Books ( where: { id: {BETWEEN: [2, 5]}}) { select { id title} } }"; + + String expected = "{Books={select=[" + + "{id=2, title=War and Peace}, " + + "{id=3, title=Anna Karenina}, " + + "{id=5, title=The Cherry Orchard}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryWithNumericNotBetweenPredicate() { + //given: + String query = "query { Books ( where: { id: {NOT_BETWEEN: [2, 5]}}) { select { id title} } }"; + + String expected = "{Books={select=[" + + "{id=6, title=The Seagull}, " + + "{id=7, title=Three Sisters}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryWithDateBetweenPredicate() { + //given: + String query = "query { Books ( where: { publicationDate: {BETWEEN: [\"1869-01-01\", \"1896-01-01\"]}}) { select { id title publicationDate} } }"; + + String expected = "{Books={select=[" + + "{id=2, title=War and Peace, publicationDate=1869-01-01 00:00:00.0}, " + + "{id=3, title=Anna Karenina, publicationDate=1877-04-01 00:00:00.0}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryWithDateNotBetweenPredicate() { + //given: + String query = "query { Books ( where: { publicationDate: {NOT_BETWEEN: [\"1869-01-01\", \"1896-01-01\"]}}) { select { id title publicationDate} } }"; + + String expected = "{Books={select=[" + + "{id=5, title=The Cherry Orchard, publicationDate=1904-01-17 00:00:00.0}, " + + "{id=6, title=The Seagull, publicationDate=1896-10-17 00:00:00.0}, " + + "{id=7, title=Three Sisters, publicationDate=1900-01-01 00:00:00.0}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } } \ No newline at end of file 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 e3a5c5756..e033dbc7c 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 @@ -555,5 +555,40 @@ public void queryWithTypenameSimple() { //then: assertThat(result.toString()).isEqualTo(expected); } + + @Test + public void queryWithStringBetweenPredicate() { + //given: + String query = "query { Humans ( where: { id: {BETWEEN: [\"1001\", \"1003\"]}}) { select { id } } }"; + + String expected = "{Humans={select=[" + + "{id=1001}, " + + "{id=1002}, " + + "{id=1003}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } + + @Test + public void queryWithStringNotBetweenPredicate() { + //given: + String query = "query { Humans ( where: { id: {NOT_BETWEEN: [\"1001\", \"1003\"]}}) { select { id } } }"; + + String expected = "{Humans={select=[" + + "{id=1000}, " + + "{id=1004}" + + "]}}"; + + //when: + Object result = executor.execute(query).getData(); + + //then: + assertThat(result.toString()).isEqualTo(expected); + } } diff --git a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/book/Book.java b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/book/Book.java index c255249f2..d5b85bdfb 100644 --- a/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/book/Book.java +++ b/graphql-jpa-query-schema/src/test/java/com/introproventures/graphql/jpa/query/schema/model/book/Book.java @@ -16,6 +16,8 @@ package com.introproventures.graphql.jpa.query.schema.model.book; +import java.util.Date; + import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -38,4 +40,6 @@ public class Book { @Enumerated(EnumType.STRING) Genre genre; + + Date publicationDate; } diff --git a/graphql-jpa-query-schema/src/test/resources/data.sql b/graphql-jpa-query-schema/src/test/resources/data.sql index f97b4ecc9..85c811ae3 100644 --- a/graphql-jpa-query-schema/src/test/resources/data.sql +++ b/graphql-jpa-query-schema/src/test/resources/data.sql @@ -108,12 +108,13 @@ insert into thing (id, type) values -- Books insert into author (id, name, genre) values (1, 'Leo Tolstoy', 'NOVEL'); -insert into book (id, title, author_id, genre) values (2, 'War and Peace', 1, 'NOVEL'); -insert into book (id, title, author_id, genre) values (3, 'Anna Karenina', 1, 'NOVEL'); +insert into book (id, title, author_id, genre, publication_date) values (2, 'War and Peace', 1, 'NOVEL', '1869-01-01'); +insert into book (id, title, author_id, genre, publication_date) values (3, 'Anna Karenina', 1, 'NOVEL', '1877-04-01'); insert into author (id, name, genre) values (4, 'Anton Chekhov', 'PLAY'); -insert into book (id, title, author_id, genre) values (5, 'The Cherry Orchard', 4, 'PLAY'); -insert into book (id, title, author_id, genre) values (6, 'The Seagull', 4, 'PLAY'); -insert into book (id, title, author_id, genre) values (7, 'Three Sisters', 4, 'PLAY'); +insert into book (id, title, author_id, genre, publication_date) values (5, 'The Cherry Orchard', 4, 'PLAY', '1904-01-17'); +insert into book (id, title, author_id, genre, publication_date) values (6, 'The Seagull', 4, 'PLAY', '1896-10-17'); +insert into book (id, title, author_id, genre, publication_date) values (7, 'Three Sisters', 4, 'PLAY', '1900-01-01'); + insert into author_phone_numbers(phone_number, author_id) values ('1-123-1234', 1), ('1-123-5678', 1),