This library helps GraphQL developers build awesome APIs with fine grain filtering support.
- grahql-java (v13.x)
- Java 8.x & Above
This library will help create filter conditions which are dynamically created by combining any supported filter criteria field along with any of the supported logical operations including AND, OR and NOT. Consider the examples below.
{
searchEmployees (filter : {
or : [{ firstName : {contains : "Saurabh"}},{ lastName : {equals : "Jaiswal"}}]
}) {
firstName
lastName
age
}
}
{
searchEmployees (filter : {
and : [{ firtName : {contains : "Saurabh"}},{ age : {gte : 25}}]
}) {
firstName
lastName
age
}
}
{
searchEmployees (filter : {
and : [
{ firstName : {contains : "Saurabh"}},
{ or : [{ lastName: {equals : "Jaiswal"}},{ age: {gte: 25}}
]}]
}) {
firstName
lastName
age
}
}
<dependency>
<groupId>com.intuit.graphql</groupId>
<artifactId>graphql-filter-java</artifactId>
<version>1.0.0</version>
</dependency># Define the query type
type Query {
searchEmployees(filter: Filter): [Employee!]
}
# Define the types
type Employee {
firstName: String!
lastName: String!
age: Int!
birthDate: DateTime!
}
# Define filter input
input Filter {
firstName: StringExpression
lastName: StringExpression
age: IntExpression
birthDate: DateExpression
and: [Filter!]
or: [Filter!]
not: Filter
}
# Define String expression
input StringExpression {
equals: String
contains: String
starts: String
ends: String
in: [String!]
}
# Define Int Expression
input IntExpression {
eq: Int
gt: Int
gte: Int
lt: Int
lte: Int
in: [Int!]
}
# Define Date Expression
input DateExpression {
eq: DateTime
gt: DateTime
gte: DateTime
lt: DateTime
lte: DateTime
}
Generates filter expression using JPA Specification for any SQL database.
@Repository
public interface EmployeeRepository extends JpaRepository<EmployeeEntity, String>, JpaSpecificationExecutor<EmployeeEntity> {
}@Getter
@Setter
@Entity
@Table(name = "employee")
public class EmployeeEntity {
private String firstName;
private String lastName;
private Integer age;
}@Component
@Transactional
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
/**
* Searched employees based on filter criteria.
*/
public List<EmployeeEntity> searchEmployees(DataFetchingEnvironment env) {
List<EmployeeEntity> employees = null;
Specification<EmployeeEntity> specification = getSpecification(env);
if (specification != null) {
employees = employeeRepository.findAll(specification);
} else {
employees = employeeRepository.findAll();
}
return employees;
}
/**
* Generates the filter JPA specification
*/
private Specification<EmployeeEntity> getSpecification(DataFetchingEnvironment env) {
FilterExpression.FilterExpressionBuilder builder = FilterExpression.newFilterExpressionBuilder();
FilterExpression filterExpression = builder.field(env.getField())
.args(env.getArguments())
.build();
Specification<EmployeeEntity> specification = filterExpression.getExpression(ExpressionFormat.JPA);
return specification;
}
}Generates SQL WHERE clause which can then be directly applied to any SQL database.
private String getExpression(DataFetchingEnvironment env) {
FilterExpression.FilterExpressionBuilder builder = FilterExpression.newFilterExpressionBuilder();
FilterExpression filterExpression = builder.field(env.getField())
.args(env.getArguments())
.build();
String expression = filterExpression.getExpression(ExpressionFormat.SQL);
return expression;
}WHERE ((lastName = 'Jaiswal') OR (firstName LIKE '%Saurabh%'))
Generates a DynamoDB FilterExpression string along with an ExpressionAttributeValues map
that can be passed directly to a DynamoDB ScanRequest or QueryRequest.
private void queryDynamoDB(DataFetchingEnvironment env, DynamoDBMapper mapper) {
FilterExpression.FilterExpressionBuilder builder = FilterExpression.newFilterExpressionBuilder();
FilterExpression filterExpression = builder.field(env.getField())
.args(env.getArguments())
.build();
DynamoDBExpressionVisitor visitor = new DynamoDBExpressionVisitor(null, null);
String filterExpressionStr = filterExpression.getExpression(ExpressionFormat.DYNAMODB);
Map<String, Object> expressionValues = visitor.getExpressionValues();
// Convert expressionValues to Map<String, AttributeValue> and use with DynamoDB SDK
}(contains(firstName, :firstName))
{ ":firstName": "Saurabh" }
| GraphQL operator | DynamoDB expression | Parameter name |
|---|---|---|
contains |
contains(field, :field) |
:field |
starts |
begins_with(field, :field) |
:field |
equals / eq |
field = :field |
:field |
gt |
field > :min_field |
:min_field |
gte |
field >= :min_field |
:min_field |
lt |
field < :max_field |
:max_field |
lte |
field <= :max_field |
:max_field |
in |
field IN (:field_0, :field_1, ...) |
:field_N |
between |
field BETWEEN :min_field AND :max_field |
:min_field, :max_field |
When graphql-java receives and parses the source filter expression, it creates an AST in memory which contains all the fields, operators and values supplied in the source filter. The problem is the generated AST does not know about the valid rules of a correct logical expression with multiple filter criteria. In order to get a meaningful expression out of the source filter input, filter library parses the GraphQL generated AST and generates a new expression AST with correct syntax and semantics. After this step, the generated AST looks as shown below in memory.
(firstName contains Saurabh) and ((lastName equals Jaiswal) or (age gte 25))
- Infix String
- SQL WHERE clause
- JPA Specification
- MongoDB Criteria
- Elasticsearch Criteria
- DynamoDB FilterExpression
- String (EQUALS, CONTAINS, STARTS, ENDS)
- Numeric (EQ, LT, GT, LTE, GTE)
- Range (IN, BETWEEN)
- AND
- OR
- NOT
- MySQL (via JPA Specification or SQL WHERE clause)
- MongoDB (via Criteria)
- Elasticsearch (via Criteria)
- DynamoDB (via FilterExpression)

