refcodes-criteria: Criteria construction for selecting, filtering or identifying

README

The REFCODES.ORG codes represent a group of artifacts consolidating parts of my work in the past years. Several topics are covered which I consider useful for you, programmers, developers and software engineers.

What is this repository for?

This artifact allows the construction of Criteria instances for selecting, filtering or identifying (such as the selection of entries from a database). This implies the creation of query statements from Criteria instances which can be applied to data sinks; such as databases (SQL like statements). In turn, this also implies parsing (construction) of criteria trees from a query statement.

How do I get set up?

To get up and running, include the following dependency (without the three dots “…”) in your pom.xml:

1
2
3
4
5
6
7
8
9
<dependencies>
	...
	<dependency>
		<artifactId>refcodes-criteria</artifactId>
		<groupId>org.refcodes</groupId>
		<version>3.3.5</version>
	</dependency>
	...
</dependencies>

The artifact is hosted directly at Maven Central. Jump straight to the source codes at Bitbucket. Read the artifact’s javadoc at javadoc.io.

Overview

This toolkit allows the creation of query statements from Criteria instances as well as parsing (construction) of criteria trees from query statements. Such a parsable query statement might look as follows:

( ( ( City = 'Berlin' ) OR ( City = 'Munich' ) ) AND ( Surname = 'Miller' ) )

A dedicated QueryFactory implementation is provided for parsing the above query statement into a Criteria hierarchy (ExpressionQueryFactory) and a dedicated CriteriaFactory implementation is provided for producing a query expression (as above) from a Criteria hierarchy (ExpressionCriteriaFactory).

A Criteria represents an atomic query or atomic logical operator with which complex queries can be constructed in an object oriented manner by combining the Criteria instances in a hierarchical tree structure. From this tree structure, query statements can be generated. In turn, query statements provided as logical operators can be parsed for the construction of Criteria trees. This means that a query is constructed from a Criteria tree with CriteriaNode node instances and CriteriaLeaf leaf instances. A node may contain other node instances and/or other leaf instances, all of which being the node’s children.

CriteriaNode

A CriteriaNode tree node may represent a logical AND, a logical OR or a logical NOT applied on the node’s children Criteria (CriteriaNode instances and CriteriaLeaf instances).

CriteriaLeaf

A CriteriaLeaf tree leaf is an expression usually relating to a key (for example identifying a table’s column in a database) and a value, both of which consolidating an expression (for example “City = 'Munich'”).

Criteria

The Criteria itself is the base definition of functionality which the CriteriaNode and CriteriaLeaf implementations are to support. Mainly, a Criteria is to have an alias (for example “AND”, “OR”, “LESS_THAN” and so on).

CriteriaSugar

The CriteriaSugar syntactic sugar is a handy class which may be statically imported in order to allow declarative definitions of Criteria trees. In the Java code this may look as follows (simplified):

1
2
3
4
import static org.refcodes.criteria.CriteriaSugar.*;
...
Criteria theCriteria = and( or( equalWith( "City", "Berlin" ), equalWith( "City", "Munich" ) ), equalWith( "Surname", "Miller" ) );
...

CriteriaFactory

The CriteriaFactory constructs a Criteria tree from a provided query. The syntax of the query is implementation specific and may look as follows:

( ( ( City = 'Berlin' ) OR ( City = 'Munich' ) ) AND ( Surname = 'Miller' ) )

CAUTION: The syntax supported for the query statement is implementation depended!

The ExpressionCriteriaFactory implements a CriteriaFactory being capable of parsing the above query.

QueryFactory

The QueryFactory generates a query from the provided Criteria tree. The resulting query may be targeted at a database and therefore be SQL like.

CAUTION: The syntax supported for the query statement is implementation depended!

This enables the creation of query statements from the a Criteria tree which can be applied to data sinks; such as databases (SQL like statements), e.g:

( ( ( City = 'Berlin' ) OR ( City = 'Munich' ) ) AND ( Surname = 'Miller' ) )

The ExpressionQueryFactory implements a QueryFactory being capable of producing the above query.

Snippets of interest

Below find some code snippets which demonstrate the various aspects of using the refcodes-criteria artifact (and , if applicable, its offsprings). See also the example source codes of this artifact for further information on the usage of this artifact.

Creating queries from criteria

