A Java library to compile and parse simple filter queries.
The following query formats can be parsed:
// multiple terms with AND and OR operator
(x > 0 && !(y < 1 || z == 'test')) //full text search
"text search" test -example
Default term operators:
==, !=, <, <=, >, >=, text, regex
Logical operators:
AND, OR, XOR, NOR
To build the library from sources:
Clone github repository
$ git clone https://github.com/nRo/SimpleQueryCompiler.git
Change to the created folder and run
mvn install$ cd SimpleQueryCompiler
$ mvn install
Include it by adding the following to your project's
pom.xml:
<dependencies>
...
<dependency>
<groupId>de.alexgruen</groupId>
<artifactId>querycompiler</artifactId>
<version>0.3-SNAPSHOT</version>
</dependency>
...
</dependencies>This library works by defining by building a QueryCompiler for the desired output type. Building a QueryCompiler requires the definition of Creators for the supported term operators (==, !=, <, <=, ...) and logical operators (AND, OR,...). The Creators are responsible to create the target object, for example SQL queries, elasticsearch queries,...
There are two ways to create a QueryCompiler. For the following examples be specify example ElasticSearchQuery as target class:
publicabstractclassElasticSearchQueryimplementsQuery{
//Every query must create an ElasticSearch QueryBuilderpublicabstractQueryBuildercreate();
...
}If a QueryCompiler is built, input strings can be parsed with:
QueryCompiler<ElasticSearchQuery> esCompiler = ...
ElasticSearchQueryesQuery = esCompiler.compile("(x > 1 && y < 1)");DefaultCreator is an abstract class that contains functions for all standard operations. If such a method is not overridden, an error is thrown if the input query string contains the respective operator.
publicclassElasticSearchQueryCreatorextendsDefaultCreator<ElasticSearchQuery> {
//override eq(field,value) to support == operator in input queries//text search is implemented by overriding the fullSearch(Value value) method@OverridepublicElasticSearchQueryeq(Fieldfield, Valuevalue) {
returnnewElasticSearchQuery() {
@OverridepublicQueryBuildercreate() {
returnQueryBuilders.termQuery(field.toString(), value.getValue());
}
};
}
//override other term operator methods: ne (!=), le(>=), lt(>),...
...
//override and(query....) to support AND concatenation of terms@OverridepublicElasticSearchQueryand(ElasticSearchQuery... queries) {
returnnewElasticSearchQuery() {
@OverridepublicQueryBuildercreate() {
BoolQueryBuilderboolQueryBuilder = boolQuery();
for(ElasticSearchQueryq : queries){
boolQueryBuilder.must(q.create());
}
returnboolQueryBuilder;
}
};
}
//override other logic operator methods: OR, XOR, NOR,...
}The compiler can then be created using the following Builder methods:
QueryCompiler<ElasticSearchQuery> esCompiler
= QueryCompiler.create(ElasticSearchQuery.class)
.withDefaultCreator(newElasticSearchQueryCreator())
.build();QueryCompiler<ElasticSearchQuery> compiler = QueryCompiler
.create(ElasticSearchQuery.class)
.withTermCreator(
TermOperators.EQ,
(field,value) -> newElasticSearchQuery() {
@OverridepublicStringcreate() {
returnQueryBuilders.termQuery(field.toString(), value.getValue());
}
}
)
.withANDCreator(
(queries) -> newElasticSearchQuery() {
BoolQueryBuilderboolQueryBuilder = boolQuery();
for(ElasticSearchQueryq : queries){
boolQueryBuilder.must(q.create());
}
returnboolQueryBuilder;
}
)
).build()Custom term operators can be added to the QueryCompilerBuilder. In this example we add a PHRASE operator that creates an matchPhrase query for elasticsearch. User can than input queries like:;
x PHRASE 'test phrase' || y == 'test'
QueryCompiler<ElasticSearchQuery> compiler = QueryCompiler
.create(ElasticSearchQuery.class)
.withTermCreator(
.withTermCreator(
newTermOperator("PHRASE"),
(f,v) -> newElasticSearchQuery() {
@OverridepublicStringcreate() {
returnQueryBuilders.matchPhraseQuery(field.toString(), value.getValue());
}
}
)
)
)Queries are first compiled to a tree before being converted to the target object. This tree can be printed
QueryTreetree = compiler.compileTree(
"(x > 0 && y < 1 || (u == 2 && (h != 1 || z < 2))) || z == 2");
Stringstr = QueryPrinter.DEFAULT.toString(tree.getRoot());
/** * ──┐ OR ├──┐ AND │ ├── (x > 0) │ └── (y < 1) ├──┐ AND │ ├── (u == 2) │ └──┐ OR │ ├── (h != 1) │ └── (z < 2) └── (z == 2) **///The tree can then be converted to the target objectElasticSearchQueryesQuery = compiler.compile(tree);