The code snippet below uses an ExpressionQueryFactory and constructs a query equivalent to NOT ( NOT ( ( City != 'Berlin' ) AND ( ZIP < 30000 ) ) NOT ( AGE > 65 ) ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import static org.refcodes.criteria.CriteriaSugar.*;
import org.refcodes.criteria.*;
...
		Criteria theCriteria = not(
			and(
				not(
					and(
						notEqualWith( "City", "Berlin" ), lessThan( "ZIP", 30000 )
					)
				),
				greaterThan( "AGE", 65 )
			)
		);
		ExpressionQueryFactory theQueryFactory = new ExpressionQueryFactory();
		String theQuery = theQueryFactory.fromCriteria( theCriteria );
		System.out.println( "Query = " + theQuery );
...

Note that we combine NotCriteria and AndCriteria instances with NotEqualsWithCriteria, LessThanCriteria and GreaterThanCriteria instances (further nesting is possible, only limited by your ideas).

Query = NOT ( NOT ( ( City != 'Berlin' ) NOT ( ZIP < 30000 ) ) NOT ( AGE > 65 ) )

The code snippet below uses an ExpressionQueryFactory and constructs a query equivalent to NOT ( ( City = 'Berlin' ) NOT ( ZIP = 10337 ) ) OR ( Age >= 18 ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import static org.refcodes.criteria.CriteriaSugar.*;
import org.refcodes.criteria.*;
...
		Criteria theCriteria = not(
			and(
				not(
					and(
						notEqualWith( "City", "Berlin" ), lessThan( "ZIP", 30000 )
					)
				),
				greaterThan( "AGE", 65 )
			)
		);
		ExpressionQueryFactory theQueryFactory = new ExpressionQueryFactory();
		String theQuery = theQueryFactory.fromCriteria( theCriteria );
		System.out.println( "Query = " + theQuery );
...

Note that we combine OrCriteria and AndCriteria instances with EqualsWithCriteria and GreaterOrEqualThanCriteria instances (further nesting is possible, only limited by your ideas). Finally the ExpressionQueryFactory instance (line 15) produces the according expression:

Query = NOT ( ( City = 'Berlin' ) NOT ( ZIP = 10337 ) ) OR ( Age >= 18 )

Creating criteria from queries

The code snippet below uses an ExpressionCriteriaFactory and parses the query NOT ( NOT ( ( City != 'Berlin' ) NOT ( ZIP < 30000 ) ) NOT ( AGE > 65 ) ):

1
2
3
4
5
6
7
8
import static org.refcodes.criteria.CriteriaSugar.*;
import org.refcodes.criteria.*;
...
		ExpressionCriteriaFactory theExpressionFactory = new ExpressionCriteriaFactory();
		String theQuery = "NOT ( NOT ( ( City != 'Berlin' ) NOT ( ZIP < 30000 ) ) NOT ( AGE > 65 ) )";
		Criteria theCriteria = theExpressionFactory.fromQuery( theQuery );
		System.out.println( theCriteria.toSchema() );
...

Feeding the expression NOT ( NOT ( ( City != 'Berlin' ) NOT ( ZIP < 30000 ) ) NOT ( AGE > 65 ) ) into the ExpressionCriteriaFactory instance (line 6) produces the according Criteria and finally the CriteriaSchema representation (line 7) is produced:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
NotCriteria: {
  ALIAS: "NOT",
  DESCRIPTION: "Logical NOT expression.",
  TYPE: "org.refcodes.criteria.NotCriteria",
  AndCriteria: {
    ALIAS: "AND",
    DESCRIPTION: "Logical AND expression.",
    TYPE: "org.refcodes.criteria.AndCriteria",
    NotCriteria: {
      ALIAS: "NOT",
      DESCRIPTION: "Logical NOT expression.",
      TYPE: "org.refcodes.criteria.NotCriteria",
      AndCriteria: {
        ALIAS: "AND",
        DESCRIPTION: "Logical AND expression.",
        TYPE: "org.refcodes.criteria.AndCriteria",
        NotEqualWithCriteria: {
          ALIAS: "UNEQUAL",
          DESCRIPTION: "Logical UNEQUAL (!=) expression.",
          KEY: "City",
          TYPE: "org.refcodes.criteria.NotEqualWithCriteria",
          VALUE: "Berlin"
        },
        LessThanCriteria: {
          ALIAS: "LESS_OR_EQUAL_THAN",
          DESCRIPTION: "Logical LESS (<) expression.",
          KEY: "ZIP",
          TYPE: "org.refcodes.criteria.LessThanCriteria",
          VALUE: 30000
        }
      }
    },
    GreaterThanCriteria: {
      ALIAS: "GREATER_THAN",
      DESCRIPTION: "Logical GREATER (>) expression.",
      KEY: "AGE",
      TYPE: "org.refcodes.criteria.GreaterThanCriteria",
      VALUE: 65
    }
  }
}

The CriteriaSchema is a JSON alike representation of your expression and helps to further process, analyze, debug or document your expressions.

Examples

See the example source codes of this artifact for further information on the usage of this artifact.

Contribution guidelines

  • Report issues
  • Finding bugs
  • Helping fixing bugs
  • Making code and documentation better
  • Enhance the code

Who do I talk to?

  • Siegfried Steiner (steiner@refcodes.org)

Terms and conditions

The REFCODES.ORG group of artifacts is published under some open source licenses; covered by the refcodes-licensing (org.refcodes group) artifact - evident in each artifact in question as of the pom.xml dependency included in such artifact.