diff --git a/pom.xml b/pom.xml
index 31213b1ecce..5972285d55b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,7 @@
7.70.0.Final
netgrif-oss
https://sonarcloud.io
+ 4.13.1
@@ -505,6 +506,14 @@
jackson-module-jsonSchema
2.13.2
+
+
+
+ org.antlr
+ antlr4-runtime
+ ${antlr4.version}
+
+
@@ -779,6 +788,22 @@
license-maven-plugin
2.0.0
+
+ org.antlr
+ antlr4-maven-plugin
+ ${antlr4.version}
+
+ src/main/java
+ ${project.build.directory}/generated-sources/java/
+
+
+
+
+ antlr4
+
+
+
+
diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
index f00157c40fa..065b70c23c4 100644
--- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
+++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
@@ -32,6 +32,7 @@ import com.netgrif.application.engine.petrinet.domain.version.Version
import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService
import com.netgrif.application.engine.petrinet.service.interfaces.IUriService
import com.netgrif.application.engine.rules.domain.RuleRepository
+import com.netgrif.application.engine.search.interfaces.ISearchService
import com.netgrif.application.engine.startup.DefaultFiltersRunner
import com.netgrif.application.engine.startup.FilterRunner
import com.netgrif.application.engine.utils.FullPageRequest
@@ -177,6 +178,9 @@ class ActionDelegate /*TODO: release/8.0.0: implements ActionAPI*/ {
@Autowired
PublicViewProperties publicViewProperties
+ @Autowired
+ ISearchService searchService
+
FrontendActionOutcome Frontend
/**
@@ -2321,4 +2325,20 @@ class ActionDelegate /*TODO: release/8.0.0: implements ActionAPI*/ {
Case taskCase = workflowService.findOne(task.caseId)
return taskCase.getPetriNet().getDataSet().get(fieldId)
}
+
+ String explainQuery(String query) {
+ return searchService.explainQuery(query)
+ }
+
+ long count(String query) {
+ return searchService.count(query)
+ }
+
+ def search(String query) {
+ return searchService.search(query)
+ }
+
+ boolean exists(String query) {
+ return searchService.exists(query)
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticCase.java b/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticCase.java
index ebe113ea85c..49480de8bae 100644
--- a/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticCase.java
+++ b/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticCase.java
@@ -77,11 +77,9 @@ public class ElasticCase {
private Map dataSet;
- @Field(type = Keyword)
- private Set taskIds;
+ private Map tasks;
- @Field(type = Keyword)
- private Set taskMongoIds;
+ private Map places;
@Field(type = Keyword)
private Set enabledRoles;
@@ -126,8 +124,6 @@ public ElasticCase(Case useCase) {
author = useCase.getAuthor().getId();
authorName = useCase.getAuthor().getFullName();
authorEmail = useCase.getAuthor().getEmail();
- taskIds = useCase.getTasks().keySet();
- taskMongoIds = useCase.getTasks().values().stream().map(TaskPair::getTaskStringId).collect(Collectors.toSet());
enabledRoles = new HashSet<>(useCase.getEnabledRoles());
viewRoles = new HashSet<>(useCase.getViewRoles());
viewUserRefs = new HashSet<>(useCase.getViewUserRefs());
@@ -137,6 +133,17 @@ public ElasticCase(Case useCase) {
tags = new HashMap<>(useCase.getTags());
dataSet = new HashMap<>();
+
+ tasks = useCase.getTasks().entrySet().stream()
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> new Task(entry.getValue().getTaskStringId(), entry.getValue().getState(), entry.getValue().getUserId())
+ ));
+ places = useCase.getActivePlaces().entrySet().stream()
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> new Place(entry.getValue())
+ ));
}
public void update(ElasticCase useCase) {
@@ -146,8 +153,6 @@ public void update(ElasticCase useCase) {
uriNodeId = useCase.getUriNodeId();
}
title = useCase.getTitle();
- taskIds = useCase.getTaskIds();
- taskMongoIds = useCase.getTaskMongoIds();
enabledRoles = useCase.getEnabledRoles();
viewRoles = useCase.getViewRoles();
viewUserRefs = useCase.getViewUserRefs();
@@ -157,5 +162,7 @@ public void update(ElasticCase useCase) {
tags = useCase.getTags();
dataSet = useCase.getDataSet();
+ tasks = useCase.getTasks();
+ places = useCase.getPlaces();
}
}
\ No newline at end of file
diff --git a/src/main/java/com/netgrif/application/engine/elastic/domain/MapField.java b/src/main/java/com/netgrif/application/engine/elastic/domain/MapField.java
index 0e3d22a4317..e75fb41cd3e 100644
--- a/src/main/java/com/netgrif/application/engine/elastic/domain/MapField.java
+++ b/src/main/java/com/netgrif/application/engine/elastic/domain/MapField.java
@@ -1,14 +1,12 @@
package com.netgrif.application.engine.elastic.domain;
+import com.netgrif.application.engine.petrinet.domain.I18nString;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.annotations.Field;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.Keyword;
@@ -20,17 +18,21 @@ public class MapField extends TextField {
@Field(type = Keyword)
public List keyValue = new ArrayList<>();
- public MapField(String key, List values) {
+ public List options = new ArrayList<>();
+
+ public MapField(String key, List values, Map options) {
super(values);
this.keyValue.add(key);
+ this.options.addAll(options.keySet());
}
- public MapField(Map> valuePairs) {
+ public MapField(Map> valuePairs, Map options) {
super();
valuePairs.forEach((key, value) -> {
this.keyValue.add(key);
this.textValue.addAll(value);
this.fulltextValue.addAll(value);
});
+ this.options.addAll(options.keySet());
}
}
diff --git a/src/main/java/com/netgrif/application/engine/elastic/domain/Place.java b/src/main/java/com/netgrif/application/engine/elastic/domain/Place.java
new file mode 100644
index 00000000000..b6c14908bd1
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/elastic/domain/Place.java
@@ -0,0 +1,21 @@
+package com.netgrif.application.engine.elastic.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.data.elasticsearch.annotations.Field;
+
+import static org.springframework.data.elasticsearch.annotations.FieldType.Integer;
+import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode
+@AllArgsConstructor
+public class Place {
+
+ @Field(type = Integer)
+ public Integer marking;
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/netgrif/application/engine/elastic/domain/Task.java b/src/main/java/com/netgrif/application/engine/elastic/domain/Task.java
new file mode 100644
index 00000000000..fecd15381ed
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/elastic/domain/Task.java
@@ -0,0 +1,27 @@
+package com.netgrif.application.engine.elastic.domain;
+
+import com.netgrif.application.engine.workflow.domain.State;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.springframework.data.elasticsearch.annotations.Field;
+
+import static org.springframework.data.elasticsearch.annotations.FieldType.Text;
+
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode
+@AllArgsConstructor
+public class Task {
+
+ @Field(type = Text)
+ public String stringId;
+
+ @Field(type = Text)
+ public State state;
+
+ @Field(type = Text)
+ public String userId;
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/transform/EnumerationMapFieldTransformer.java b/src/main/java/com/netgrif/application/engine/elastic/service/transform/EnumerationMapFieldTransformer.java
index d472da66020..393cb1f0709 100644
--- a/src/main/java/com/netgrif/application/engine/elastic/service/transform/EnumerationMapFieldTransformer.java
+++ b/src/main/java/com/netgrif/application/engine/elastic/service/transform/EnumerationMapFieldTransformer.java
@@ -18,7 +18,7 @@ public MapField transform(EnumerationMapField caseField, EnumerationMapField pet
DataFieldValue selectedKey = caseField.getValue();
String value = selectedKey != null ? selectedKey.getValue() : null;
I18nString selectedValue = options.get(value) != null ? options.get(value) : new I18nString();
- return new MapField(value, selectedValue.collectTranslations());
+ return new MapField(value, selectedValue.collectTranslations(), caseField.getOptions());
}
@Override
diff --git a/src/main/java/com/netgrif/application/engine/elastic/service/transform/MultichoiceMapFieldTransformer.java b/src/main/java/com/netgrif/application/engine/elastic/service/transform/MultichoiceMapFieldTransformer.java
index 80c4ea2bd6c..59948dc3123 100644
--- a/src/main/java/com/netgrif/application/engine/elastic/service/transform/MultichoiceMapFieldTransformer.java
+++ b/src/main/java/com/netgrif/application/engine/elastic/service/transform/MultichoiceMapFieldTransformer.java
@@ -25,7 +25,7 @@ public MapField transform(MultichoiceMapField caseField, MultichoiceMapField pet
I18nString selectedValue = options.get(value) != null ? options.get(value) : new I18nString();
fieldValues.put(value, selectedValue.collectTranslations());
}
- return new MapField(fieldValues);
+ return new MapField(fieldValues, options);
}
@Override
diff --git a/src/main/java/com/netgrif/application/engine/search/QueryLangErrorListener.java b/src/main/java/com/netgrif/application/engine/search/QueryLangErrorListener.java
new file mode 100644
index 00000000000..03057c23102
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/QueryLangErrorListener.java
@@ -0,0 +1,36 @@
+package com.netgrif.application.engine.search;
+
+import lombok.Getter;
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+public class QueryLangErrorListener extends BaseErrorListener {
+ List errorMessages = new ArrayList<>();
+
+ @Override
+ public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e)
+ throws ParseCancellationException {
+ errorMessages.add(underlineError(recognizer, (Token) offendingSymbol, line, charPositionInLine, msg));
+ }
+
+ protected String underlineError(Recognizer, ?> recognizer, Token offendingToken, int line, int charPositionInLine, String msg) {
+ String underlineErrorMsg = msg + "\n";
+ int start = offendingToken.getStartIndex();
+ int stop = offendingToken.getStopIndex();
+ if (start > stop) {
+ return underlineErrorMsg;
+ }
+ CommonTokenStream tokens = (CommonTokenStream) recognizer.getInputStream();
+ String input = tokens.getTokenSource().getInputStream().toString();
+ String[] lines = input.split("\n");
+ String errorLine = lines[line - 1];
+ underlineErrorMsg += errorLine + "\n";
+ underlineErrorMsg += " ".repeat(charPositionInLine) + "^".repeat(stop - start + 1) + "\n";
+
+ return underlineErrorMsg;
+ }
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/QueryLangEvaluator.java b/src/main/java/com/netgrif/application/engine/search/QueryLangEvaluator.java
new file mode 100644
index 00000000000..72ca6548115
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/QueryLangEvaluator.java
@@ -0,0 +1,1422 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.search.antlr4.QueryLangBaseListener;
+import com.netgrif.application.engine.search.antlr4.QueryLangParser;
+import com.netgrif.application.engine.auth.domain.QUser;
+import com.netgrif.application.engine.petrinet.domain.QPetriNet;
+import com.netgrif.application.engine.search.enums.ComparisonType;
+import com.netgrif.application.engine.search.enums.QueryType;
+import com.netgrif.application.engine.workflow.domain.QCase;
+import com.netgrif.application.engine.workflow.domain.QTask;
+import com.netgrif.application.engine.workflow.domain.State;
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.DateTimePath;
+import com.querydsl.core.types.dsl.StringPath;
+import lombok.Getter;
+import lombok.Setter;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.ParseTreeProperty;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.bson.types.ObjectId;
+import org.bson.types.QObjectId;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.netgrif.application.engine.search.utils.SearchUtils.*;
+
+public class QueryLangEvaluator extends QueryLangBaseListener {
+
+ private final ParseTreeProperty elasticQuery = new ParseTreeProperty<>();
+ private final ParseTreeProperty mongoQuery = new ParseTreeProperty<>();
+
+ @Getter
+ private QueryType type;
+ @Getter
+ private Boolean multiple;
+ @Getter
+ @Setter
+ private Boolean searchWithElastic = false;
+ @Getter
+ private Predicate fullMongoQuery;
+ @Getter
+ private String fullElasticQuery;
+ @Getter
+ private Pageable pageable;
+
+ private int pageNumber = 0;
+ private int pageSize = 20;
+ private final List sortOrders = new ArrayList<>();
+
+ public void setElasticQuery(ParseTree node, String query) {
+ elasticQuery.put(node, query);
+ }
+
+ public String getElasticQuery(ParseTree node) {
+ return elasticQuery.get(node);
+ }
+
+ public void setMongoQuery(ParseTree node, Predicate predicate) {
+ mongoQuery.put(node, predicate);
+ }
+
+ public Predicate getMongoQuery(ParseTree node) {
+ return mongoQuery.get(node);
+ }
+
+ private void processBasicExpression(ParseTree child, ParseTree current) {
+ setMongoQuery(current, getMongoQuery(child));
+ setElasticQuery(current, getElasticQuery(child));
+ }
+
+ private void processOrExpression(List children, ParseTree current) {
+ List predicates = children.stream()
+ .map(this::getMongoQuery)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ String elasticQuery = children.stream()
+ .map(this::getElasticQuery)
+ .filter(Objects::nonNull)
+ .collect(Collectors.joining(" OR "));
+
+ if (!predicates.isEmpty()) {
+ BooleanBuilder predicate = new BooleanBuilder();
+ predicates.forEach(predicate::or);
+ setMongoQuery(current, predicate);
+ }
+ setElasticQuery(current, elasticQuery.isBlank() ? null : elasticQuery);
+ }
+
+ private void processAndExpression(List children, ParseTree current) {
+ List predicates = children.stream()
+ .map(this::getMongoQuery)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ String elasticQuery = children.stream()
+ .map(this::getElasticQuery)
+ .filter(Objects::nonNull)
+ .collect(Collectors.joining(" AND "));
+
+ if (!predicates.isEmpty()) {
+ BooleanBuilder predicate = new BooleanBuilder();
+ predicates.forEach(predicate::and);
+ setMongoQuery(current, predicate);
+ }
+ setElasticQuery(current, elasticQuery.isBlank() ? null : elasticQuery);
+ }
+
+ private void processConditionGroup(ParseTree child, ParseTree current, Boolean not, Boolean parenthesis) {
+ Predicate predicate = getMongoQuery(child);
+ String elasticQuery = getElasticQuery(child);
+
+ if (predicate != null) {
+ predicate = not ? (predicate).not() : (predicate);
+ }
+
+ if (elasticQuery != null) {
+ if (parenthesis) {
+ elasticQuery = "(" + elasticQuery + ")";
+ }
+
+ if (not) {
+ elasticQuery = "NOT " + elasticQuery;
+ }
+ }
+
+ setMongoQuery(current, predicate);
+ setElasticQuery(current, elasticQuery);
+ }
+
+ @Override
+ public void enterProcessQuery(QueryLangParser.ProcessQueryContext ctx) {
+ type = QueryType.PROCESS;
+ multiple = ctx.resource.getType() == QueryLangParser.PROCESSES;
+ }
+
+ @Override
+ public void exitProcessQuery(QueryLangParser.ProcessQueryContext ctx) {
+ processBasicExpression(ctx.processConditions(), ctx);
+ fullMongoQuery = getMongoQuery(ctx);
+ fullElasticQuery = getElasticQuery(ctx);
+ pageable = PageRequest.of(pageNumber, pageSize, Sort.by(sortOrders));
+ }
+
+ @Override
+ public void enterCaseQuery(QueryLangParser.CaseQueryContext ctx) {
+ type = QueryType.CASE;
+ multiple = ctx.resource.getType() == QueryLangParser.CASES;
+ }
+
+ @Override
+ public void exitCaseQuery(QueryLangParser.CaseQueryContext ctx) {
+ processBasicExpression(ctx.caseConditions(), ctx);
+ fullMongoQuery = getMongoQuery(ctx);
+ fullElasticQuery = getElasticQuery(ctx);
+ pageable = PageRequest.of(pageNumber, pageSize, Sort.by(sortOrders));
+ }
+
+ @Override
+ public void enterTaskQuery(QueryLangParser.TaskQueryContext ctx) {
+ type = QueryType.TASK;
+ multiple = ctx.resource.getType() == QueryLangParser.TASKS;
+ }
+
+ @Override
+ public void exitTaskQuery(QueryLangParser.TaskQueryContext ctx) {
+ processBasicExpression(ctx.taskConditions(), ctx);
+ fullMongoQuery = getMongoQuery(ctx);
+ fullElasticQuery = getElasticQuery(ctx);
+ pageable = PageRequest.of(pageNumber, pageSize, Sort.by(sortOrders));
+ }
+
+ @Override
+ public void enterUserQuery(QueryLangParser.UserQueryContext ctx) {
+ type = QueryType.USER;
+ multiple = ctx.resource.getType() == QueryLangParser.USERS;
+ }
+
+ @Override
+ public void exitUserQuery(QueryLangParser.UserQueryContext ctx) {
+ processBasicExpression(ctx.userConditions(), ctx);
+ fullMongoQuery = getMongoQuery(ctx);
+ fullElasticQuery = getElasticQuery(ctx);
+ pageable = PageRequest.of(pageNumber, pageSize, Sort.by(sortOrders));
+ }
+
+ @Override
+ public void exitProcessConditions(QueryLangParser.ProcessConditionsContext ctx) {
+ processBasicExpression(ctx.processOrExpression(), ctx);
+ }
+
+ @Override
+ public void exitProcessOrExpression(QueryLangParser.ProcessOrExpressionContext ctx) {
+ List children = ctx.processAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processOrExpression(children, ctx);
+ }
+
+ @Override
+ public void exitProcessAndExpression(QueryLangParser.ProcessAndExpressionContext ctx) {
+ List children = ctx.processConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processAndExpression(children, ctx);
+ }
+
+ @Override
+ public void exitProcessConditionGroupBasic(QueryLangParser.ProcessConditionGroupBasicContext ctx) {
+ processConditionGroup(ctx.processCondition(), ctx, false, false);
+ }
+
+ @Override
+ public void exitProcessConditionGroupParenthesis(QueryLangParser.ProcessConditionGroupParenthesisContext ctx) {
+ processConditionGroup(ctx.processConditions(), ctx, ctx.NOT() != null, true);
+ }
+
+ @Override
+ public void exitProcessCondition(QueryLangParser.ProcessConditionContext ctx) {
+ processBasicExpression(ctx.processComparisons(), ctx);
+ }
+
+ @Override
+ public void exitCaseConditions(QueryLangParser.CaseConditionsContext ctx) {
+ processBasicExpression(ctx.caseOrExpression(), ctx);
+ }
+
+ @Override
+ public void exitCaseOrExpression(QueryLangParser.CaseOrExpressionContext ctx) {
+ List children = ctx.caseAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processOrExpression(children, ctx);
+ }
+
+ @Override
+ public void exitCaseAndExpression(QueryLangParser.CaseAndExpressionContext ctx) {
+ List children = ctx.caseConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processAndExpression(children, ctx);
+ }
+
+ @Override
+ public void exitCaseConditionGroupBasic(QueryLangParser.CaseConditionGroupBasicContext ctx) {
+ processConditionGroup(ctx.caseCondition(), ctx, false, false);
+ }
+
+ @Override
+ public void exitCaseConditionGroupParenthesis(QueryLangParser.CaseConditionGroupParenthesisContext ctx) {
+ processConditionGroup(ctx.caseConditions(), ctx, ctx.NOT() != null, true);
+ }
+
+ @Override
+ public void exitCaseCondition(QueryLangParser.CaseConditionContext ctx) {
+ processBasicExpression(ctx.caseComparisons(), ctx);
+ }
+
+ @Override
+ public void exitTaskConditions(QueryLangParser.TaskConditionsContext ctx) {
+ processBasicExpression(ctx.taskOrExpression(), ctx);
+ }
+
+ @Override
+ public void exitTaskOrExpression(QueryLangParser.TaskOrExpressionContext ctx) {
+ List children = ctx.taskAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processOrExpression(children, ctx);
+ }
+
+ @Override
+ public void exitTaskAndExpression(QueryLangParser.TaskAndExpressionContext ctx) {
+ List children = ctx.taskConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processAndExpression(children, ctx);
+ }
+
+ @Override
+ public void exitTaskConditionGroupBasic(QueryLangParser.TaskConditionGroupBasicContext ctx) {
+ processConditionGroup(ctx.taskCondition(), ctx, false, false);
+ }
+
+ @Override
+ public void exitTaskConditionGroupParenthesis(QueryLangParser.TaskConditionGroupParenthesisContext ctx) {
+ processConditionGroup(ctx.taskConditions(), ctx, ctx.NOT() != null, true);
+ }
+
+ @Override
+ public void exitTaskCondition(QueryLangParser.TaskConditionContext ctx) {
+ processBasicExpression(ctx.taskComparisons(), ctx);
+ }
+
+ @Override
+ public void exitUserConditions(QueryLangParser.UserConditionsContext ctx) {
+ processBasicExpression(ctx.userOrExpression(), ctx);
+ }
+
+ @Override
+ public void exitUserOrExpression(QueryLangParser.UserOrExpressionContext ctx) {
+ List children = ctx.userAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processOrExpression(children, ctx);
+ }
+
+ @Override
+ public void exitUserAndExpression(QueryLangParser.UserAndExpressionContext ctx) {
+ List children = ctx.userConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processAndExpression(children, ctx);
+ }
+
+ @Override
+ public void exitUserConditionGroupBasic(QueryLangParser.UserConditionGroupBasicContext ctx) {
+ processConditionGroup(ctx.userCondition(), ctx, false, false);
+ }
+
+ @Override
+ public void exitUserConditionGroupParenthesis(QueryLangParser.UserConditionGroupParenthesisContext ctx) {
+ processConditionGroup(ctx.userConditions(), ctx, ctx.NOT() != null, true);
+ }
+
+ @Override
+ public void exitUserCondition(QueryLangParser.UserConditionContext ctx) {
+ processBasicExpression(ctx.userComparisons(), ctx);
+ }
+
+ @Override
+ public void exitProcessComparisons(QueryLangParser.ProcessComparisonsContext ctx) {
+ processBasicExpression(ctx.children.get(0), ctx);
+ }
+
+ @Override
+ public void exitCaseComparisons(QueryLangParser.CaseComparisonsContext ctx) {
+ processBasicExpression(ctx.children.get(0), ctx);
+ }
+
+ @Override
+ public void exitTaskComparisons(QueryLangParser.TaskComparisonsContext ctx) {
+ processBasicExpression(ctx.children.get(0), ctx);
+ }
+
+ @Override
+ public void exitUserComparisons(QueryLangParser.UserComparisonsContext ctx) {
+ processBasicExpression(ctx.children.get(0), ctx);
+ }
+
+ @Override
+ public void exitIdBasic(QueryLangParser.IdBasicContext ctx) {
+ QObjectId qObjectId;
+ Token op = ctx.objectIdComparison().op;
+ boolean not = ctx.objectIdComparison().NOT() != null;
+ checkOp(ComparisonType.ID, op);
+ ObjectId objectId = getObjectIdValue(ctx.objectIdComparison().STRING().getText());
+
+ switch (type) {
+ case PROCESS:
+ qObjectId = QPetriNet.petriNet.id;
+ break;
+ case CASE:
+ qObjectId = QCase.case$.id;
+ setElasticQuery(ctx, buildElasticQuery("stringId", op.getType(), objectId.toString(), not));
+ break;
+ case TASK:
+ qObjectId = QTask.task.id;
+ break;
+ case USER:
+ qObjectId = QUser.user.id;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildObjectIdPredicate(qObjectId, op.getType(), objectId, not));
+ }
+
+ @Override
+ public void exitIdList(QueryLangParser.IdListContext ctx) {
+ QObjectId qObjectId;
+ Token op = ctx.inListStringComparison().op;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ checkOp(ComparisonType.ID, op);
+ List objectIdList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getObjectIdValue(node.getText()))
+ .collect(Collectors.toList());
+ List stringIdList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getStringValue(node.getText()))
+ .collect(Collectors.toList());
+
+ switch (type) {
+ case PROCESS:
+ qObjectId = QPetriNet.petriNet.id;
+ break;
+ case CASE:
+ qObjectId = QCase.case$.id;
+ setElasticQuery(ctx, buildElasticQueryInList("stringId", stringIdList, not));
+ break;
+ case TASK:
+ qObjectId = QTask.task.id;
+ break;
+ case USER:
+ qObjectId = QUser.user.id;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildObjectIdPredicateInList(qObjectId, objectIdList, not));
+ }
+
+ @Override
+ public void exitTitleBasic(QueryLangParser.TitleBasicContext ctx) {
+ StringPath stringPath;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ switch (type) {
+ case PROCESS:
+ stringPath = QPetriNet.petriNet.title.defaultValue;
+ break;
+ case CASE:
+ stringPath = QCase.case$.title;
+ setElasticQuery(ctx, buildElasticQuery("title", op.getType(), string, not));
+ break;
+ case TASK:
+ stringPath = QTask.task.title.defaultValue;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitTitleList(QueryLangParser.TitleListContext ctx) {
+ StringPath stringPath;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ switch (type) {
+ case PROCESS:
+ stringPath = QPetriNet.petriNet.title.defaultValue;
+ break;
+ case CASE:
+ stringPath = QCase.case$.title;
+ setElasticQuery(ctx, buildElasticQueryInList("title", stringList, not));
+ break;
+ case TASK:
+ stringPath = QTask.task.title.defaultValue;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitTitleRange(QueryLangParser.TitleRangeContext ctx) {
+ StringPath stringPath;
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ switch (type) {
+ case PROCESS:
+ stringPath = QPetriNet.petriNet.title.defaultValue;
+ break;
+ case CASE:
+ stringPath = QCase.case$.title;
+ setElasticQuery(ctx, buildElasticQueryInRange("title", leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ break;
+ case TASK:
+ stringPath = QTask.task.title.defaultValue;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildStringPredicateInRange(stringPath, leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitIdentifierBasic(QueryLangParser.IdentifierBasicContext ctx) {
+ StringPath stringPath = QPetriNet.petriNet.identifier;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitIdentifierList(QueryLangParser.IdentifierListContext ctx) {
+ StringPath stringPath = QPetriNet.petriNet.identifier;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitIdentifierRange(QueryLangParser.IdentifierRangeContext ctx) {
+ StringPath stringPath = QPetriNet.petriNet.identifier;
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, buildStringPredicateInRange(stringPath, leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitVersionBasic(QueryLangParser.VersionBasicContext ctx) {
+ Token op = ctx.op;
+ boolean not = ctx.NOT() != null;
+ String versionString = ctx.VERSION_NUMBER().getText();
+
+ setMongoQuery(ctx, buildVersionPredicate(op.getType(), versionString, not));
+ }
+
+ @Override
+ public void exitVersionListCmp(QueryLangParser.VersionListCmpContext ctx) {
+ boolean not = ctx.inListVersionComparison().NOT() != null;
+ List stringList = ctx.inListVersionComparison().versionList().VERSION_NUMBER().stream().map(TerminalNode::getText).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildVersionPredicateInList(stringList, not));
+ }
+
+ @Override
+ public void exitVersionRangeCmp(QueryLangParser.VersionRangeCmpContext ctx) {
+ boolean not = ctx.inRangeVersionComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeVersionComparison().versionRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeVersionComparison().versionRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeVersionComparison().versionRange().VERSION_NUMBER(0).getText());
+ String rightString = getStringValue(ctx.inRangeVersionComparison().versionRange().VERSION_NUMBER(1).getText());
+
+ setMongoQuery(ctx, buildVersionPredicateInRange(leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitCdDateBasic(QueryLangParser.CdDateBasicContext ctx) {
+ DateTimePath dateTimePath;
+ Token op = ctx.dateComparison().op;
+ boolean not = ctx.dateComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateComparison().DATE().getText());
+
+ switch (type) {
+ case PROCESS:
+ dateTimePath = QPetriNet.petriNet.creationDate;
+ break;
+ case CASE:
+ dateTimePath = QCase.case$.creationDate;
+ setElasticQuery(ctx, buildElasticQuery("creationDateSortable", op.getType(), String.valueOf(Timestamp.valueOf(localDateTime).getTime()), not));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildDateTimePredicate(dateTimePath, op.getType(), localDateTime, not));
+ }
+
+ @Override
+ public void exitCdDateTimeBasic(QueryLangParser.CdDateTimeBasicContext ctx) {
+ DateTimePath dateTimePath;
+ Token op = ctx.dateTimeComparison().op;
+ boolean not = ctx.dateTimeComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateTimeComparison().DATETIME().getText());
+
+ switch (type) {
+ case PROCESS:
+ dateTimePath = QPetriNet.petriNet.creationDate;
+ break;
+ case CASE:
+ dateTimePath = QCase.case$.creationDate;
+ setElasticQuery(ctx, buildElasticQuery("creationDateSortable", op.getType(), String.valueOf(Timestamp.valueOf(localDateTime).getTime()), not));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildDateTimePredicate(dateTimePath, op.getType(), localDateTime, not));
+ }
+
+ @Override
+ public void exitCdDateList(QueryLangParser.CdDateListContext ctx) {
+ DateTimePath dateTimePath;
+ boolean not = ctx.inListDateComparison().NOT() != null;
+ List terminalNodeList = ctx.inListDateComparison().dateList() != null ? ctx.inListDateComparison().dateList().DATE() : ctx.inListDateComparison().dateTimeList().DATETIME() ;
+ List stringDateList = terminalNodeList.stream().map(TerminalNode::getText).collect(Collectors.toList());
+
+ switch (type) {
+ case PROCESS:
+ dateTimePath = QPetriNet.petriNet.creationDate;
+ break;
+ case CASE:
+ dateTimePath = QCase.case$.creationDate;
+ List timestampStringList = stringDateList.stream().map(dateString -> {
+ LocalDateTime localDateTime = toDateTime(dateString);
+ return String.valueOf(Timestamp.valueOf(localDateTime).getTime());
+ }).collect(Collectors.toList());
+ setElasticQuery(ctx, buildElasticQueryInList("creationDateSortable", timestampStringList, not));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildDateTimePredicateInList(dateTimePath, stringDateList, not));
+ }
+
+ @Override
+ public void exitCdDateRange(QueryLangParser.CdDateRangeContext ctx) {
+ DateTimePath dateTimePath;
+ boolean not = ctx.inRangeDateComparison().NOT() != null;
+ boolean leftEndpointOpen;
+ boolean rightEndpointOpen;
+ LocalDateTime leftDateTime;
+ LocalDateTime rightDateTime;
+ if (ctx.inRangeDateComparison().dateRange() != null) {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(1).getText());
+ } else {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(1).getText());
+ }
+
+
+ switch (type) {
+ case PROCESS:
+ dateTimePath = QPetriNet.petriNet.creationDate;
+ break;
+ case CASE:
+ dateTimePath = QCase.case$.creationDate;
+ setElasticQuery(ctx, buildElasticQueryInRange("creationDateSortable", String.valueOf(Timestamp.valueOf(leftDateTime).getTime()), leftEndpointOpen, String.valueOf(Timestamp.valueOf(rightDateTime).getTime()), rightEndpointOpen, not));
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown query type: " + type);
+ }
+
+ setMongoQuery(ctx, buildDateTimePredicateInRange(dateTimePath, leftDateTime, leftEndpointOpen, rightDateTime, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitProcessIdBasic(QueryLangParser.ProcessIdBasicContext ctx) {
+ StringPath stringPath = QTask.task.processId;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitProcessIdList(QueryLangParser.ProcessIdListContext ctx) {
+ StringPath stringPath = QTask.task.processId;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getStringValue(node.getText()))
+ .collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitProcessIdObjIdBasic(QueryLangParser.ProcessIdObjIdBasicContext ctx) {
+ QObjectId qObjectId = QCase.case$.petriNetObjectId;
+ Token op = ctx.objectIdComparison().op;
+ boolean not = ctx.objectIdComparison().NOT() != null;
+ ObjectId objectId = getObjectIdValue(ctx.objectIdComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildObjectIdPredicate(qObjectId, op.getType(), objectId, not));
+ setElasticQuery(ctx, buildElasticQuery("processId", op.getType(), objectId.toString(), not));
+ }
+
+ @Override
+ public void exitProcessIdObjIdList(QueryLangParser.ProcessIdObjIdListContext ctx) {
+ QObjectId qObjectId = QCase.case$.petriNetObjectId;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List objectIdList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getObjectIdValue(node.getText()))
+ .collect(Collectors.toList());
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getStringValue(node.getText()))
+ .collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildObjectIdPredicateInList(qObjectId, objectIdList, not));
+ setElasticQuery(ctx, buildElasticQueryInList("processId", stringList, not));
+ }
+
+ @Override
+ public void exitProcessIdentifierBasic(QueryLangParser.ProcessIdentifierBasicContext ctx) {
+ StringPath stringPath = QCase.case$.processIdentifier;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ setElasticQuery(ctx, buildElasticQuery("processIdentifier", op.getType(), string, not));
+ }
+
+ @Override
+ public void exitProcessIdentifierList(QueryLangParser.ProcessIdentifierListContext ctx) {
+ StringPath stringPath = QCase.case$.processIdentifier;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ setElasticQuery(ctx, buildElasticQueryInList("processIdentifier", stringList, not));
+ }
+
+ @Override
+ public void exitProcessIdentifierRange(QueryLangParser.ProcessIdentifierRangeContext ctx) {
+ StringPath stringPath = QCase.case$.processIdentifier;
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, buildStringPredicateInRange(stringPath, leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ setElasticQuery(ctx, buildElasticQueryInRange("processIdentifier", leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitAuthorBasic(QueryLangParser.AuthorBasicContext ctx) {
+ StringPath stringPath = QCase.case$.author.id;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ setElasticQuery(ctx, buildElasticQuery("author", op.getType(), string, not));
+ }
+
+ @Override
+ public void exitAuthorList(QueryLangParser.AuthorListContext ctx) {
+ StringPath stringPath = QCase.case$.author.id;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getStringValue(node.getText()))
+ .collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ setElasticQuery(ctx, buildElasticQueryInList("author", stringList, not));
+ }
+
+ @Override
+ public void exitTransitionIdBasic(QueryLangParser.TransitionIdBasicContext ctx) {
+ StringPath stringPath = QTask.task.transitionId;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitTransitionIdList(QueryLangParser.TransitionIdListContext ctx) {
+ StringPath stringPath = QTask.task.transitionId;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitTransitionIdRange(QueryLangParser.TransitionIdRangeContext ctx) {
+ StringPath stringPath = QTask.task.transitionId;
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, buildStringPredicateInRange(stringPath, leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitStateComparison(QueryLangParser.StateComparisonContext ctx) {
+ switch (ctx.state.getType()) {
+ case QueryLangParser.ENABLED:
+ setMongoQuery(ctx, QTask.task.state.eq(State.ENABLED));
+ break;
+ case QueryLangParser.DISABLED:
+ setMongoQuery(ctx, QTask.task.state.eq(State.DISABLED));
+ break;
+ }
+ }
+
+ @Override
+ public void exitUserIdBasic(QueryLangParser.UserIdBasicContext ctx) {
+ StringPath stringPath = QTask.task.userId;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitUserIdList(QueryLangParser.UserIdListContext ctx) {
+ StringPath stringPath = QTask.task.userId;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getStringValue(node.getText()))
+ .collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitCaseIdBasic(QueryLangParser.CaseIdBasicContext ctx) {
+ StringPath stringPath = QTask.task.caseId;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitCaseIdList(QueryLangParser.CaseIdListContext ctx) {
+ StringPath stringPath = QTask.task.caseId;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getStringValue(node.getText()))
+ .collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitLaDateBasic(QueryLangParser.LaDateBasicContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastAssigned;
+ Token op = ctx.dateComparison().op;
+ boolean not = ctx.dateComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateComparison().DATE().getText());
+
+ setMongoQuery(ctx, buildDateTimePredicate(dateTimePath, op.getType(), localDateTime, not));
+ }
+
+ @Override
+ public void exitLaDateTimeBasic(QueryLangParser.LaDateTimeBasicContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastAssigned;
+ Token op = ctx.dateTimeComparison().op;
+ boolean not = ctx.dateTimeComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateTimeComparison().DATETIME().getText());
+
+ setMongoQuery(ctx, buildDateTimePredicate(dateTimePath, op.getType(), localDateTime, not));
+ }
+
+ @Override
+ public void exitLaDateList(QueryLangParser.LaDateListContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastAssigned;
+ boolean not = ctx.inListDateComparison().NOT() != null;
+ List terminalNodeList = ctx.inListDateComparison().dateList() != null ? ctx.inListDateComparison().dateList().DATE() : ctx.inListDateComparison().dateTimeList().DATETIME() ;
+ List stringDateList = terminalNodeList.stream().map(TerminalNode::getText).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildDateTimePredicateInList(dateTimePath, stringDateList, not));
+ }
+
+ @Override
+ public void exitLaDateRange(QueryLangParser.LaDateRangeContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastAssigned;
+ boolean not = ctx.inRangeDateComparison().NOT() != null;
+ boolean leftEndpointOpen;
+ boolean rightEndpointOpen;
+ LocalDateTime leftDateTime;
+ LocalDateTime rightDateTime;
+ if (ctx.inRangeDateComparison().dateRange() != null) {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(1).getText());
+ } else {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(1).getText());
+ }
+
+ setMongoQuery(ctx, buildDateTimePredicateInRange(dateTimePath, leftDateTime, leftEndpointOpen, rightDateTime, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitLfDateBasic(QueryLangParser.LfDateBasicContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastFinished;
+ Token op = ctx.dateComparison().op;
+ boolean not = ctx.dateComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateComparison().DATE().getText());
+
+ setMongoQuery(ctx, buildDateTimePredicate(dateTimePath, op.getType(), localDateTime, not));
+ }
+
+ @Override
+ public void exitLfDateTimeBasic(QueryLangParser.LfDateTimeBasicContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastFinished;
+ Token op = ctx.dateTimeComparison().op;
+ boolean not = ctx.dateTimeComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateTimeComparison().DATETIME().getText());
+
+ setMongoQuery(ctx, buildDateTimePredicate(dateTimePath, op.getType(), localDateTime, not));
+ }
+
+ @Override
+ public void exitLfDateList(QueryLangParser.LfDateListContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastFinished;
+ boolean not = ctx.inListDateComparison().NOT() != null;
+ List terminalNodeList = ctx.inListDateComparison().dateList() != null ? ctx.inListDateComparison().dateList().DATE() : ctx.inListDateComparison().dateTimeList().DATETIME() ;
+ List stringDateList = terminalNodeList.stream().map(TerminalNode::getText).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildDateTimePredicateInList(dateTimePath, stringDateList, not));
+ }
+
+ @Override
+ public void exitLfDateRange(QueryLangParser.LfDateRangeContext ctx) {
+ DateTimePath dateTimePath = QTask.task.lastFinished;
+ boolean not = ctx.inRangeDateComparison().NOT() != null;
+ boolean leftEndpointOpen;
+ boolean rightEndpointOpen;
+ LocalDateTime leftDateTime;
+ LocalDateTime rightDateTime;
+ if (ctx.inRangeDateComparison().dateRange() != null) {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(1).getText());
+ } else {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(1).getText());
+ }
+
+ setMongoQuery(ctx, buildDateTimePredicateInRange(dateTimePath, leftDateTime, leftEndpointOpen, rightDateTime, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitNameBasic(QueryLangParser.NameBasicContext ctx) {
+ StringPath stringPath = QUser.user.name;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitNameList(QueryLangParser.NameListContext ctx) {
+ StringPath stringPath = QUser.user.name;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitNameRange(QueryLangParser.NameRangeContext ctx) {
+ StringPath stringPath = QUser.user.name;
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, buildStringPredicateInRange(stringPath, leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitSurnameBasic(QueryLangParser.SurnameBasicContext ctx) {
+ StringPath stringPath = QUser.user.surname;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitSurnameList(QueryLangParser.SurnameListContext ctx) {
+ StringPath stringPath = QUser.user.surname;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitSurnameRange(QueryLangParser.SurnameRangeContext ctx) {
+ StringPath stringPath = QUser.user.surname;
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, buildStringPredicateInRange(stringPath, leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitEmailBasic(QueryLangParser.EmailBasicContext ctx) {
+ StringPath stringPath = QUser.user.email;
+ Token op = ctx.stringComparison().op;
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, buildStringPredicate(stringPath, op.getType(), string, not));
+ }
+
+ @Override
+ public void exitEmailList(QueryLangParser.EmailListContext ctx) {
+ StringPath stringPath = QUser.user.email;
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, buildStringPredicateInList(stringPath, stringList, not));
+ }
+
+ @Override
+ public void exitEmailRange(QueryLangParser.EmailRangeContext ctx) {
+ StringPath stringPath = QUser.user.email;
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, buildStringPredicateInRange(stringPath, leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ }
+
+ @Override
+ public void exitDataString(QueryLangParser.DataStringContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ Token op = ctx.stringComparison().op;
+ checkOp(ComparisonType.STRING, op);
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("dataSet." + fieldId + ".textValue", op.getType(), string, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataStringList(QueryLangParser.DataStringListContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInList("dataSet." + fieldId + ".textValue", stringList, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataStringRange(QueryLangParser.DataStringRangeContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInRange("dataSet." + fieldId + ".textValue", leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataNumber(QueryLangParser.DataNumberContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ Token op = ctx.numberComparison().op;
+ checkOp(ComparisonType.NUMBER, op);
+ boolean not = ctx.numberComparison().NOT() != null;
+ String number = ctx.numberComparison().number.getText();
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("dataSet." + fieldId + ".numberValue", op.getType(), number, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataNumberList(QueryLangParser.DataNumberListContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ boolean not = ctx.inListNumberComparison().NOT() != null;
+ List terminalNodeList = ctx.inListNumberComparison().intList() != null ? ctx.inListNumberComparison().intList().INT() : ctx.inListNumberComparison().doubleList().DOUBLE();
+ List stringNumberList = terminalNodeList.stream().map(TerminalNode::getText).collect(Collectors.toList());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInList("dataSet." + fieldId + ".numberValue", stringNumberList, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataNumberRange(QueryLangParser.DataNumberRangeContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ boolean not = ctx.inRangeNumberComparison().NOT() != null;
+ boolean leftEndpointOpen;
+ boolean rightEndpointOpen;
+ String leftNumberAsString;
+ String rightNumberAsString;
+ if (ctx.inRangeNumberComparison().intRange() != null) {
+ leftEndpointOpen = ctx.inRangeNumberComparison().intRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeNumberComparison().intRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftNumberAsString = ctx.inRangeNumberComparison().intRange().INT(0).getText();
+ rightNumberAsString = ctx.inRangeNumberComparison().intRange().INT(1).getText();
+ } else {
+ leftEndpointOpen = ctx.inRangeNumberComparison().doubleRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeNumberComparison().doubleRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftNumberAsString = ctx.inRangeNumberComparison().doubleRange().DOUBLE(0).getText();
+ rightNumberAsString = ctx.inRangeNumberComparison().doubleRange().DOUBLE(1).getText();
+ }
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInRange("dataSet." + fieldId + ".numberValue", leftNumberAsString, leftEndpointOpen, rightNumberAsString, rightEndpointOpen, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDate(QueryLangParser.DataDateContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ Token op = ctx.dateComparison().op;
+ checkOp(ComparisonType.DATE, op);
+ boolean not = ctx.dateComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateComparison().DATE().getText());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("dataSet." + fieldId + ".timestampValue", op.getType(), String.valueOf(Timestamp.valueOf(localDateTime).getTime()), not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDatetime(QueryLangParser.DataDatetimeContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ Token op = ctx.dateTimeComparison().op;
+ checkOp(ComparisonType.DATETIME, op);
+ boolean not = ctx.dateTimeComparison().NOT() != null;
+ LocalDateTime localDateTime = toDateTime(ctx.dateTimeComparison().DATETIME().getText());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("dataSet." + fieldId + ".timestampValue", op.getType(), String.valueOf(Timestamp.valueOf(localDateTime).getTime()), not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDateList(QueryLangParser.DataDateListContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ boolean not = ctx.inListDateComparison().NOT() != null;
+ List terminalNodeList = ctx.inListDateComparison().dateList() != null ? ctx.inListDateComparison().dateList().DATE() : ctx.inListDateComparison().dateTimeList().DATETIME();
+ List stringNumberList = terminalNodeList.stream().map(TerminalNode::getText).map(dateAsString -> {
+ LocalDateTime localDateTime = toDateTime(dateAsString);
+ return String.valueOf(Timestamp.valueOf(localDateTime).getTime());
+ }).collect(Collectors.toList());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInList("dataSet." + fieldId + ".timestampValue", stringNumberList, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDateRange(QueryLangParser.DataDateRangeContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ boolean not = ctx.inRangeDateComparison().NOT() != null;
+ boolean leftEndpointOpen;
+ boolean rightEndpointOpen;
+ LocalDateTime leftDateTime;
+ LocalDateTime rightDateTime;
+ if (ctx.inRangeDateComparison().dateRange() != null) {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateRange().DATE(1).getText());
+ } else {
+ leftEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeDateComparison().dateTimeRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(0).getText());
+ rightDateTime = toDateTime(ctx.inRangeDateComparison().dateTimeRange().DATETIME(1).getText());
+ }
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInRange("dataSet." + fieldId + ".timestampValue", String.valueOf(Timestamp.valueOf(leftDateTime).getTime()), leftEndpointOpen, String.valueOf(Timestamp.valueOf(rightDateTime).getTime()), rightEndpointOpen, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataBoolean(QueryLangParser.DataBooleanContext ctx) {
+ String fieldId = ctx.dataValue().fieldId.getText();
+ Token op = ctx.booleanComparison().op;
+ checkOp(ComparisonType.BOOLEAN, op);
+ boolean not = ctx.booleanComparison().NOT() != null;
+ String booleanValue = ctx.booleanComparison().BOOLEAN().getText();
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("dataSet." + fieldId + ".booleanValue", op.getType(), booleanValue, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataOptionsBasic(QueryLangParser.DataOptionsBasicContext ctx) {
+ String fieldId = ctx.dataOptions().fieldId.getText();
+ Token op = ctx.stringComparison().op;
+ checkOp(ComparisonType.STRING, op);
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("dataSet." + fieldId + ".options", op.getType(), string, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataOptionsList(QueryLangParser.DataOptionsListContext ctx) {
+ String fieldId = ctx.dataOptions().fieldId.getText();
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream().map(node -> getStringValue(node.getText())).collect(Collectors.toList());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInList("dataSet." + fieldId + ".options", stringList, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataOptionsRange(QueryLangParser.DataOptionsRangeContext ctx) {
+ String fieldId = ctx.dataOptions().fieldId.getText();
+ boolean not = ctx.inRangeStringComparison().NOT() != null;
+ boolean leftEndpointOpen = ctx.inRangeStringComparison().stringRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ boolean rightEndpointOpen = ctx.inRangeStringComparison().stringRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ String leftString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(0).getText());
+ String rightString = getStringValue(ctx.inRangeStringComparison().stringRange().STRING(1).getText());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInRange("dataSet." + fieldId + ".options", leftString, leftEndpointOpen, rightString, rightEndpointOpen, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitPlacesBasic(QueryLangParser.PlacesBasicContext ctx) {
+ String placeId = ctx.places().placeId.getText();
+ Token op = ctx.numberComparison().op;
+ checkOp(ComparisonType.NUMBER, op);
+ boolean not = ctx.numberComparison().NOT() != null;
+ String numberValue = ctx.numberComparison().number.getText();
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("places." + placeId + ".marking", op.getType(), numberValue, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitPlacesList(QueryLangParser.PlacesListContext ctx) {
+ String placeId = ctx.places().placeId.getText();
+ boolean not = ctx.inListNumberComparison().NOT() != null;
+ List terminalNodeList = ctx.inListNumberComparison().intList() != null ? ctx.inListNumberComparison().intList().INT() : ctx.inListNumberComparison().doubleList().DOUBLE();
+ List stringNumberList = terminalNodeList.stream().map(TerminalNode::getText).collect(Collectors.toList());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInList("places." + placeId + ".marking", stringNumberList, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitPlacesRange(QueryLangParser.PlacesRangeContext ctx) {
+ String placeId = ctx.places().placeId.getText();
+ boolean not = ctx.inRangeNumberComparison().NOT() != null;
+ boolean leftEndpointOpen;
+ boolean rightEndpointOpen;
+ String leftNumberAsString;
+ String rightNumberAsString;
+ if (ctx.inRangeNumberComparison().intRange() != null) {
+ leftEndpointOpen = ctx.inRangeNumberComparison().intRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeNumberComparison().intRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftNumberAsString = ctx.inRangeNumberComparison().intRange().INT(0).getText();
+ rightNumberAsString = ctx.inRangeNumberComparison().intRange().INT(1).getText();
+ } else {
+ leftEndpointOpen = ctx.inRangeNumberComparison().doubleRange().leftEndpoint.getText().equals(LEFT_OPEN_ENDPOINT);
+ rightEndpointOpen = ctx.inRangeNumberComparison().doubleRange().rightEndpoint.getText().equals(RIGHT_OPEN_ENDPOINT);
+ leftNumberAsString = ctx.inRangeNumberComparison().doubleRange().DOUBLE(0).getText();
+ rightNumberAsString = ctx.inRangeNumberComparison().doubleRange().DOUBLE(1).getText();
+ }
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInRange("places." + placeId + ".marking", leftNumberAsString, leftEndpointOpen, rightNumberAsString, rightEndpointOpen, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitTasksStateComparison(QueryLangParser.TasksStateComparisonContext ctx) {
+ String taskId = ctx.tasksState().taskId.getText();
+ Token op = ctx.op;
+ checkOp(ComparisonType.STRING, op);
+ boolean not = ctx.NOT() != null;
+ State state = ctx.state.getType() == QueryLangParser.ENABLED ? State.ENABLED : State.DISABLED;
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("tasks." + taskId + ".state", op.getType(), state.toString(), not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitTasksUserIdBasic(QueryLangParser.TasksUserIdBasicContext ctx) {
+ String taskId = ctx.tasksUserId().taskId.getText();
+ Token op = ctx.stringComparison().op;
+ checkOp(ComparisonType.STRING, op);
+ boolean not = ctx.stringComparison().NOT() != null;
+ String string = getStringValue(ctx.stringComparison().STRING().getText());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQuery("tasks." + taskId + ".userId", op.getType(), string, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitTasksUserIdList(QueryLangParser.TasksUserIdListContext ctx) {
+ String taskId = ctx.tasksUserId().taskId.getText();
+ boolean not = ctx.inListStringComparison().NOT() != null;
+ List stringList = ctx.inListStringComparison().stringList().STRING().stream()
+ .map(node -> getStringValue(node.getText()))
+ .collect(Collectors.toList());
+
+ setMongoQuery(ctx, null);
+ setElasticQuery(ctx, buildElasticQueryInList("tasks." + taskId + ".userId", stringList, not));
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitPaging(QueryLangParser.PagingContext ctx) {
+ pageNumber = Integer.parseInt(ctx.pageNum.getText());
+
+ if (ctx.pageSize != null) {
+ pageSize = Integer.parseInt(ctx.pageSize.getText());
+ }
+ }
+
+ @Override
+ public void exitCaseSorting(QueryLangParser.CaseSortingContext ctx) {
+ ctx.caseAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop;
+ if (searchWithElastic) {
+ // todo NAE-1997: sorting by data value, options
+ if (attrOrd.caseAttribute().places() != null) {
+ prop = "places." + attrOrd.caseAttribute().places().placeId.getText() + ".marking";
+ } else if (attrOrd.caseAttribute().tasksState() != null) {
+ prop = "tasks." + attrOrd.caseAttribute().tasksState().taskId.getText() + ".state.keyword";
+ } else if (attrOrd.caseAttribute().tasksUserId() != null) {
+ prop = "tasks." + attrOrd.caseAttribute().tasksUserId().taskId.getText() + ".userId.keyword";
+ } else {
+ prop = caseAttrToSortPropElasticMapping.get(attrOrd.caseAttribute().getText().toLowerCase());
+ }
+ } else {
+ prop = caseAttrToSortPropMapping.get(attrOrd.caseAttribute().getText().toLowerCase());
+ }
+
+ if (prop == null) {
+ return;
+ }
+ sortOrders.add(new Sort.Order(dir, prop));
+ });
+ }
+
+ @Override
+ public void exitProcessSorting(QueryLangParser.ProcessSortingContext ctx) {
+ ctx.processAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop = processAttrToSortPropMapping.get(attrOrd.processAttribute().getText().toLowerCase());
+ if (prop == null) {
+ return;
+ }
+ sortOrders.add(new Sort.Order(dir, prop));
+ });
+ }
+
+ @Override
+ public void exitTaskSorting(QueryLangParser.TaskSortingContext ctx) {
+ ctx.taskAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop = taskAttrToSortPropMapping.get(attrOrd.taskAttribute().getText().toLowerCase());
+ if (prop == null) {
+ return;
+ }
+ sortOrders.add(new Sort.Order(dir, prop));
+ });
+ }
+
+ @Override
+ public void exitUserSorting(QueryLangParser.UserSortingContext ctx) {
+ ctx.userAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop = userAttrToSortPropMapping.get(attrOrd.userAttribute().getText().toLowerCase());
+ if (prop == null) {
+ return;
+ }
+ sortOrders.add(new Sort.Order(dir, prop));
+ });
+ }
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/QueryLangExplainEvaluator.java b/src/main/java/com/netgrif/application/engine/search/QueryLangExplainEvaluator.java
new file mode 100644
index 00000000000..c02f6cc586b
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/QueryLangExplainEvaluator.java
@@ -0,0 +1,505 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.search.antlr4.QueryLangBaseListener;
+import com.netgrif.application.engine.search.antlr4.QueryLangParser;
+import com.netgrif.application.engine.search.enums.QueryType;
+import com.netgrif.application.engine.search.utils.QueryLangTreeNode;
+import lombok.Getter;
+import org.antlr.v4.runtime.tree.ErrorNodeImpl;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.ParseTreeProperty;
+import org.springframework.data.domain.Sort;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.netgrif.application.engine.search.utils.SearchUtils.*;
+
+public class QueryLangExplainEvaluator extends QueryLangBaseListener {
+
+ private final ParseTreeProperty node = new ParseTreeProperty<>();
+ @Getter
+ private QueryLangTreeNode root = null;
+
+ @Getter
+ private QueryType type;
+ @Getter
+ private boolean multiple;
+ @Getter
+ private boolean searchWithElastic = false;
+
+ private int pageNumber = 0;
+ private int pageSize = 20;
+ private final List sortOrders = new ArrayList<>();
+
+ public String explain() {
+ StringBuilder result = new StringBuilder("Searching ");
+
+ result.append(multiple ? "multiple instances" : "single instance")
+ .append(" of resource ")
+ .append(type != null ? type.name() : "invalid type")
+ .append(" with ")
+ .append(searchWithElastic ? "Elasticsearch" : "MongoDB")
+ .append(".\n")
+ .append("page number: ").append(pageNumber)
+ .append(", page size: ").append(pageSize)
+ .append("\n");
+
+ if (!sortOrders.isEmpty()) {
+ result.append("sort by ").append(String.join(";", sortOrders)).append("\n");
+ }
+
+ result.append(root != null ? root.toString() : "Tree visualisation not available.");
+
+ return result.toString();
+ }
+
+ public void setQueryLangTreeNode(ParseTree node, QueryLangTreeNode queryLangTreeNode) {
+ if (queryLangTreeNode == null) {
+ queryLangTreeNode = new QueryLangTreeNode("error: " + node.getText());
+ }
+ this.node.put(node, queryLangTreeNode);
+ }
+
+ public QueryLangTreeNode getQueryLangTreeNode(ParseTree node) {
+ return this.node.get(node);
+ }
+
+ private static QueryLangTreeNode createTreeNode(String name, List children, List errors) {
+ List combinedChildren = Stream.concat(children.stream(), errors.stream()).collect(Collectors.toList());
+ return new QueryLangTreeNode(name, combinedChildren);
+ }
+
+ private QueryLangTreeNode getErrorFromNode(ParseTree node) {
+ if (node instanceof ErrorNodeImpl) {
+ String errorMsg = "error: " + ((ErrorNodeImpl) node).symbol.getText();
+ return new QueryLangTreeNode(errorMsg);
+ }
+ return null;
+ }
+
+ private List getErrorsFromChildren(List children) {
+ List errors = new ArrayList<>();
+ children.forEach(child -> {
+ if (child instanceof ErrorNodeImpl) {
+ errors.add(getErrorFromNode(child));
+ }
+ });
+ return errors;
+ }
+
+ private List getErrorsRecursive(ParseTree node) {
+ List errors = new ArrayList<>();
+ if (node.getChildCount() == 0) {
+ if (node instanceof ErrorNodeImpl) {
+ errors.add(getErrorFromNode(node));
+ }
+ return errors;
+ }
+
+ int numChildren = node.getChildCount();
+ for (int i = 0; i < numChildren; i++) {
+ errors.addAll(getErrorsRecursive(node.getChild(i)));
+ }
+ return errors;
+ }
+
+ private void processComplexExpression(String nodeName, List children, ParseTree current) {
+ if (children.size() == 1) {
+ setQueryLangTreeNode(current, getQueryLangTreeNode(children.get(0)));
+ return;
+ }
+ List errorNodes = getErrorsFromChildren(children);
+
+ List childrenNodes = children.stream()
+ .map(child -> {
+ QueryLangTreeNode node = getQueryLangTreeNode(child);
+ if (node == null) {
+ errorNodes.addAll(getErrorsRecursive(child));
+ }
+ return node;
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+
+ setQueryLangTreeNode(current, createTreeNode(nodeName, childrenNodes, errorNodes));
+ }
+
+ @Override
+ public void enterProcessQuery(QueryLangParser.ProcessQueryContext ctx) {
+ type = QueryType.PROCESS;
+ multiple = ctx.resource.getType() == QueryLangParser.PROCESSES;
+ }
+
+ @Override
+ public void exitProcessQuery(QueryLangParser.ProcessQueryContext ctx) {
+ root = createTreeNode("process query", List.of(getQueryLangTreeNode(ctx.processConditions())), getErrorsFromChildren(ctx.children));
+ }
+
+ @Override
+ public void enterCaseQuery(QueryLangParser.CaseQueryContext ctx) {
+ type = QueryType.CASE;
+ multiple = ctx.resource.getType() == QueryLangParser.CASES;
+ }
+
+ @Override
+ public void exitCaseQuery(QueryLangParser.CaseQueryContext ctx) {
+ QueryLangTreeNode childTreeNode = getQueryLangTreeNode(ctx.caseConditions());
+ root = createTreeNode("case query", List.of(childTreeNode), getErrorsFromChildren(ctx.children));
+ }
+
+ @Override
+ public void enterTaskQuery(QueryLangParser.TaskQueryContext ctx) {
+ type = QueryType.TASK;
+ multiple = ctx.resource.getType() == QueryLangParser.TASKS;
+ }
+
+ @Override
+ public void exitTaskQuery(QueryLangParser.TaskQueryContext ctx) {
+ root = createTreeNode("task query", List.of(getQueryLangTreeNode(ctx.taskConditions())), getErrorsFromChildren(ctx.children));
+ }
+
+ @Override
+ public void enterUserQuery(QueryLangParser.UserQueryContext ctx) {
+ type = QueryType.USER;
+ multiple = ctx.resource.getType() == QueryLangParser.USERS;
+ }
+
+ @Override
+ public void exitUserQuery(QueryLangParser.UserQueryContext ctx) {
+ root = createTreeNode("user query", List.of(getQueryLangTreeNode(ctx.userConditions())), getErrorsFromChildren(ctx.children));
+ }
+
+ @Override
+ public void exitProcessConditions(QueryLangParser.ProcessConditionsContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.processOrExpression()));
+ }
+
+ @Override
+ public void exitProcessOrExpression(QueryLangParser.ProcessOrExpressionContext ctx) {
+ List children = ctx.processAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processComplexExpression("OR", children, ctx);
+ }
+
+ @Override
+ public void exitProcessAndExpression(QueryLangParser.ProcessAndExpressionContext ctx) {
+ List children = ctx.processConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processComplexExpression("AND", children, ctx);
+ }
+
+ @Override
+ public void exitProcessConditionGroupBasic(QueryLangParser.ProcessConditionGroupBasicContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.processCondition()));
+ }
+
+ @Override
+ public void exitProcessConditionGroupParenthesis(QueryLangParser.ProcessConditionGroupParenthesisContext ctx) {
+ setQueryLangTreeNode(ctx, createTreeNode("()", List.of(getQueryLangTreeNode(ctx.processConditions())), getErrorsFromChildren(ctx.children)));
+ }
+
+ @Override
+ public void exitCaseConditions(QueryLangParser.CaseConditionsContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.caseOrExpression()));
+ }
+
+ @Override
+ public void exitCaseOrExpression(QueryLangParser.CaseOrExpressionContext ctx) {
+ List children = ctx.caseAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processComplexExpression("OR", children, ctx);
+ }
+
+ @Override
+ public void exitCaseAndExpression(QueryLangParser.CaseAndExpressionContext ctx) {
+ List children = ctx.caseConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processComplexExpression("AND", children, ctx);
+ }
+
+ @Override
+ public void exitCaseConditionGroupBasic(QueryLangParser.CaseConditionGroupBasicContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.caseCondition()));
+ }
+
+
+ @Override
+ public void exitCaseConditionGroupParenthesis(QueryLangParser.CaseConditionGroupParenthesisContext ctx) {
+ setQueryLangTreeNode(ctx, createTreeNode("()", List.of(getQueryLangTreeNode(ctx.caseConditions())), getErrorsFromChildren(ctx.children)));
+ }
+
+ @Override
+ public void exitTaskConditions(QueryLangParser.TaskConditionsContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.taskOrExpression()));
+ }
+
+ @Override
+ public void exitTaskOrExpression(QueryLangParser.TaskOrExpressionContext ctx) {
+ List children = ctx.taskAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processComplexExpression("OR", children, ctx);
+ }
+
+ @Override
+ public void exitTaskAndExpression(QueryLangParser.TaskAndExpressionContext ctx) {
+ List children = ctx.taskConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processComplexExpression("AND", children, ctx);
+ }
+
+ @Override
+ public void exitTaskConditionGroupBasic(QueryLangParser.TaskConditionGroupBasicContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.taskCondition()));
+ }
+
+ @Override
+ public void exitTaskConditionGroupParenthesis(QueryLangParser.TaskConditionGroupParenthesisContext ctx) {
+ setQueryLangTreeNode(ctx, createTreeNode("()", List.of(getQueryLangTreeNode(ctx.taskConditions())), getErrorsFromChildren(ctx.children)));
+ }
+
+ @Override
+ public void exitUserConditions(QueryLangParser.UserConditionsContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.userOrExpression()));
+ }
+
+ @Override
+ public void exitUserOrExpression(QueryLangParser.UserOrExpressionContext ctx) {
+ List children = ctx.userAndExpression().stream()
+ .map(andExpression -> (ParseTree) andExpression)
+ .collect(Collectors.toList());
+
+ processComplexExpression("OR", children, ctx);
+ }
+
+ @Override
+ public void exitUserAndExpression(QueryLangParser.UserAndExpressionContext ctx) {
+ List children = ctx.userConditionGroup().stream()
+ .map(conditionGroup -> (ParseTree) conditionGroup)
+ .collect(Collectors.toList());
+
+ processComplexExpression("AND", children, ctx);
+ }
+
+ @Override
+ public void exitUserConditionGroupBasic(QueryLangParser.UserConditionGroupBasicContext ctx) {
+ setQueryLangTreeNode(ctx, getQueryLangTreeNode(ctx.userCondition()));
+ }
+
+ @Override
+ public void exitUserConditionGroupParenthesis(QueryLangParser.UserConditionGroupParenthesisContext ctx) {
+ setQueryLangTreeNode(ctx, createTreeNode("()", List.of(getQueryLangTreeNode(ctx.userConditions())), getErrorsFromChildren(ctx.children)));
+ }
+
+ @Override
+ public void exitProcessCondition(QueryLangParser.ProcessConditionContext ctx) {
+ List errors = getErrorsRecursive(ctx);
+ setQueryLangTreeNode(ctx, new QueryLangTreeNode(ctx.getText(), errors));
+ }
+
+ @Override
+ public void exitCaseCondition(QueryLangParser.CaseConditionContext ctx) {
+ List errors = getErrorsRecursive(ctx);
+ setQueryLangTreeNode(ctx, new QueryLangTreeNode(ctx.getText(), errors));
+ }
+
+ @Override
+ public void exitTaskCondition(QueryLangParser.TaskConditionContext ctx) {
+ List errors = getErrorsRecursive(ctx);
+ setQueryLangTreeNode(ctx, new QueryLangTreeNode(ctx.getText(), errors));
+ }
+
+ @Override
+ public void exitUserCondition(QueryLangParser.UserConditionContext ctx) {
+ List errors = getErrorsRecursive(ctx);
+ setQueryLangTreeNode(ctx, new QueryLangTreeNode(ctx.getText(), errors));
+ }
+
+ @Override
+ public void exitPlacesBasic(QueryLangParser.PlacesBasicContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void enterPlacesList(QueryLangParser.PlacesListContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void enterPlacesRange(QueryLangParser.PlacesRangeContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitTasksStateComparison(QueryLangParser.TasksStateComparisonContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitTasksUserIdBasic(QueryLangParser.TasksUserIdBasicContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitTasksUserIdList(QueryLangParser.TasksUserIdListContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataString(QueryLangParser.DataStringContext ctx) {
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataNumber(QueryLangParser.DataNumberContext ctx) {
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDate(QueryLangParser.DataDateContext ctx) {
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDatetime(QueryLangParser.DataDatetimeContext ctx) {
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataBoolean(QueryLangParser.DataBooleanContext ctx) {
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDateList(QueryLangParser.DataDateListContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataDateRange(QueryLangParser.DataDateRangeContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataNumberList(QueryLangParser.DataNumberListContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataNumberRange(QueryLangParser.DataNumberRangeContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataStringList(QueryLangParser.DataStringListContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataStringRange(QueryLangParser.DataStringRangeContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataOptionsBasic(QueryLangParser.DataOptionsBasicContext ctx) {
+ this.searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataOptionsList(QueryLangParser.DataOptionsListContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitDataOptionsRange(QueryLangParser.DataOptionsRangeContext ctx) {
+ searchWithElastic = true;
+ }
+
+ @Override
+ public void exitPaging(QueryLangParser.PagingContext ctx) {
+ pageNumber = Integer.parseInt(ctx.pageNum.getText());
+
+ if (ctx.pageSize != null) {
+ pageSize = Integer.parseInt(ctx.pageSize.getText());
+ }
+ }
+
+ @Override
+ public void exitCaseSorting(QueryLangParser.CaseSortingContext ctx) {
+ ctx.caseAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop;
+ if (searchWithElastic) {
+ // todo NAE-1997: sorting by data value, options
+ if (attrOrd.caseAttribute().places() != null) {
+ prop = "places." + attrOrd.caseAttribute().places().placeId.getText() + ".marking";
+ } else if (attrOrd.caseAttribute().tasksState() != null) {
+ prop = "tasks." + attrOrd.caseAttribute().tasksState().taskId.getText() + ".state.keyword";
+ } else if (attrOrd.caseAttribute().tasksUserId() != null) {
+ prop = "tasks." + attrOrd.caseAttribute().tasksUserId().taskId.getText() + ".userId.keyword";
+ } else {
+ prop = caseAttrToSortPropElasticMapping.get(attrOrd.caseAttribute().getText().toLowerCase());
+ }
+ } else {
+ prop = caseAttrToSortPropMapping.get(attrOrd.caseAttribute().getText().toLowerCase());
+ }
+
+ if (prop == null) {
+ sortOrders.add("Invalid attribute: " + attrOrd.caseAttribute().getText());
+ }
+ sortOrders.add("attribute: " + prop + ", ordering: " + dir);
+ });
+ }
+
+ @Override
+ public void exitProcessSorting(QueryLangParser.ProcessSortingContext ctx) {
+ ctx.processAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop = processAttrToSortPropMapping.get(attrOrd.processAttribute().getText().toLowerCase());
+ if (prop == null) {
+ sortOrders.add("Invalid attribute: " + attrOrd.processAttribute().getText());
+ }
+ sortOrders.add("attribute: " + prop + ", ordering: " + dir);
+ });
+ }
+
+ @Override
+ public void exitTaskSorting(QueryLangParser.TaskSortingContext ctx) {
+ ctx.taskAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop = taskAttrToSortPropMapping.get(attrOrd.taskAttribute().getText().toLowerCase());
+ if (prop == null) {
+ sortOrders.add("Invalid attribute: " + attrOrd.taskAttribute().getText());
+ }
+ sortOrders.add("attribute: " + prop + ", ordering: " + dir);
+ });
+ }
+
+ @Override
+ public void exitUserSorting(QueryLangParser.UserSortingContext ctx) {
+ ctx.userAttributeOrdering().forEach(attrOrd -> {
+ Sort.Direction dir = attrOrd.ordering != null ? Sort.Direction.fromString(attrOrd.ordering.getText()) : Sort.Direction.ASC;
+ String prop = userAttrToSortPropMapping.get(attrOrd.userAttribute().getText().toLowerCase());
+ if (prop == null) {
+ sortOrders.add("Invalid attribute: " + attrOrd.userAttribute().getText());
+ }
+ sortOrders.add("attribute: " + prop + ", ordering: " + dir);
+ });
+ }
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/SearchService.java b/src/main/java/com/netgrif/application/engine/search/SearchService.java
new file mode 100644
index 00000000000..f78ee099b06
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/SearchService.java
@@ -0,0 +1,158 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.auth.domain.repositories.UserRepository;
+import com.netgrif.application.engine.auth.service.interfaces.IUserService;
+import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService;
+import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest;
+import com.netgrif.application.engine.petrinet.domain.repositories.PetriNetRepository;
+import com.netgrif.application.engine.search.interfaces.ISearchService;
+import com.netgrif.application.engine.search.utils.SearchUtils;
+import com.netgrif.application.engine.workflow.domain.Case;
+import com.netgrif.application.engine.workflow.domain.repositories.CaseRepository;
+import com.netgrif.application.engine.workflow.domain.repositories.TaskRepository;
+import com.querydsl.core.types.Predicate;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+import static com.netgrif.application.engine.search.utils.SearchUtils.evaluateQuery;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SearchService implements ISearchService {
+
+ private final PetriNetRepository petriNetRepository;
+
+ private final IElasticCaseService elasticCaseService;
+
+ private final CaseRepository caseRepository;
+
+ private final TaskRepository taskRepository;
+
+ private final UserRepository userRepository;
+
+ private final IUserService userService;
+
+ private Long countCasesElastic(String elasticQuery) {
+ CaseSearchRequest caseSearchRequest = new CaseSearchRequest();
+ caseSearchRequest.query = elasticQuery;
+ return elasticCaseService.count(
+ List.of(caseSearchRequest),
+ userService.getLoggedOrSystem().transformToLoggedUser(),
+ LocaleContextHolder.getLocale(),
+ false
+ );
+ }
+
+ private List findCasesElastic(String elasticQuery, Pageable pageable) {
+ CaseSearchRequest caseSearchRequest = new CaseSearchRequest();
+ caseSearchRequest.query = elasticQuery;
+ return elasticCaseService.search(
+ List.of(caseSearchRequest),
+ userService.getLoggedOrSystem().transformToLoggedUser(),
+ pageable,
+ LocaleContextHolder.getLocale(),
+ false
+ ).getContent();
+ }
+
+ private boolean existsCasesElastic(String elasticQuery) {
+ return countCasesElastic(elasticQuery) > 0;
+ }
+
+ @Override
+ public String explainQuery(String input) {
+ return SearchUtils.explainQuery(input);
+ }
+
+ @Override
+ public Object search(String input) {
+ QueryLangEvaluator evaluator = evaluateQuery(input);
+ Predicate predicate = evaluator.getFullMongoQuery();
+ String elasticQuery = evaluator.getFullElasticQuery();
+ Pageable pageable = evaluator.getPageable();
+
+ switch (evaluator.getType()) {
+ case PROCESS:
+ if (evaluator.getMultiple()) {
+ return petriNetRepository.findAll(predicate, pageable).getContent();
+ }
+ return petriNetRepository.findAll(predicate, PageRequest.of(0, 1))
+ .getContent().stream().findFirst().orElse(null);
+ case CASE:
+ if (!evaluator.getSearchWithElastic()) {
+ if (evaluator.getMultiple()) {
+ return caseRepository.findAll(predicate, pageable).getContent();
+ }
+ return caseRepository.findAll(predicate, PageRequest.of(0, 1))
+ .getContent().stream().findFirst().orElse(null);
+ }
+
+ List cases = findCasesElastic(elasticQuery, pageable);
+ return evaluator.getMultiple() ? cases : cases.stream().findFirst().orElse(null);
+ case TASK:
+ if (evaluator.getMultiple()) {
+ return taskRepository.findAll(predicate, pageable).getContent();
+ }
+ return taskRepository.findAll(predicate, PageRequest.of(0, 1))
+ .getContent().stream().findFirst().orElse(null);
+ case USER:
+ if (evaluator.getMultiple()) {
+ return userRepository.findAll(predicate, pageable).getContent();
+ }
+ return userRepository.findAll(predicate, PageRequest.of(0, 1))
+ .getContent().stream().findFirst().orElse(null);
+ }
+ return null;
+ }
+
+ @Override
+ public Long count(String input) {
+ QueryLangEvaluator evaluator = evaluateQuery(input);
+ Predicate predicate = evaluator.getFullMongoQuery();
+ String elasticQuery = evaluator.getFullElasticQuery();
+
+ switch (evaluator.getType()) {
+ case PROCESS:
+ return petriNetRepository.count(predicate);
+ case CASE:
+ if (!evaluator.getSearchWithElastic()) {
+ return caseRepository.count(predicate);
+ }
+ return countCasesElastic(elasticQuery);
+ case TASK:
+ return taskRepository.count(predicate);
+ case USER:
+ return userRepository.count(predicate);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean exists(String input) {
+ QueryLangEvaluator evaluator = evaluateQuery(input);
+ Predicate predicate = evaluator.getFullMongoQuery();
+ String elasticQuery = evaluator.getFullElasticQuery();
+
+ switch (evaluator.getType()) {
+ case PROCESS:
+ return petriNetRepository.exists(predicate);
+ case CASE:
+ if (!evaluator.getSearchWithElastic()) {
+ return caseRepository.exists(predicate);
+ }
+ return existsCasesElastic(elasticQuery);
+ case TASK:
+ return taskRepository.exists(predicate);
+ case USER:
+ return userRepository.exists(predicate);
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/antlr4/QueryLang.g4 b/src/main/java/com/netgrif/application/engine/search/antlr4/QueryLang.g4
new file mode 100644
index 00000000000..88c455c6c03
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/antlr4/QueryLang.g4
@@ -0,0 +1,360 @@
+// todo NAE-1997: generate this with plugin
+grammar QueryLang;
+
+query: resource=(PROCESS | PROCESSES) delimeter processConditions (paging)? (processSorting)? EOF # processQuery
+ | resource=(CASE | CASES) delimeter caseConditions (paging)? (caseSorting)? EOF # caseQuery
+ | resource=(TASK | TASKS) delimeter taskConditions (paging)? (taskSorting)? EOF # taskQuery
+ | resource=(USER | USERS) delimeter userConditions (paging)? (userSorting)? EOF # userQuery
+ ;
+
+processConditions: processOrExpression ;
+processOrExpression: processAndExpression (SPACE OR SPACE processAndExpression)* ;
+processAndExpression: processConditionGroup (SPACE AND SPACE processConditionGroup)* ;
+processConditionGroup: processCondition # processConditionGroupBasic
+ | (NOT SPACE?)? '(' SPACE? processConditions SPACE? ')' SPACE? # processConditionGroupParenthesis
+ ;
+processCondition: processComparisons SPACE? ;
+
+caseConditions: caseOrExpression ;
+caseOrExpression: caseAndExpression (SPACE OR SPACE caseAndExpression)* ;
+caseAndExpression: caseConditionGroup (SPACE AND SPACE caseConditionGroup)* ;
+caseConditionGroup: caseCondition # caseConditionGroupBasic
+ | (NOT SPACE?)? '(' SPACE? caseConditions SPACE? ')' SPACE? # caseConditionGroupParenthesis
+ ;
+caseCondition: caseComparisons SPACE? ;
+
+taskConditions: taskOrExpression ;
+taskOrExpression: taskAndExpression (SPACE OR SPACE taskAndExpression)* ;
+taskAndExpression: taskConditionGroup (SPACE AND SPACE taskConditionGroup)* ;
+taskConditionGroup: taskCondition # taskConditionGroupBasic
+ | (NOT SPACE?)? '(' SPACE? taskConditions SPACE? ')' SPACE? # taskConditionGroupParenthesis
+ ;
+taskCondition: taskComparisons SPACE? ;
+
+userConditions: userOrExpression ;
+userOrExpression: userAndExpression (SPACE OR SPACE userAndExpression)* ;
+userAndExpression: userConditionGroup (SPACE AND SPACE userConditionGroup)* ;
+userConditionGroup: userCondition # userConditionGroupBasic
+ | (NOT SPACE?)? '(' SPACE? userConditions SPACE? ')' SPACE? # userConditionGroupParenthesis
+ ;
+userCondition: userComparisons SPACE? ;
+
+// delimeter
+delimeter: SPACE WHERE SPACE | SPACE? ':' SPACE ;
+WHERE: W H E R E ;
+
+// paging
+paging: PAGE SPACE pageNum=INT (SPACE SIZE SPACE pageSize=INT)? SPACE?;
+
+// sorting
+processSorting: SORT_BY SPACE processAttributeOrdering (',' SPACE? processAttributeOrdering)* SPACE?;
+processAttributeOrdering: processAttribute (SPACE ordering=(ASC | DESC))? ;
+processAttribute: ID
+ | IDENTIFIER
+ | VERSION
+ | TITLE
+ | CREATION_DATE
+ ;
+
+caseSorting: SORT_BY SPACE caseAttributeOrdering (',' SPACE? caseAttributeOrdering)* SPACE?;
+caseAttributeOrdering: caseAttribute (SPACE ordering=(ASC | DESC))? ;
+caseAttribute: ID
+ | PROCESS_ID
+ | PROCESS_IDENTIFIER
+ | TITLE
+ | CREATION_DATE
+ | AUTHOR
+ | places
+ | tasksUserId
+ | tasksState
+ | dataValue
+ | dataOptions
+ ;
+
+taskSorting: SORT_BY SPACE taskAttributeOrdering (',' SPACE? taskAttributeOrdering)* SPACE?;
+taskAttributeOrdering: taskAttribute (SPACE ordering=(ASC | DESC))? ;
+taskAttribute: ID
+ | TRANSITION_ID
+ | TITLE
+ | STATE
+ | USER_ID
+ | CASE_ID
+ | PROCESS_ID
+ | LAST_ASSIGN
+ | LAST_FINISH
+ ;
+
+userSorting: SORT_BY SPACE userAttributeOrdering (',' SPACE? userAttributeOrdering)* SPACE?;
+userAttributeOrdering: userAttribute (SPACE ordering=(ASC | DESC))? ;
+userAttribute: ID
+ | NAME
+ | SURNAME
+ | EMAIL
+ ;
+
+// resource comparisons
+processComparisons: idComparison
+ | identifierComparison
+ | versionComparison
+ | titleComparison
+ | creationDateComparison
+ ;
+
+caseComparisons: idComparison
+ | processIdObjIdComparison
+ | processIdentifierComparison
+ | titleComparison
+ | creationDateComparison
+ | authorComparison
+ | placesComparison
+ | tasksStateComparison
+ | tasksUserIdComparison
+ | dataValueComparison
+ | dataOptionsComparison
+ ;
+
+taskComparisons: idComparison
+ | transitionIdComparison
+ | titleComparison
+ | stateComparison
+ | userIdComparison
+ | caseIdComparison
+ | processIdComparison
+ | lastAssignComparison
+ | lastFinishComparison
+ ;
+
+userComparisons: idComparison
+ | nameComparison
+ | surnameComparison
+ | emailComparison
+ ;
+
+// attribute comparisons
+idComparison: ID SPACE objectIdComparison # idBasic
+ | ID SPACE inListStringComparison # idList
+ ;
+titleComparison: TITLE SPACE stringComparison # titleBasic
+ | TITLE SPACE inListStringComparison # titleList
+ | TITLE SPACE inRangeStringComparison # titleRange
+ ;
+identifierComparison: IDENTIFIER SPACE stringComparison # identifierBasic
+ | IDENTIFIER SPACE inListStringComparison # identifierList
+ | IDENTIFIER SPACE inRangeStringComparison # identifierRange
+ ;
+versionComparison: VERSION SPACE (NOT SPACE?)? op=(EQ | LT | GT | LTE | GTE) SPACE VERSION_NUMBER # versionBasic
+ | VERSION SPACE inListVersionComparison # versionListCmp
+ | VERSION SPACE inRangeVersionComparison # versionRangeCmp
+ ;
+creationDateComparison: CREATION_DATE SPACE dateComparison # cdDateBasic
+ | CREATION_DATE SPACE dateTimeComparison # cdDateTimeBasic
+ | CREATION_DATE SPACE inListDateComparison # cdDateList
+ | CREATION_DATE SPACE inRangeDateComparison # cdDateRange
+ ;
+processIdComparison: PROCESS_ID SPACE stringComparison # processIdBasic
+ | PROCESS_ID SPACE inListStringComparison # processIdList
+ ;
+processIdObjIdComparison: PROCESS_ID SPACE objectIdComparison # processIdObjIdBasic
+ | PROCESS_ID SPACE inListStringComparison # processIdObjIdList
+ ;
+processIdentifierComparison: PROCESS_IDENTIFIER SPACE stringComparison # processIdentifierBasic
+ | PROCESS_IDENTIFIER SPACE inListStringComparison # processIdentifierList
+ | PROCESS_IDENTIFIER SPACE inRangeStringComparison # processIdentifierRange
+ ;
+authorComparison: AUTHOR SPACE stringComparison # authorBasic
+ | AUTHOR SPACE inListStringComparison # authorList
+ ;
+transitionIdComparison: TRANSITION_ID SPACE stringComparison # transitionIdBasic
+ | TRANSITION_ID SPACE inListStringComparison # transitionIdList
+ | TRANSITION_ID SPACE inRangeStringComparison # transitionIdRange
+ ;
+stateComparison: STATE SPACE EQ SPACE state=(ENABLED | DISABLED) ;
+userIdComparison: USER_ID SPACE stringComparison # userIdBasic
+ | USER_ID SPACE inListStringComparison # userIdList
+ ;
+caseIdComparison: CASE_ID SPACE stringComparison # caseIdBasic
+ | CASE_ID SPACE inListStringComparison # caseIdList
+ ;
+lastAssignComparison: LAST_ASSIGN SPACE dateComparison # laDateBasic
+ | LAST_ASSIGN SPACE dateTimeComparison # laDateTimeBasic
+ | LAST_ASSIGN SPACE inListDateComparison # laDateList
+ | LAST_ASSIGN SPACE inRangeDateComparison # laDateRange
+ ;
+lastFinishComparison: LAST_FINISH SPACE dateComparison # lfDateBasic
+ | LAST_FINISH SPACE dateTimeComparison # lfDateTimeBasic
+ | LAST_FINISH SPACE inListDateComparison # lfDateList
+ | LAST_FINISH SPACE inRangeDateComparison # lfDateRange
+ ;
+nameComparison: NAME SPACE stringComparison # nameBasic
+ | NAME SPACE inListStringComparison # nameList
+ | NAME SPACE inRangeStringComparison # nameRange
+ ;
+surnameComparison: SURNAME SPACE stringComparison # surnameBasic
+ | SURNAME SPACE inListStringComparison # surnameList
+ | SURNAME SPACE inRangeStringComparison # surnameRange
+ ;
+emailComparison: EMAIL SPACE stringComparison # emailBasic
+ | EMAIL SPACE inListStringComparison # emailList
+ | EMAIL SPACE inRangeStringComparison # emailRange
+ ;
+dataValueComparison: dataValue SPACE stringComparison # dataString
+ | dataValue SPACE numberComparison # dataNumber
+ | dataValue SPACE dateComparison # dataDate
+ | dataValue SPACE dateTimeComparison # dataDatetime
+ | dataValue SPACE booleanComparison # dataBoolean
+ | dataValue SPACE inListStringComparison # dataStringList
+ | dataValue SPACE inListNumberComparison # dataNumberList
+ | dataValue SPACE inListDateComparison # dataDateList
+ | dataValue SPACE inRangeStringComparison # dataStringRange
+ | dataValue SPACE inRangeNumberComparison # dataNumberRange
+ | dataValue SPACE inRangeDateComparison # dataDateRange
+ ;
+dataOptionsComparison: dataOptions SPACE stringComparison # dataOptionsBasic
+ | dataOptions SPACE inListStringComparison # dataOptionsList
+ | dataOptions SPACE inRangeStringComparison # dataOptionsRange
+ ;
+placesComparison: places SPACE numberComparison # placesBasic
+ | places SPACE inListNumberComparison # placesList
+ | places SPACE inRangeNumberComparison # placesRange
+ ;
+tasksStateComparison: tasksState SPACE (NOT SPACE?)? op=EQ SPACE state=(ENABLED | DISABLED) ;
+tasksUserIdComparison: tasksUserId SPACE stringComparison # tasksUserIdBasic
+ | tasksUserId SPACE inListStringComparison # tasksUserIdList
+ ;
+
+// basic comparisons
+objectIdComparison: (NOT SPACE?)? op=EQ SPACE STRING ;
+stringComparison: (NOT SPACE?)? op=(EQ | CONTAINS | LT | GT | LTE | GTE) SPACE STRING ;
+numberComparison: (NOT SPACE?)? op=(EQ | LT | GT | LTE | GTE) SPACE number=(INT | DOUBLE) ;
+dateComparison: (NOT SPACE?)? op=(EQ | LT | GT | LTE | GTE) SPACE DATE ;
+dateTimeComparison: (NOT SPACE?)? op=(EQ | LT | GT | LTE | GTE) SPACE DATETIME ;
+booleanComparison: (NOT SPACE?)? op=EQ SPACE BOOLEAN ;
+
+// in list/in range comparisons
+inListStringComparison: (NOT SPACE?)? op=IN SPACE stringList ;
+inListNumberComparison: (NOT SPACE?)? op=IN SPACE (intList | doubleList) ;
+inListDateComparison: (NOT SPACE?)? op=IN SPACE (dateList | dateTimeList) ;
+inListVersionComparison: (NOT SPACE?)? op=IN SPACE versionList ;
+inRangeStringComparison: (NOT SPACE?)? op=IN SPACE stringRange ;
+inRangeNumberComparison: (NOT SPACE?)? op=IN SPACE (intRange | doubleRange) ;
+inRangeDateComparison: (NOT SPACE?)? op=IN SPACE (dateRange | dateTimeRange) ;
+inRangeVersionComparison: (NOT SPACE?)? op=IN SPACE versionRange ;
+
+// special attribute rules
+dataValue: DATA '.' fieldId=JAVA_ID '.' VALUE ;
+dataOptions: DATA '.' fieldId=JAVA_ID '.' OPTIONS ;
+places: PLACES '.' placeId=JAVA_ID '.' MARKING ;
+tasksState: TASKS '.' taskId=JAVA_ID '.' STATE ;
+tasksUserId: TASKS '.' taskId=JAVA_ID '.' USER_ID ;
+
+// operators
+AND: A N D | '&' ;
+OR: O R | '|' ;
+NOT: N O T | '!' ;
+EQ: E Q | '==' ;
+LT: L T | '<' ;
+GT: G T | '>' ;
+LTE: L T E | '<=' ;
+GTE: G T E | '>=' ;
+CONTAINS: C O N T A I N S | '~';
+IN: I N ;
+
+// resurces
+CASE: C A S E ;
+CASES: C A S E S ;
+TASK: T A S K ;
+TASKS: T A S K S ;
+USER: U S E R ;
+USERS: U S E R S ;
+PROCESS: P R O C E S S ;
+PROCESSES: P R O C E S S E S ;
+
+// attributes
+ID: I D ;
+TITLE: T I T L E ;
+IDENTIFIER: I D E N T I F I E R ;
+VERSION: V E R S I O N ;
+CREATION_DATE: C R E A T I O N D A T E ;
+PROCESS_ID: P R O C E S S I D ;
+PROCESS_IDENTIFIER: P R O C E S S I D E N T I F I E R ;
+AUTHOR: A U T H O R ;
+PLACES: P L A C E S ;
+TRANSITION_ID: T R A N S I T I O N I D ;
+STATE: S T A T E ;
+USER_ID: U S E R I D ;
+CASE_ID: C A S E I D ;
+LAST_ASSIGN: L A S T A S S I G N ;
+LAST_FINISH: L A S T F I N I S H ;
+NAME: N A M E ;
+SURNAME: S U R N A M E ;
+EMAIL: E M A I L ;
+
+DATA: D A T A ;
+VALUE: V A L U E ;
+OPTIONS: O P T I O N S ;
+MARKING: M A R K I N G ;
+ENABLED: E N A B L E D ;
+DISABLED: D I S A B L E D ;
+
+// paging
+PAGE: P A G E ;
+SIZE: S I Z E ;
+
+// sorting
+SORT_BY: S O R T SPACE B Y ;
+ASC: A S C ;
+DESC: D E S C ;
+
+// basic types
+stringList: '(' SPACE? (STRING SPACE? (',' SPACE? STRING SPACE? )* )? SPACE? ')' ;
+intList: '(' SPACE? (INT SPACE? (',' SPACE? INT SPACE? )* )? SPACE? ')' ;
+doubleList: '(' SPACE? (DOUBLE SPACE? (',' SPACE? DOUBLE SPACE? )* )? SPACE? ')' ;
+dateList: '(' SPACE? (DATE SPACE? (',' SPACE? DATE SPACE? )* )? SPACE? ')' ;
+dateTimeList: '(' SPACE? (DATETIME SPACE? (',' SPACE? DATETIME SPACE? )* )? SPACE? ')' ;
+versionList: '(' SPACE? (VERSION_NUMBER SPACE? (',' SPACE? VERSION_NUMBER SPACE? )* )? SPACE? ')' ;
+stringRange: leftEndpoint=('(' | '[') SPACE? STRING SPACE? ':' SPACE? STRING SPACE? rightEndpoint=(')' | ']') ;
+intRange: leftEndpoint=('(' | '[') SPACE? INT SPACE? ':' SPACE? INT SPACE? rightEndpoint=(')' | ']') ;
+doubleRange: leftEndpoint=('(' | '[') SPACE? DOUBLE SPACE? ':' SPACE? DOUBLE SPACE? rightEndpoint=(')' | ']') ;
+dateRange: leftEndpoint=('(' | '[') SPACE? DATE SPACE? ':' SPACE? DATE SPACE? rightEndpoint=(')' | ']') ;
+dateTimeRange: leftEndpoint=('(' | '[') SPACE? DATETIME SPACE? ':' SPACE? DATETIME SPACE? rightEndpoint=(')' | ']') ;
+versionRange: leftEndpoint=('(' | '[') SPACE? VERSION_NUMBER SPACE? ':' SPACE? VERSION_NUMBER SPACE? rightEndpoint=(')' | ']') ;
+STRING: '\'' (~('\'' | '\r' | '\n'))* '\'' ; // todo NAE-1997: escape???
+INT: DIGIT+ ;
+DOUBLE: DIGIT+ '.' DIGIT+ ;
+DATETIME: DATE 'T' ([01] DIGIT | '2' [0-3]) ':' [0-5] DIGIT ':' [0-5] DIGIT ('.' DIGIT+)? ; // 2020-03-03T20:00:00.055 // todo NAE-1997: format
+DATE: DIGIT DIGIT DIGIT DIGIT '-' ('0' [1-9] | '1' [0-2]) '-' ('0' [1-9] | [12] DIGIT | '3' [01]) ; // 2020-03-03 // todo NAE-1997: format
+BOOLEAN: T R U E | F A L S E ;
+VERSION_NUMBER: DIGIT+ '.' DIGIT+ '.' DIGIT+ ;
+JAVA_ID: [a-zA-Z$_] [a-zA-Z0-9$_]* ;
+
+SPACE: [ ]+ ;
+ANY: . ;
+
+// fragments
+fragment A : [aA];
+fragment B : [bB];
+fragment C : [cC];
+fragment D : [dD];
+fragment E : [eE];
+fragment F : [fF];
+fragment G : [gG];
+fragment H : [hH];
+fragment I : [iI];
+fragment J : [jJ];
+fragment K : [kK];
+fragment L : [lL];
+fragment M : [mM];
+fragment N : [nN];
+fragment O : [oO];
+fragment P : [pP];
+fragment Q : [qQ];
+fragment R : [rR];
+fragment S : [sS];
+fragment T : [tT];
+fragment U : [uU];
+fragment V : [vV];
+fragment W : [wW];
+fragment X : [xX];
+fragment Y : [yY];
+fragment Z : [zZ];
+fragment DIGIT: [0-9];
diff --git a/src/main/java/com/netgrif/application/engine/search/enums/ComparisonType.java b/src/main/java/com/netgrif/application/engine/search/enums/ComparisonType.java
new file mode 100644
index 00000000000..a013863f7e5
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/enums/ComparisonType.java
@@ -0,0 +1,11 @@
+package com.netgrif.application.engine.search.enums;
+
+public enum ComparisonType {
+ ID,
+ STRING,
+ NUMBER,
+ DATE,
+ DATETIME,
+ BOOLEAN,
+ OPTIONS
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/enums/QueryType.java b/src/main/java/com/netgrif/application/engine/search/enums/QueryType.java
new file mode 100644
index 00000000000..10453af537f
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/enums/QueryType.java
@@ -0,0 +1,8 @@
+package com.netgrif.application.engine.search.enums;
+
+public enum QueryType {
+ PROCESS,
+ CASE,
+ TASK,
+ USER
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/interfaces/ISearchService.java b/src/main/java/com/netgrif/application/engine/search/interfaces/ISearchService.java
new file mode 100644
index 00000000000..b8dddee63bc
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/interfaces/ISearchService.java
@@ -0,0 +1,11 @@
+package com.netgrif.application.engine.search.interfaces;
+
+public interface ISearchService {
+ String explainQuery(String query);
+
+ Object search(String query);
+
+ Long count(String query);
+
+ boolean exists(String query);
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/utils/QueryLangTreeNode.java b/src/main/java/com/netgrif/application/engine/search/utils/QueryLangTreeNode.java
new file mode 100644
index 00000000000..440952b211c
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/utils/QueryLangTreeNode.java
@@ -0,0 +1,43 @@
+package com.netgrif.application.engine.search.utils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * https://stackoverflow.com/a/8948691
+ */
+public class QueryLangTreeNode {
+ final String name;
+ final List children;
+
+ public QueryLangTreeNode(String name, List children) {
+ this.name = name;
+ this.children = children;
+ }
+
+ public QueryLangTreeNode(String name) {
+ this.name = name;
+ this.children = new ArrayList<>();
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder(50);
+ print(buffer, "", "");
+ return buffer.toString();
+ }
+
+ private void print(StringBuilder buffer, String prefix, String childrenPrefix) {
+ buffer.append(prefix);
+ buffer.append(name);
+ buffer.append('\n');
+ for (Iterator it = children.iterator(); it.hasNext();) {
+ QueryLangTreeNode next = it.next();
+ if (it.hasNext()) {
+ next.print(buffer, childrenPrefix + "├── ", childrenPrefix + "│ ");
+ } else {
+ next.print(buffer, childrenPrefix + "└── ", childrenPrefix + " ");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/netgrif/application/engine/search/utils/SearchUtils.java b/src/main/java/com/netgrif/application/engine/search/utils/SearchUtils.java
new file mode 100644
index 00000000000..2d88ad0ef87
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/search/utils/SearchUtils.java
@@ -0,0 +1,425 @@
+package com.netgrif.application.engine.search.utils;
+
+import com.netgrif.application.engine.search.QueryLangErrorListener;
+import com.netgrif.application.engine.search.QueryLangEvaluator;
+import com.netgrif.application.engine.search.QueryLangExplainEvaluator;
+import com.netgrif.application.engine.search.antlr4.QueryLangBaseListener;
+import com.netgrif.application.engine.search.antlr4.QueryLangLexer;
+import com.netgrif.application.engine.search.antlr4.QueryLangParser;
+import com.netgrif.application.engine.petrinet.domain.QPetriNet;
+import com.netgrif.application.engine.petrinet.domain.version.QVersion;
+import com.netgrif.application.engine.petrinet.domain.version.Version;
+import com.netgrif.application.engine.search.enums.ComparisonType;
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.BooleanExpression;
+import com.querydsl.core.types.dsl.DateTimePath;
+import com.querydsl.core.types.dsl.StringPath;
+import lombok.extern.slf4j.Slf4j;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.bson.types.ObjectId;
+import org.bson.types.QObjectId;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+public class SearchUtils {
+
+ public static final Map> comparisonOperators = Map.of(
+ ComparisonType.ID, List.of(QueryLangParser.EQ, QueryLangParser.IN),
+ ComparisonType.STRING, List.of(QueryLangParser.EQ, QueryLangParser.CONTAINS, QueryLangParser.LT, QueryLangParser.LTE, QueryLangParser.GT, QueryLangParser.GTE),
+ ComparisonType.NUMBER, List.of(QueryLangParser.EQ, QueryLangParser.LT, QueryLangParser.LTE, QueryLangParser.GT, QueryLangParser.GTE),
+ ComparisonType.DATE, List.of(QueryLangParser.EQ, QueryLangParser.LT, QueryLangParser.LTE, QueryLangParser.GT, QueryLangParser.GTE),
+ ComparisonType.DATETIME, List.of(QueryLangParser.EQ, QueryLangParser.LT, QueryLangParser.LTE, QueryLangParser.GT, QueryLangParser.GTE),
+ ComparisonType.BOOLEAN, List.of(QueryLangParser.EQ)
+ );
+
+ public static final Map processAttrToSortPropMapping = Map.of(
+ "id", "id",
+ "identifier", "identifier",
+ "version", "version",
+ "title", "title.defaultValue",
+ "creationdate", "creationDate"
+ );
+
+ public static final Map caseAttrToSortPropMapping = Map.of(
+ "id", "id",
+ "processidentifier", "processIdentifier",
+ "processid", "petriNetObjectId",
+ "title", "title",
+ "creationdate", "creationDate",
+ "author", "author.id"
+ );
+
+ public static final Map caseAttrToSortPropElasticMapping = Map.of(
+ "id", "stringId.keyword",
+ "processidentifier", "processIdentifier.keyword",
+ "processid", "processId.keyword",
+ "title", "title.keyword",
+ "creationdate", "creationDateSortable",
+ "author", "author.keyword"
+ );
+
+ public static final Map taskAttrToSortPropMapping = Map.of(
+ "id", "id",
+ "transitionid", "transitionId",
+ "title", "title.defaultValue",
+ "state", "state",
+ "userid", "userId",
+ "caseid", "caseId",
+ "processid", "processId",
+ "lastassign", "lastAssigned",
+ "lastfinish", "lastFinished"
+ );
+
+ public static final Map userAttrToSortPropMapping = Map.of(
+ "id", "id",
+ "name", "name",
+ "surname", "surname",
+ "email", "email"
+ );
+
+ public static final String LEFT_OPEN_ENDPOINT = "(";
+ public static final String RIGHT_OPEN_ENDPOINT = ")";
+
+ public static String toDateString(LocalDate localDate) {
+ return localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
+ }
+
+ public static String toDateString(LocalDateTime localDateTime) {
+ return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE);
+ }
+
+ public static String toDateTimeString(LocalDate localDate) {
+ return localDate.atTime(12, 0, 0).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+ }
+
+ public static String toDateTimeString(LocalDateTime localDateTime) {
+ return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+ }
+
+ public static LocalDateTime toDateTime(String dateTimeString) {
+ try {
+ return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+ } catch (DateTimeParseException ignored) {
+ try {
+ return LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE).atTime(12, 0, 0);
+ } catch (DateTimeParseException e) {
+ throw new IllegalArgumentException("Invalid date/datetime format");
+ }
+ }
+ }
+
+ public static LocalDate toDate(String dateString) {
+ try {
+ return LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE);
+ } catch (DateTimeParseException ignored) {
+ try {
+ return LocalDateTime.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE_TIME).toLocalDate();
+ } catch (DateTimeParseException e) {
+ throw new IllegalArgumentException("Invalid date/datetime format");
+ }
+ }
+ }
+
+ public static QueryLangExplainEvaluator explainQueryInternal(ParseTreeWalker walker, QueryLangParser.QueryContext query, QueryLangErrorListener errorListener) {
+ QueryLangExplainEvaluator evaluator = new QueryLangExplainEvaluator();
+ walker.walk(evaluator, query);
+
+ if (!errorListener.getErrorMessages().isEmpty()) {
+ throw new IllegalArgumentException("\n" + evaluator.explain() + "\n" + String.join("\n", errorListener.getErrorMessages()));
+ }
+
+ return evaluator;
+ }
+
+ private static QueryLangBaseListener evaluateQueryInternal(String input, boolean onlyExplain) {
+ if (input == null || input.isEmpty()) {
+ throw new IllegalArgumentException("Query cannot be empty.");
+ }
+
+ QueryLangLexer lexer = new QueryLangLexer(CharStreams.fromString(input));
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
+ QueryLangParser parser = new QueryLangParser(tokens);
+ QueryLangErrorListener errorListener = new QueryLangErrorListener();
+ parser.removeErrorListeners();
+ parser.addErrorListener(errorListener);
+
+ QueryLangParser.QueryContext query = parser.query();
+ ParseTreeWalker walker = new ParseTreeWalker();
+
+ if (onlyExplain || !errorListener.getErrorMessages().isEmpty()) {
+ return explainQueryInternal(walker, query, errorListener);
+ }
+
+ QueryLangEvaluator evaluator = new QueryLangEvaluator();
+ walker.walk(evaluator, query);
+
+ return evaluator;
+ }
+
+ public static QueryLangEvaluator evaluateQuery(String input) {
+ return (QueryLangEvaluator) evaluateQueryInternal(input, false);
+ }
+
+ public static String explainQuery(String input) {
+ QueryLangExplainEvaluator evaluator = (QueryLangExplainEvaluator) evaluateQueryInternal(input, true);
+ return evaluator.explain();
+ }
+
+ public static String getStringValue(String queryLangString) {
+ return queryLangString.replace("'", "");
+ }
+
+ public static ObjectId getObjectIdValue(String queryLangString) {
+ String objectId = getStringValue(queryLangString);
+ if (ObjectId.isValid(objectId)) {
+ return new ObjectId(objectId);
+ }
+
+ throw new IllegalArgumentException("Invalid objectId: " + objectId);
+ }
+
+ public static void checkOp(ComparisonType type, Token op) {
+ if (!comparisonOperators.get(type).contains(op.getType())) {
+ throw new IllegalArgumentException("Operator " + op.getText() + " is not applicable for type " + type.toString());
+ }
+ }
+
+ public static Predicate buildObjectIdPredicate(QObjectId qObjectId, int op, ObjectId objectId, boolean not) {
+ if (op != QueryLangParser.EQ) {
+ throw new UnsupportedOperationException("Operator is not available for id comparison");
+ }
+
+ Predicate predicate = qObjectId.eq(objectId);
+ if (not) {
+ predicate = predicate.not();
+ }
+ return predicate;
+ }
+
+ public static Predicate buildObjectIdPredicateInList(QObjectId qObjectId, List values, boolean not) {
+ Predicate predicate = qObjectId.in(values);
+
+ return not ? predicate.not() : predicate;
+ }
+
+ public static Predicate buildStringPredicate(StringPath stringPath, int op, String string, boolean not) {
+ Predicate predicate = null;
+ switch (op) {
+ case QueryLangParser.EQ:
+ predicate = stringPath.eq(string);
+ break;
+ case QueryLangParser.CONTAINS:
+ predicate = stringPath.contains(string);
+ break;
+ case QueryLangParser.LT:
+ predicate = stringPath.lt(string);
+ break;
+ case QueryLangParser.LTE:
+ predicate = stringPath.loe(string);
+ break;
+ case QueryLangParser.GT:
+ predicate = stringPath.gt(string);
+ break;
+ case QueryLangParser.GTE:
+ predicate = stringPath.goe(string);
+ break;
+ }
+
+ if (predicate == null) {
+ throw new UnsupportedOperationException("Operator is not available for string comparison");
+ }
+
+ if (not) {
+ predicate = predicate.not();
+ }
+ return predicate;
+ }
+
+ public static Predicate buildStringPredicateInList(StringPath stringPath, List values, boolean not) {
+ Predicate predicate = stringPath.in(values);
+
+ return not ? predicate.not() : predicate;
+ }
+
+ public static Predicate buildStringPredicateInRange(StringPath stringPath, String leftValue, boolean leftEndpointOpen, String rightValue, boolean rightEndpointOpen, boolean not) {
+ BooleanExpression leftExpression = leftEndpointOpen ? stringPath.gt(leftValue) : stringPath.goe(leftValue);
+ BooleanExpression rightExpression = rightEndpointOpen ? stringPath.lt(rightValue) : stringPath.loe(rightValue);
+ Predicate predicate = leftExpression.and(rightExpression);
+
+ return not ? predicate.not() : predicate;
+ }
+
+ public static Predicate buildVersionPredicate(int op, String versionString, boolean not) {
+ String[] versionNumber = versionString.split("\\.");
+ long major = Long.parseLong(versionNumber[0]);
+ long minor = Long.parseLong(versionNumber[1]);
+ long patch = Long.parseLong(versionNumber[2]);
+
+ QVersion qVersion = QPetriNet.petriNet.version;
+
+ Predicate predicate = null;
+ switch (op) {
+ case QueryLangParser.EQ:
+ predicate = qVersion.eq(new Version(major, minor, patch));
+ break;
+ case QueryLangParser.GT:
+ predicate = qVersion.major.gt(major)
+ .or(qVersion.major.eq(major).and(qVersion.minor.gt(minor)))
+ .or(qVersion.major.eq(major).and(qVersion.minor.eq(minor).and(qVersion.patch.gt(patch))));
+ break;
+ case QueryLangParser.GTE:
+ predicate = qVersion.major.goe(major)
+ .or(qVersion.major.eq(major).and(qVersion.minor.goe(minor)))
+ .or(qVersion.major.eq(major).and(qVersion.minor.eq(minor).and(qVersion.patch.goe(patch))));
+ break;
+ case QueryLangParser.LT:
+ predicate = qVersion.major.lt(major)
+ .or(qVersion.major.eq(major).and(qVersion.minor.lt(minor)))
+ .or(qVersion.major.eq(major).and(qVersion.minor.eq(minor).and(qVersion.patch.lt(patch))));
+ break;
+ case QueryLangParser.LTE:
+ predicate = qVersion.major.loe(major)
+ .or(qVersion.major.eq(major).and(qVersion.minor.loe(minor)))
+ .or(qVersion.major.eq(major).and(qVersion.minor.eq(minor).and(qVersion.patch.loe(patch))));
+ break;
+ }
+
+ if (predicate == null) {
+ throw new UnsupportedOperationException("Operator is not available for version comparison");
+ }
+
+ if (not) {
+ predicate = predicate.not();
+ }
+ return predicate;
+ }
+
+ public static Predicate buildVersionPredicateInList(List values, boolean not) {
+ List versions = values.stream().map(stringVersion -> {
+ String[] versionNumber = stringVersion.split("\\.");
+ long major = Long.parseLong(versionNumber[0]);
+ long minor = Long.parseLong(versionNumber[1]);
+ long patch = Long.parseLong(versionNumber[2]);
+
+ return new Version(major, minor, patch);
+ }).collect(Collectors.toList());
+
+ Predicate predicate = QPetriNet.petriNet.version.in(versions);
+ return not ? predicate.not() : predicate;
+ }
+
+ public static Predicate buildVersionPredicateInRange(String leftValue, boolean leftEndpointOpen, String rightValue, boolean rightEndpointOpen, boolean not) {
+ Predicate leftExpression = buildVersionPredicate(leftEndpointOpen ? QueryLangParser.GT : QueryLangParser.GTE, leftValue, false);
+ Predicate rightExpression = buildVersionPredicate(rightEndpointOpen ? QueryLangParser.LT : QueryLangParser.LTE, rightValue, false);
+
+ BooleanBuilder predicate = new BooleanBuilder();
+ predicate.and(leftExpression);
+ predicate.and(rightExpression);
+
+ return not ? predicate.not() : predicate;
+ }
+
+ public static Predicate buildDateTimePredicate(DateTimePath dateTimePath, int op, LocalDateTime localDateTime, boolean not) {
+ Predicate predicate = null;
+ switch (op) {
+ case QueryLangParser.EQ:
+ predicate = dateTimePath.eq(localDateTime);
+ break;
+ case QueryLangParser.LT:
+ predicate = dateTimePath.lt(localDateTime);
+ break;
+ case QueryLangParser.LTE:
+ predicate = dateTimePath.loe(localDateTime);
+ break;
+ case QueryLangParser.GT:
+ predicate = dateTimePath.gt(localDateTime);
+ break;
+ case QueryLangParser.GTE:
+ predicate = dateTimePath.goe(localDateTime);
+ break;
+ }
+
+ if (predicate == null) {
+ throw new UnsupportedOperationException("Operator is not available for date/datetime comparison");
+ }
+
+ if (not) {
+ predicate = predicate.not();
+ }
+ return predicate;
+ }
+
+ public static Predicate buildDateTimePredicateInList(DateTimePath dateTimePath, List values, boolean not) {
+ List dateTimes = values.stream().map(SearchUtils::toDateTime).collect(Collectors.toList());
+ Predicate predicate = dateTimePath.in(dateTimes);
+
+ return not ? predicate.not() : predicate;
+ }
+
+ public static Predicate buildDateTimePredicateInRange(DateTimePath dateTimePath, LocalDateTime leftValue, boolean leftEndpointOpen, LocalDateTime rightValue, boolean rightEndpointOpen, boolean not) {
+ BooleanExpression leftExpression = leftEndpointOpen ? dateTimePath.gt(leftValue) : dateTimePath.goe(leftValue);
+ BooleanExpression rightExpression = rightEndpointOpen ? dateTimePath.lt(rightValue) : dateTimePath.loe(rightValue);
+ Predicate predicate = leftExpression.and(rightExpression);
+
+ return not ? predicate.not() : predicate;
+ }
+
+ public static String buildElasticQuery(String attribute, int op, String value, boolean not) {
+ String query = null;
+ switch (op) {
+ case QueryLangParser.EQ:
+ case QueryLangParser.IN:
+ query = attribute + ":" + value;
+ break;
+ case QueryLangParser.LT:
+ query = attribute + ":<" + value;
+ break;
+ case QueryLangParser.LTE:
+ query = attribute + ":<=" + value;
+ break;
+ case QueryLangParser.GT:
+ query = attribute + ":>" + value;
+ break;
+ case QueryLangParser.GTE:
+ query = attribute + ":>=" + value;
+ break;
+ case QueryLangParser.CONTAINS:
+ query = attribute + ":*" + value + "*";
+ break;
+ }
+
+ if (query == null) {
+ throw new UnsupportedOperationException("Operator is not available for elastic comparison");
+ }
+
+ if (not) {
+ query = "NOT " + query;
+ }
+ return query;
+ }
+
+ public static String buildElasticQueryInList(String attribute, List values, boolean not) {
+ String valuesQuery = "(" + String.join(" OR ", values) + ")";
+ return buildElasticQuery(attribute, QueryLangParser.IN, valuesQuery, not);
+ }
+
+ public static String buildElasticQueryInRange(String attribute, String leftValue, boolean leftEndpointOpen, String rightValue, boolean rightEndpointOpen, boolean not) {
+ String query = "("
+ + buildElasticQuery(attribute, leftEndpointOpen ? QueryLangParser.GT : QueryLangParser.GTE, leftValue, false)
+ + " AND "
+ + buildElasticQuery(attribute, rightEndpointOpen ? QueryLangParser.LT : QueryLangParser.LTE, rightValue, false)
+ + ")";
+ return not ? "NOT " + query : query;
+ }
+}
diff --git a/src/test/java/com/netgrif/application/engine/search/QueryLangTest.java b/src/test/java/com/netgrif/application/engine/search/QueryLangTest.java
new file mode 100644
index 00000000000..57d85b6ec73
--- /dev/null
+++ b/src/test/java/com/netgrif/application/engine/search/QueryLangTest.java
@@ -0,0 +1,1866 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.auth.domain.QUser;
+import com.netgrif.application.engine.auth.domain.User;
+import com.netgrif.application.engine.petrinet.domain.PetriNet;
+import com.netgrif.application.engine.petrinet.domain.QPetriNet;
+import com.netgrif.application.engine.petrinet.domain.version.Version;
+import com.netgrif.application.engine.search.utils.MongoDbUtils;
+import com.netgrif.application.engine.workflow.domain.*;
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.DateTimePath;
+import com.querydsl.core.types.dsl.StringPath;
+import lombok.extern.slf4j.Slf4j;
+import org.bson.Document;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static com.netgrif.application.engine.search.utils.SearchUtils.evaluateQuery;
+
+@Slf4j
+@SpringBootTest
+@ActiveProfiles({"test"})
+@ExtendWith(SpringExtension.class)
+public class QueryLangTest {
+ public static final ObjectId GENERIC_OBJECT_ID = ObjectId.get();
+
+ @Autowired
+ MongoOperations mongoOperations;
+
+ @Test
+ public void testSimpleMongodbProcessQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, PetriNet.class);
+
+ // id comparison
+ Predicate actual = evaluateQuery(String.format("process: id eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("process: id in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.in(GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // identifier comparison
+ checkStringComparison(mongoDbUtils, "process", "identifier", QPetriNet.petriNet.identifier);
+
+ // version comparison
+ actual = evaluateQuery("process: version eq 1.1.1").getFullMongoQuery();
+ expected = QPetriNet.petriNet.version.eq(new Version(1, 1, 1));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version lt 1.1.1").getFullMongoQuery();
+ expected = QPetriNet.petriNet.version.major.lt(1)
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.lt(1)))
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.eq(1L).and(QPetriNet.petriNet.version.patch.lt(1))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version lte 1.1.1").getFullMongoQuery();
+ expected = QPetriNet.petriNet.version.major.loe(1)
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.loe(1)))
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.eq(1L).and(QPetriNet.petriNet.version.patch.loe(1))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version gt 1.1.1").getFullMongoQuery();
+ expected = QPetriNet.petriNet.version.major.gt(1)
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.gt(1)))
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.eq(1L).and(QPetriNet.petriNet.version.patch.gt(1))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version gte 1.1.1").getFullMongoQuery();
+ expected = QPetriNet.petriNet.version.major.goe(1)
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.goe(1)))
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.eq(1L).and(QPetriNet.petriNet.version.patch.goe(1))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ Version v1 = new Version(1, 1, 1);
+ Version v2 = new Version(2, 2, 2);
+ Version v3 = new Version(3, 3, 3);
+ actual = evaluateQuery("process: version in (1.1.1, 2.2.2, 3.3.3)").getFullMongoQuery();
+ expected = QPetriNet.petriNet.version.in(List.of(v1, v2, v3));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version not in (1.1.1, 2.2.2, 3.3.3)").getFullMongoQuery();
+ expected = QPetriNet.petriNet.version.in(List.of(v1, v2, v3)).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version in (1.1.1 : 2.2.2)").getFullMongoQuery();
+ BooleanBuilder builder = new BooleanBuilder();
+ builder.and(QPetriNet.petriNet.version.major.gt(1)
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.gt(1)))
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.eq(1L).and(QPetriNet.petriNet.version.patch.gt(1)))));
+ builder.and(QPetriNet.petriNet.version.major.lt(2)
+ .or(QPetriNet.petriNet.version.major.eq(2L).and(QPetriNet.petriNet.version.minor.lt(2)))
+ .or(QPetriNet.petriNet.version.major.eq(2L).and(QPetriNet.petriNet.version.minor.eq(2L).and(QPetriNet.petriNet.version.patch.lt(2)))));
+ expected = builder;
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version in [1.1.1 : 2.2.2]").getFullMongoQuery();
+ builder = new BooleanBuilder();
+ builder.and(QPetriNet.petriNet.version.major.goe(1)
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.goe(1)))
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.eq(1L).and(QPetriNet.petriNet.version.patch.goe(1)))));
+ builder.and(QPetriNet.petriNet.version.major.loe(2)
+ .or(QPetriNet.petriNet.version.major.eq(2L).and(QPetriNet.petriNet.version.minor.loe(2)))
+ .or(QPetriNet.petriNet.version.major.eq(2L).and(QPetriNet.petriNet.version.minor.eq(2L).and(QPetriNet.petriNet.version.patch.loe(2)))));
+ expected = builder;
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("process: version not in (1.1.1 : 2.2.2]").getFullMongoQuery();
+ builder = new BooleanBuilder();
+ builder.and(QPetriNet.petriNet.version.major.gt(1)
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.gt(1)))
+ .or(QPetriNet.petriNet.version.major.eq(1L).and(QPetriNet.petriNet.version.minor.eq(1L).and(QPetriNet.petriNet.version.patch.gt(1)))));
+ builder.and(QPetriNet.petriNet.version.major.loe(2)
+ .or(QPetriNet.petriNet.version.major.eq(2L).and(QPetriNet.petriNet.version.minor.loe(2)))
+ .or(QPetriNet.petriNet.version.major.eq(2L).and(QPetriNet.petriNet.version.minor.eq(2L).and(QPetriNet.petriNet.version.patch.loe(2)))));
+ expected = builder.not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // title comparison
+ checkStringComparison(mongoDbUtils, "process", "title", QPetriNet.petriNet.title.defaultValue);
+
+ // creationDate comparison
+ checkDateComparison(mongoDbUtils, "process", "creationDate", QPetriNet.petriNet.creationDate);
+ }
+
+ @Test
+ public void testComplexMongodbProcessQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, PetriNet.class);
+
+ // not comparison
+ Predicate actual = evaluateQuery(String.format("process: id not eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and title eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID).and(QPetriNet.petriNet.title.defaultValue.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and not comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and title not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID).and(QPetriNet.petriNet.title.defaultValue.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' or title eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID).or(QPetriNet.petriNet.title.defaultValue.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or not comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' or title not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID).or(QPetriNet.petriNet.title.defaultValue.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID).and(QPetriNet.petriNet.title.defaultValue.eq("test").or(QPetriNet.petriNet.title.defaultValue.eq("test1")));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // parenthesis not comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and not (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID).and(QPetriNet.petriNet.title.defaultValue.eq("test").or(QPetriNet.petriNet.title.defaultValue.eq("test1")).not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and (title eq 'test' or (title eq 'test1' and identifier eq 'test'))", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QPetriNet.petriNet.id.eq(GENERIC_OBJECT_ID)
+ .and(QPetriNet.petriNet.title.defaultValue.eq("test")
+ .or(QPetriNet.petriNet.title.defaultValue.eq("test1").and(QPetriNet.petriNet.identifier.eq("test"))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+ }
+
+ @Test
+ public void testSimpleMongodbCaseQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, Case.class);
+
+ // id comparison
+ Predicate actual = evaluateQuery(String.format("case: id eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QCase.case$.id.eq(GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("case: id in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.id.in(GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // processId comparison
+ actual = evaluateQuery(String.format("case: processId eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.petriNetObjectId.eq(GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("case: processId in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.petriNetObjectId.in(GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // processIdentifier comparison
+ checkStringComparison(mongoDbUtils, "case", "processIdentifier", QCase.case$.processIdentifier);
+
+ // title comparison
+ checkStringComparison(mongoDbUtils, "case", "title", QCase.case$.title);
+
+ // creationDate comparison
+ checkDateComparison(mongoDbUtils, "case", "creationDate", QCase.case$.creationDate);
+
+ // author comparison
+ actual = evaluateQuery("case: author eq 'test'").getFullMongoQuery();
+ expected = QCase.case$.author.id.eq("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("case: author contains 'test'").getFullMongoQuery();
+ expected = QCase.case$.author.id.contains("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("case: author in ('test', 'test1')").getFullMongoQuery();
+ expected = QCase.case$.author.id.in("test", "test1");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // only available for elastic query
+ // places comparison
+ actual = evaluateQuery("case: places.p1.marking eq 1").getFullMongoQuery();
+ assert actual == null;
+
+ // task state comparison
+ actual = evaluateQuery("case: tasks.t1.state eq enabled").getFullMongoQuery();
+ assert actual == null;
+
+ // task userId comparison
+ actual = evaluateQuery("case: tasks.t1.userId eq 'test'").getFullMongoQuery();
+ assert actual == null;
+
+ // data value comparison
+ actual = evaluateQuery("case: data.field1.value eq 'test'").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value contains 'test'").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value eq 1").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value lt 1").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value lte 1").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value gt 1").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value gte 1").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value eq 2011-12-03T10:15:30").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value lt 2011-12-03T10:15:30").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value lte 2011-12-03T10:15:30").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value gt 2011-12-03T10:15:30").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value gte 2011-12-03T10:15:30").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.value eq true").getFullMongoQuery();
+ assert actual == null;
+
+ // data options comparison
+ actual = evaluateQuery("case: data.field1.options eq 'test'").getFullMongoQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("case: data.field1.options contains 'test'").getFullMongoQuery();
+ assert actual == null;
+ }
+
+ @Test
+ public void testComplexMongodbCaseQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, Case.class);
+
+ // not comparison
+ Predicate actual = evaluateQuery(String.format("case: id not eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QCase.case$.id.eq(GENERIC_OBJECT_ID).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and title eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.id.eq(GENERIC_OBJECT_ID).and(QCase.case$.title.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and not comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and title not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.id.eq(GENERIC_OBJECT_ID).and(QCase.case$.title.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' or title eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.id.eq(GENERIC_OBJECT_ID).or(QCase.case$.title.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or not comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' or title not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.id.eq(GENERIC_OBJECT_ID).or(QCase.case$.title.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.id.eq(GENERIC_OBJECT_ID).and(QCase.case$.title.eq("test").or(QCase.case$.title.eq("test1")));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and (title eq 'test' or (title eq 'test1' and processIdentifier eq 'test'))", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QCase.case$.id.eq(GENERIC_OBJECT_ID)
+ .and(QCase.case$.title.eq("test")
+ .or(QCase.case$.title.eq("test1").and(QCase.case$.processIdentifier.eq("test"))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+ }
+
+ @Test
+ public void testSimpleMongodbTaskQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, Task.class);
+
+ // id comparison
+ Predicate actual = evaluateQuery(String.format("task: id eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QTask.task.id.eq(GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("task: id in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.in(GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // transitionId comparison
+ checkStringComparison(mongoDbUtils, "task", "transitionId", QTask.task.transitionId);
+
+ // title comparison
+ checkStringComparison(mongoDbUtils, "task", "title", QTask.task.title.defaultValue);
+
+ // state comparison
+ actual = evaluateQuery("task: state eq enabled").getFullMongoQuery();
+ expected = QTask.task.state.eq(State.ENABLED);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("task: state eq disabled").getFullMongoQuery();
+ expected = QTask.task.state.eq(State.DISABLED);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // userId comparison
+ actual = evaluateQuery("task: userId eq 'test'").getFullMongoQuery();
+ expected = QTask.task.userId.eq("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("task: userId contains 'test'").getFullMongoQuery();
+ expected = QTask.task.userId.contains("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("task: userId in ('test', 'test1')").getFullMongoQuery();
+ expected = QTask.task.userId.in("test", "test1");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // caseId comparison
+ actual = evaluateQuery("task: caseId eq 'test'").getFullMongoQuery();
+ expected = QTask.task.caseId.eq("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("task: caseId contains 'test'").getFullMongoQuery();
+ expected = QTask.task.caseId.contains("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("task: caseId in ('test', 'test1')").getFullMongoQuery();
+ expected = QTask.task.caseId.in("test", "test1");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // processId comparison
+ actual = evaluateQuery("task: processId eq 'test'").getFullMongoQuery();
+ expected = QTask.task.processId.eq("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("task: processId contains 'test'").getFullMongoQuery();
+ expected = QTask.task.processId.contains("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery("task: processId in ('test', 'test1')").getFullMongoQuery();
+ expected = QTask.task.processId.in("test", "test1");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // lastAssign comparison
+ checkDateComparison(mongoDbUtils, "task", "lastAssign", QTask.task.lastAssigned);
+
+ // lastFinish comparison
+ checkDateComparison(mongoDbUtils, "task", "lastFinish", QTask.task.lastFinished);
+ }
+
+ @Test
+ public void testComplexMongodbTaskQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, Task.class);
+
+ // not comparison
+ Predicate actual = evaluateQuery(String.format("task: id not eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QTask.task.id.eq(GENERIC_OBJECT_ID).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and title eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.eq(GENERIC_OBJECT_ID).and(QTask.task.title.defaultValue.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and not comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and title not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.eq(GENERIC_OBJECT_ID).and(QTask.task.title.defaultValue.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' or title eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.eq(GENERIC_OBJECT_ID).or(QTask.task.title.defaultValue.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or not comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' or title not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.eq(GENERIC_OBJECT_ID).or(QTask.task.title.defaultValue.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.eq(GENERIC_OBJECT_ID).and(QTask.task.title.defaultValue.eq("test").or(QTask.task.title.defaultValue.eq("test1")));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // parenthesis not comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and not (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.eq(GENERIC_OBJECT_ID).and(QTask.task.title.defaultValue.eq("test").or(QTask.task.title.defaultValue.eq("test1")).not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and (title eq 'test' or (title eq 'test1' and processId eq 'test'))", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QTask.task.id.eq(GENERIC_OBJECT_ID)
+ .and(QTask.task.title.defaultValue.eq("test")
+ .or(QTask.task.title.defaultValue.eq("test1").and(QTask.task.processId.eq("test"))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+ }
+
+ @Test
+ public void testSimpleMongodbUserQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, User.class);
+
+ // id comparison
+ Predicate actual = evaluateQuery(String.format("user: id eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QUser.user.id.eq(GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("user: id in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.in(GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // name comparison
+ checkStringComparison(mongoDbUtils, "user", "name", QUser.user.name);
+
+ // surname comparison
+ checkStringComparison(mongoDbUtils, "user", "surname", QUser.user.surname);
+
+ // email comparison
+ checkStringComparison(mongoDbUtils, "user", "email", QUser.user.email);
+ }
+
+ @Test
+ public void testComplexMongodbUserQuery() {
+ MongoDbUtils mongoDbUtils = new MongoDbUtils<>(mongoOperations, User.class);
+
+ // not comparison
+ Predicate actual = evaluateQuery(String.format("user: id not eq '%s'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ Predicate expected = QUser.user.id.eq(GENERIC_OBJECT_ID).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and email eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.eq(GENERIC_OBJECT_ID).and(QUser.user.email.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // and not comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and email not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.eq(GENERIC_OBJECT_ID).and(QUser.user.email.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' or email eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.eq(GENERIC_OBJECT_ID).or(QUser.user.email.eq("test"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // or not comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' or email not eq 'test'", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.eq(GENERIC_OBJECT_ID).or(QUser.user.email.eq("test").not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and (email eq 'test' or email eq 'test1')", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.eq(GENERIC_OBJECT_ID).and(QUser.user.email.eq("test").or(QUser.user.email.eq("test1")));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // parenthesis not comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and not (email eq 'test' or email eq 'test1')", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.eq(GENERIC_OBJECT_ID).and(QUser.user.email.eq("test").or(QUser.user.email.eq("test1")).not());
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and (email eq 'test' or (email eq 'test1' and name eq 'test'))", GENERIC_OBJECT_ID)).getFullMongoQuery();
+ expected = QUser.user.id.eq(GENERIC_OBJECT_ID)
+ .and(QUser.user.email.eq("test")
+ .or(QUser.user.email.eq("test1").and(QUser.user.name.eq("test"))));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+ }
+
+ @Test
+ public void testSimpleElasticProcessQuery() {
+ // elastic query should be always null
+ // id comparison
+ String actual = evaluateQuery(String.format("process: id eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // identifier comparison
+ actual = evaluateQuery("process: identifier eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("process: identifier contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // version comparison
+ actual = evaluateQuery("process: version eq 1.1.1").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("process: version lt 1.1.1").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("process: version lte 1.1.1").getFullElasticQuery();
+ assert actual == null;
+
+
+ actual = evaluateQuery("process: version gt 1.1.1").getFullElasticQuery();
+ assert actual == null;
+
+
+ actual = evaluateQuery("process: version gte 1.1.1").getFullElasticQuery();
+ assert actual == null;
+
+
+ // title comparison
+ actual = evaluateQuery("process: title eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+
+ actual = evaluateQuery("process: title contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+
+ // creationDate comparison
+ actual = evaluateQuery("process: creationDate eq 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+
+ actual = evaluateQuery("process: creationDate lt 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+
+ actual = evaluateQuery("process: creationDate lte 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+
+ actual = evaluateQuery("process: creationDate gt 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+
+ actual = evaluateQuery("process: creationDate gte 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+ }
+
+ @Test
+ public void testComplexElasticProcessQuery() {
+ // elastic query should be always null
+ // not comparison
+ String actual = evaluateQuery(String.format("process: id not eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // and comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and title eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // and not comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and title not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // or comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' or title eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // or not comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' or title not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // parenthesis not comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and not (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("process: id eq '%s' and (title eq 'test' or (title eq 'test1' and identifier eq 'test'))", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+ }
+
+ @Test
+ public void testSimpleElasticCaseQuery() {
+ // id comparison
+ String actual = evaluateQuery(String.format("case: id eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ String expected = String.format("stringId:%s", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ actual = evaluateQuery(String.format("case: id in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("stringId:(%s OR %s)", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // processId comparison
+ actual = evaluateQuery(String.format("case: processId eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("processId:%s", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ actual = evaluateQuery(String.format("case: processId in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("processId:(%s OR %s)", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // processIdentifier comparison
+ checkStringComparisonElastic("case", "processIdentifier", "processIdentifier");
+
+ // title comparison
+ checkStringComparisonElastic("case", "title", "title");
+
+ // creationDate comparison
+ checkDateComparisonElastic("case", "creationDate", "creationDateSortable");
+
+ // author comparison
+ actual = evaluateQuery("case: author eq 'test'").getFullElasticQuery();
+ expected = "author:test";
+ assert expected.equals(actual);
+
+ actual = evaluateQuery("case: author contains 'test'").getFullElasticQuery();
+ expected = "author:*test*";
+ assert expected.equals(actual);
+
+ actual = evaluateQuery(String.format("case: author in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("author:(%s OR %s)", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // places comparison
+ checkNumberComparisonElastic("case", "places.p1.marking", "places.p1.marking");
+
+ // task state comparison
+ actual = evaluateQuery("case: tasks.t1.state eq enabled").getFullElasticQuery();
+ expected = String.format("tasks.t1.state:%s", State.ENABLED);
+ assert expected.equals(actual);
+
+ actual = evaluateQuery("case: tasks.t1.state eq disabled").getFullElasticQuery();
+ expected = String.format("tasks.t1.state:%s", State.DISABLED);
+ assert expected.equals(actual);
+
+ // task userId comparison
+ actual = evaluateQuery("case: tasks.t1.userId eq 'test'").getFullElasticQuery();
+ expected = "tasks.t1.userId:test";
+ assert expected.equals(actual);
+
+ actual = evaluateQuery("case: tasks.t1.userId contains 'test'").getFullElasticQuery();
+ expected = "tasks.t1.userId:*test*";
+ assert expected.equals(actual);
+
+ actual = evaluateQuery(String.format("case: tasks.t1.userId in ('%s', '%s')", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("tasks.t1.userId:(%s OR %s)", GENERIC_OBJECT_ID, GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // data value comparison
+ checkStringComparisonElastic("case", "data.field1.value", "dataSet.field1.textValue");
+
+ checkNumberComparisonElastic("case", "data.field2.value", "dataSet.field2.numberValue");
+
+ checkDateComparisonElastic("case", "data.field3.value", "dataSet.field3.timestampValue");
+
+ actual = evaluateQuery("case: data.field1.value eq true").getFullElasticQuery();
+ expected = "dataSet.field1.booleanValue:true";
+ assert expected.equals(actual);
+
+ actual = evaluateQuery("case: data.field1.value eq false").getFullElasticQuery();
+ expected = "dataSet.field1.booleanValue:false";
+ assert expected.equals(actual);
+
+ // data options comparison
+ checkStringComparisonElastic("case", "data.field1.options", "dataSet.field1.options");
+ }
+
+ @Test
+ public void testComplexElasticCaseQuery() {
+ // not comparison
+ String actual = evaluateQuery(String.format("case: id not eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ String expected = String.format("NOT stringId:%s", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // and comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and title eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("stringId:%s AND title:test", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // and not comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and title not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("stringId:%s AND NOT title:test", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // or comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' or title eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("stringId:%s OR title:test", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // or not comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' or title not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("stringId:%s OR NOT title:test", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("stringId:%s AND (title:test OR title:test1)", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("case: id eq '%s' and (title eq 'test' or (title eq 'test1' and processIdentifier eq 'test'))", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ expected = String.format("stringId:%s AND (title:test OR (title:test1 AND processIdentifier:test))", GENERIC_OBJECT_ID);
+ assert expected.equals(actual);
+ }
+
+ @Test
+ public void testSimpleElasticTaskQuery() {
+ // elastic query should be always null
+ // id comparison
+ String actual = evaluateQuery(String.format("task: id eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // transitionId comparison
+ actual = evaluateQuery("task: transitionId eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: transitionId contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // title comparison
+ actual = evaluateQuery("task: title eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: title contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // state comparison
+ actual = evaluateQuery("task: state eq enabled").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: state eq disabled").getFullElasticQuery();
+ assert actual == null;
+
+ // userId comparison
+ actual = evaluateQuery("task: userId eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: userId contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // caseId comparison
+ actual = evaluateQuery("task: caseId eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: caseId contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // processId comparison
+ actual = evaluateQuery("task: processId eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: processId contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // lastAssign comparison
+ actual = evaluateQuery("task: lastAssign eq 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastAssign lt 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastAssign lte 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastAssign gt 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastAssign gte 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ // lastFinish comparison
+ actual = evaluateQuery("task: lastFinish eq 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastFinish lt 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastFinish lte 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastFinish gt 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("task: lastFinish gte 2011-12-03T10:15:30").getFullElasticQuery();
+ assert actual == null;
+ }
+
+ @Test
+ public void testComplexElasticTaskQuery() {
+ // elastic query should be always null
+ // not comparison
+ String actual = evaluateQuery(String.format("task: id not eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // and comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and title eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // and not comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and title not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // or comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' or title eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // or not comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' or title not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // parenthesis not comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and not (title eq 'test' or title eq 'test1')", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("task: id eq '%s' and (title eq 'test' or (title eq 'test1' and processId eq 'test'))", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+ }
+
+ @Test
+ public void testSimpleElasticUserQuery() {
+ // elastic query should be always null
+ // id comparison
+ String actual = evaluateQuery(String.format("user: id eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // name comparison
+ actual = evaluateQuery("user: name eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("user: name contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // surname comparison
+ actual = evaluateQuery("user: surname eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("user: surname contains 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ // email comparison
+ actual = evaluateQuery("user: email eq 'test'").getFullElasticQuery();
+ assert actual == null;
+
+ actual = evaluateQuery("user: email contains 'test'").getFullElasticQuery();
+ assert actual == null;
+ }
+
+ @Test
+ public void testComplexElasticUserQuery() {
+ // elastic query should be always null
+ // not comparison
+ String actual = evaluateQuery(String.format("user: id not eq '%s'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // and comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and email eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // and not comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and email not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // or comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' or email eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // or not comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' or email not eq 'test'", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // parenthesis comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and (email eq 'test' or email eq 'test1')", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // parenthesis not comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and not (email eq 'test' or email eq 'test1')", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+
+ // nested parenthesis comparison
+ actual = evaluateQuery(String.format("user: id eq '%s' and (email eq 'test' or (email eq 'test1' and name eq 'test'))", GENERIC_OBJECT_ID)).getFullElasticQuery();
+ assert actual == null;
+ }
+
+ @Test
+ public void testPagingQuery() {
+ // default
+ Pageable pageable = evaluateQuery("cases: processIdentifier eq 'test'").getPageable();
+ assert pageable.getPageNumber() == 0;
+ assert pageable.getPageSize() == 20;
+
+ // page number only
+ pageable = evaluateQuery("cases: processIdentifier eq 'test' page 2").getPageable();
+ assert pageable.getPageNumber() == 2;
+ assert pageable.getPageSize() == 20;
+
+ // page number and page size
+ pageable = evaluateQuery("cases: processIdentifier eq 'test' page 2 size 4").getPageable();
+ assert pageable.getPageNumber() == 2;
+ assert pageable.getPageSize() == 4;
+ }
+
+ @Test
+ public void testProcessSortingQuery() {
+ // default (no sort)
+ Pageable actual = evaluateQuery("processes: identifier eq 'test'").getPageable();
+ assert !actual.getSort().isSorted();
+
+ // default ordering asc
+ actual = evaluateQuery("processes: identifier eq 'test' sort by id").getPageable();
+ assert actual.getSort().isSorted();
+ List orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+
+ // set ordering
+ actual = evaluateQuery("processes: identifier eq 'test' sort by id desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("processes: identifier eq 'test' sort by identifier desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("identifier");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("processes: identifier eq 'test' sort by title asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("title.defaultValue");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("processes: identifier eq 'test' sort by version asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("version");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("processes: identifier eq 'test' sort by creationDate asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("creationDate");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ // complex set ordering
+ actual = evaluateQuery("processes: identifier eq 'test' sort by id asc, title desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title.defaultValue");
+ assert orders.get(1).getDirection().equals(Sort.Direction.DESC);
+
+ // complex default ordering
+ actual = evaluateQuery("processes: identifier eq 'test' sort by id asc, title").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title.defaultValue");
+ assert orders.get(1).getDirection().equals(Sort.Direction.ASC);
+ }
+
+ @Test
+ public void testCaseSortingMongoDbQuery() {
+ // default (no sort)
+ Pageable actual = evaluateQuery("cases: processIdentifier eq 'test'").getPageable();
+ assert !actual.getSort().isSorted();
+
+ // default ordering asc
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by id").getPageable();
+ assert actual.getSort().isSorted();
+ List orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+
+ // set ordering
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by id desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by processIdentifier desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("processIdentifier");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by title asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("title");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by processId asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("petriNetObjectId");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by creationDate asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("creationDate");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by author desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("author.id");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ // complex set ordering
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by id asc, title desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title");
+ assert orders.get(1).getDirection().equals(Sort.Direction.DESC);
+
+ // complex default ordering
+ actual = evaluateQuery("cases: processIdentifier eq 'test' sort by id asc, title").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title");
+ assert orders.get(1).getDirection().equals(Sort.Direction.ASC);
+ }
+
+ @Test
+ public void testCaseSortingElasticQuery() {
+ // default (no sort)
+ Pageable actual = evaluateQuery("cases: data.field1.value eq 'test'").getPageable();
+ assert !actual.getSort().isSorted();
+
+ // default ordering asc
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by id").getPageable();
+ assert actual.getSort().isSorted();
+ List orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("stringId.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+
+ // set ordering
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by id desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("stringId.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by processIdentifier desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("processIdentifier.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by title asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("title.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by processId asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("processId.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by creationDate asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("creationDateSortable");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by author desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("author.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by places.p1.marking desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("places.p1.marking");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by tasks.t1.state desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("tasks.t1.state.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by tasks.t1.userId desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("tasks.t1.userId.keyword");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ // complex set ordering
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by id asc, title desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("stringId.keyword");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title.keyword");
+ assert orders.get(1).getDirection().equals(Sort.Direction.DESC);
+
+ // complex default ordering
+ actual = evaluateQuery("cases: data.field1.value eq 'test' sort by id asc, title").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("stringId.keyword");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title.keyword");
+ assert orders.get(1).getDirection().equals(Sort.Direction.ASC);
+ }
+
+ @Test
+ public void testTaskSortingQuery() {
+ // default (no sort)
+ Pageable actual = evaluateQuery("tasks: title eq 'test'").getPageable();
+ assert !actual.getSort().isSorted();
+
+ // default ordering asc
+ actual = evaluateQuery("tasks: title eq 'test' sort by id").getPageable();
+ assert actual.getSort().isSorted();
+ List orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+
+ // set ordering
+ actual = evaluateQuery("tasks: title eq 'test' sort by id desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("tasks: title eq 'test' sort by transitionId desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("transitionId");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("tasks: title eq 'test' sort by title asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("title.defaultValue");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("tasks: title eq 'test' sort by processId asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("processId");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("tasks: title eq 'test' sort by caseId asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("caseId");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("tasks: title eq 'test' sort by userId asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("userId");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("tasks: title eq 'test' sort by lastAssign asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("lastAssigned");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+
+ actual = evaluateQuery("tasks: title eq 'test' sort by lastFinish desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("lastFinished");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ // complex set ordering
+ actual = evaluateQuery("tasks: title eq 'test' sort by id asc, title desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title.defaultValue");
+ assert orders.get(1).getDirection().equals(Sort.Direction.DESC);
+
+ // complex default ordering
+ actual = evaluateQuery("tasks: title eq 'test' sort by id asc, title").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("title.defaultValue");
+ assert orders.get(1).getDirection().equals(Sort.Direction.ASC);
+ }
+
+ @Test
+ public void testUserSortingQuery() {
+ // default (no sort)
+ Pageable actual = evaluateQuery("users: name eq 'test'").getPageable();
+ assert !actual.getSort().isSorted();
+
+ // default ordering asc
+ actual = evaluateQuery("users: name eq 'test' sort by id").getPageable();
+ assert actual.getSort().isSorted();
+ List orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+
+ // set ordering
+ actual = evaluateQuery("users: name eq 'test' sort by id desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("users: name eq 'test' sort by name desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("name");
+ assert orders.get(0).getDirection() == Sort.Direction.DESC;
+
+ actual = evaluateQuery("users: name eq 'test' sort by surname asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("surname");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ actual = evaluateQuery("users: name eq 'test' sort by email asc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 1;
+ assert orders.get(0).getProperty().equals("email");
+ assert orders.get(0).getDirection() == Sort.Direction.ASC;
+
+ // complex set ordering
+ actual = evaluateQuery("users: name eq 'test' sort by id asc, name desc").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("name");
+ assert orders.get(1).getDirection().equals(Sort.Direction.DESC);
+
+ // complex default ordering
+ actual = evaluateQuery("users: name eq 'test' sort by id asc, name").getPageable();
+ assert actual.getSort().isSorted();
+ orders = actual.getSort().toList();
+ assert orders.size() == 2;
+ assert orders.get(0).getProperty().equals("id");
+ assert orders.get(0).getDirection().equals(Sort.Direction.ASC);
+ assert orders.get(1).getProperty().equals("name");
+ assert orders.get(1).getDirection().equals(Sort.Direction.ASC);
+ }
+
+ @Test
+ public void testProcessQueriesFail() {
+ // using case, task, user attributes
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: processId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: processIdentifier eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: author eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: places.p1.marking eq 1"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: tasks.t1.state eq enabled"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: tasks.t1.userId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: data.field1.value eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: data.field1.options contains 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: transitionId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: state eq enabled"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: userId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: caseId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: lastAssign eq 2020-03-03"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: lastFinish eq 2020-03-03"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: name eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: surname eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("process: email eq 'test'"));
+ }
+
+ @Test
+ public void testCaseQueriesFail() {
+ // using process, task, user attributes
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: identifier eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: version eq 1.1.1"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: transitionId eq 1"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: state eq enabled"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: userId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: caseId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: lastAssign eq 2020-03-03"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: lastFinish eq 2020-03-03"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: name eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: surname eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: email eq 'test'"));
+ }
+
+ @Test
+ public void testTaskQueriesFail() {
+ // using process, case, user attributes
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: identifier eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: version eq 1.1.1"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: creationDate eq 2020-03-03"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: processIdentifier eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: places.p1.marking eq 1"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: tasks.t1.state eq enabled"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: tasks.t1.userId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: data.field1.value eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: data.field1.options contains 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: name eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: surname eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("task: email eq 'test'"));
+ }
+
+ @Test
+ public void testUserQueriesFail() {
+ // using process, case, task attributes
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: identifier eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: version eq 1.1.1"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: creationDate eq 2020-03-03"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: processId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: processIdentifier eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: author eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: places.p1.marking eq 1"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: tasks.t1.state eq enabled"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: tasks.t1.userId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: data.field1.value eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: data.field1.options contains 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: transitionId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: state eq enabled"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: userId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: caseId eq 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: lastAssign eq 2020-03-03"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("user: lastFinish eq 2020-03-03"));
+ }
+
+ @Test
+ public void testComparisonTypeFail() {
+ // id comparison
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: id contains 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: id lt 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: id lte 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: id gt 'test'"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: id gte 'test'"));
+
+ // number comparison
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: places.p1.marking contains 1"));
+
+ // date/datetime comparison
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: creationDate contains 2020-03-03"));
+
+ // boolean comparison
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: data.field1.value contains true"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: data.field1.value lt true"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: data.field1.value lte true"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: data.field1.value gt true"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> evaluateQuery("case: data.field1.value gte true"));
+ }
+
+ private static void checkStringComparison(MongoDbUtils> mongoDbUtils, String resource, String attribute, StringPath stringPath) {
+ Predicate actual = evaluateQuery(String.format("%s: %s eq 'test'", resource, attribute)).getFullMongoQuery();
+ Predicate expected = stringPath.eq("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s contains 'test'", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.contains("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s lt 'test'", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.lt("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s lte 'test'", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.loe("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s gt 'test'", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.gt("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s gte 'test'", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.goe("test");
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in ('test1', 'test2', 'test3')", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.in(List.of("test1", "test2", "test3"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in ('test1', 'test2', 'test3')", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.in(List.of("test1", "test2", "test3")).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in ('test1' : 'test2')", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.gt("test1").and(stringPath.lt("test2"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in ['test1' : 'test2']", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.goe("test1").and(stringPath.loe("test2"));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in ('test1' : 'test2']", resource, attribute)).getFullMongoQuery();
+ expected = stringPath.gt("test1").and(stringPath.loe("test2")).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+ }
+
+ private static void checkDateComparison(MongoDbUtils> mongoDbUtils, String resource, String attribute, DateTimePath dateTimePath) {
+ LocalDateTime date1 = LocalDateTime.of(2011, 12, 3, 10, 15, 30);
+ LocalDateTime date2 = LocalDateTime.of(2011, 12, 3, 11, 15, 30);
+ LocalDateTime date3 = LocalDateTime.of(2011, 12, 3, 12, 15, 30);
+ LocalDateTime date4 = LocalDateTime.of(2011, 12, 3, 12, 0, 0);
+ LocalDateTime date5 = LocalDateTime.of(2011, 12, 3, 12, 0, 0);
+ LocalDateTime date6 = LocalDateTime.of(2011, 12, 3, 12, 0, 0);
+
+ Predicate actual = evaluateQuery(String.format("%s: %s eq 2011-12-03T10:15:30", resource, attribute)).getFullMongoQuery();
+ Predicate expected = dateTimePath.eq(LocalDateTime.of(2011, 12, 3, 10, 15, 30));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s lt 2011-12-03T10:15:30", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.lt(LocalDateTime.of(2011, 12, 3, 10, 15, 30));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s lte 2011-12-03T10:15:30", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.loe(LocalDateTime.of(2011, 12, 3, 10, 15, 30));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s gt 2011-12-03T10:15:30", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.gt(LocalDateTime.of(2011, 12, 3, 10, 15, 30));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s gte 2011-12-03T10:15:30", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.goe(LocalDateTime.of(2011, 12, 3, 10, 15, 30));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03T10:15:30, 2011-12-03T11:15:30, 2011-12-03T12:15:30)", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.in(List.of(date1, date2, date3));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03T10:15:30, 2011-12-03T11:15:30, 2011-12-03T12:15:30)", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.in(List.of(date1, date2, date3)).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03T10:15:30 : 2011-12-03T11:15:30)", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.gt(date1).and(dateTimePath.lt(date2));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in [2011-12-03T10:15:30 : 2011-12-03T11:15:30]", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.goe(date1).and(dateTimePath.loe(date2));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03T10:15:30 : 2011-12-03T11:15:30]", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.gt(date1).and(dateTimePath.loe(date2)).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03, 2011-12-03, 2011-12-03)", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.in(List.of(date4, date5, date6));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03, 2011-12-03, 2011-12-03)", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.in(List.of(date4, date5, date6)).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03 : 2011-12-03)", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.gt(date4).and(dateTimePath.lt(date5));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s in [2011-12-03 : 2011-12-03]", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.goe(date4).and(dateTimePath.loe(date5));
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03 : 2011-12-03]", resource, attribute)).getFullMongoQuery();
+ expected = dateTimePath.gt(date4).and(dateTimePath.loe(date5)).not();
+
+ compareMongoQueries(mongoDbUtils, actual, expected);
+ }
+
+ private static void checkStringComparisonElastic(String resource, String attribute, String resultAttribute) {
+ String actual = evaluateQuery(String.format("%s: %s eq 'test'", resource, attribute)).getFullElasticQuery();
+ String expected = String.format("%s:test", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s contains 'test'", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:*test*", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s lt 'test'", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:test", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s gte 'test'", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:>=test", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in ('test1', 'test2', 'test3')", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:(test1 OR test2 OR test3)", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in ('test1', 'test2', 'test3')", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT %s:(test1 OR test2 OR test3)", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in ('test1' : 'test2')", resource, attribute)).getFullElasticQuery();
+ expected = String.format("(%s:>test1 AND %s:=test1 AND %s:<=test2)", resultAttribute, resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in ('test1' : 'test2']", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT (%s:>test1 AND %s:<=test2)", resultAttribute, resultAttribute);
+
+ assert actual.equals(expected);
+ }
+
+ private static void checkNumberComparisonElastic(String resource, String attribute, String resultAttribute) {
+ String actual = evaluateQuery(String.format("%s: %s eq 1", resource, attribute)).getFullElasticQuery();
+ String expected = String.format("%s:1", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s lt 1", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:<1", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s lte 1", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:<=1", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s gt 1", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:>1", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s gte 1", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:>=1", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (1, 2, 3)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:(1 OR 2 OR 3)", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (1, 2, 3)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT %s:(1 OR 2 OR 3)", resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (1 : 2)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("(%s:>1 AND %s:<2)", resultAttribute, resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in [1 : 2]", resource, attribute)).getFullElasticQuery();
+ expected = String.format("(%s:>=1 AND %s:<=2)", resultAttribute, resultAttribute);
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (1 : 2]", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT (%s:>1 AND %s:<=2)", resultAttribute, resultAttribute);
+
+ assert actual.equals(expected);
+ }
+
+ private static void checkDateComparisonElastic(String resource, String attribute, String resultAttribute) {
+ LocalDateTime date1 = LocalDateTime.of(2011, 12, 3, 10, 15, 30);
+ LocalDateTime date2 = LocalDateTime.of(2011, 12, 3, 11, 15, 30);
+ LocalDateTime date3 = LocalDateTime.of(2011, 12, 3, 12, 15, 30);
+ LocalDateTime date4 = LocalDateTime.of(2011, 12, 3, 12, 0, 0);
+ LocalDateTime date5 = LocalDateTime.of(2011, 12, 3, 12, 0, 0);
+ LocalDateTime date6 = LocalDateTime.of(2011, 12, 3, 12, 0, 0);
+
+ String actual = evaluateQuery(String.format("%s: %s eq 2011-12-03T10:15:30", resource, attribute)).getFullElasticQuery();
+ String expected = String.format("%s:%s", resultAttribute, Timestamp.valueOf(date1).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s lt 2011-12-03T10:15:30", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:<%s", resultAttribute, Timestamp.valueOf(date1).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s lte 2011-12-03T10:15:30", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:<=%s", resultAttribute, Timestamp.valueOf(date1).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s gt 2011-12-03T10:15:30", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:>%s", resultAttribute, Timestamp.valueOf(date1).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s gte 2011-12-03T10:15:30", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:>=%s", resultAttribute, Timestamp.valueOf(date1).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03T10:15:30, 2011-12-03T11:15:30, 2011-12-03T12:15:30)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:(%s OR %s OR %s)", resultAttribute, Timestamp.valueOf(date1).getTime(), Timestamp.valueOf(date2).getTime(), Timestamp.valueOf(date3).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03T10:15:30, 2011-12-03T11:15:30, 2011-12-03T12:15:30)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT %s:(%s OR %s OR %s)", resultAttribute, Timestamp.valueOf(date1).getTime(), Timestamp.valueOf(date2).getTime(), Timestamp.valueOf(date3).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03T10:15:30 : 2011-12-03T11:15:30)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("(%s:>%s AND %s:<%s)", resultAttribute, Timestamp.valueOf(date1).getTime(), resultAttribute, Timestamp.valueOf(date2).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in [2011-12-03T10:15:30 : 2011-12-03T11:15:30]", resource, attribute)).getFullElasticQuery();
+ expected = String.format("(%s:>=%s AND %s:<=%s)", resultAttribute, Timestamp.valueOf(date1).getTime(), resultAttribute, Timestamp.valueOf(date2).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03T10:15:30 : 2011-12-03T11:15:30]", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT (%s:>%s AND %s:<=%s)", resultAttribute, Timestamp.valueOf(date1).getTime(), resultAttribute, Timestamp.valueOf(date2).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03, 2011-12-03, 2011-12-03)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("%s:(%s OR %s OR %s)", resultAttribute, Timestamp.valueOf(date4).getTime(), Timestamp.valueOf(date5).getTime(), Timestamp.valueOf(date6).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03, 2011-12-03, 2011-12-03)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT %s:(%s OR %s OR %s)", resultAttribute, Timestamp.valueOf(date4).getTime(), Timestamp.valueOf(date5).getTime(), Timestamp.valueOf(date6).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in (2011-12-03 : 2011-12-03)", resource, attribute)).getFullElasticQuery();
+ expected = String.format("(%s:>%s AND %s:<%s)", resultAttribute, Timestamp.valueOf(date4).getTime(), resultAttribute, Timestamp.valueOf(date5).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s in [2011-12-03 : 2011-12-03]", resource, attribute)).getFullElasticQuery();
+ expected = String.format("(%s:>=%s AND %s:<=%s)", resultAttribute, Timestamp.valueOf(date4).getTime(), resultAttribute, Timestamp.valueOf(date5).getTime());
+
+ assert actual.equals(expected);
+
+ actual = evaluateQuery(String.format("%s: %s not in (2011-12-03 : 2011-12-03]", resource, attribute)).getFullElasticQuery();
+ expected = String.format("NOT (%s:>%s AND %s:<=%s)", resultAttribute, Timestamp.valueOf(date4).getTime(), resultAttribute, Timestamp.valueOf(date5).getTime());
+
+ assert actual.equals(expected);
+ }
+
+ private static void compareMongoQueries(MongoDbUtils> mongoDbUtils, Predicate actual, Predicate expected) {
+ Document actualDocument = mongoDbUtils.convertPredicateToDocument(actual);
+ Document expectedDocument = mongoDbUtils.convertPredicateToDocument(expected);
+
+ assert actualDocument.equals(expectedDocument);
+ }
+}
diff --git a/src/test/java/com/netgrif/application/engine/search/SearchCaseTest.java b/src/test/java/com/netgrif/application/engine/search/SearchCaseTest.java
new file mode 100644
index 00000000000..00f7b23399d
--- /dev/null
+++ b/src/test/java/com/netgrif/application/engine/search/SearchCaseTest.java
@@ -0,0 +1,578 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.TestHelper;
+import com.netgrif.application.engine.auth.domain.Authority;
+import com.netgrif.application.engine.auth.domain.IUser;
+import com.netgrif.application.engine.auth.domain.User;
+import com.netgrif.application.engine.petrinet.domain.I18nString;
+import com.netgrif.application.engine.petrinet.domain.PetriNet;
+import com.netgrif.application.engine.petrinet.domain.dataset.*;
+import com.netgrif.application.engine.petrinet.domain.roles.ProcessRole;
+import com.netgrif.application.engine.search.interfaces.ISearchService;
+import com.netgrif.application.engine.search.utils.SearchUtils;
+import com.netgrif.application.engine.startup.ImportHelper;
+import com.netgrif.application.engine.workflow.domain.Case;
+import com.netgrif.application.engine.workflow.web.responsebodies.DataSet;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.netgrif.application.engine.search.utils.SearchTestUtils.*;
+import static com.netgrif.application.engine.search.utils.SearchUtils.toDateString;
+import static com.netgrif.application.engine.search.utils.SearchUtils.toDateTimeString;
+
+@Slf4j
+@SpringBootTest
+@ActiveProfiles({"test"})
+@ExtendWith(SpringExtension.class)
+public class SearchCaseTest {
+
+ public static final String TEST_TRANSITION_ID = "search_test_t1";
+
+ @Autowired
+ private ImportHelper importHelper;
+
+ @Autowired
+ private TestHelper testHelper;
+
+ @Autowired
+ private ISearchService searchService;
+
+ private Map auths;
+
+ @BeforeEach
+ void setup() {
+ testHelper.truncateDbs();
+ auths = importHelper.createAuthorities(Map.of("user", Authority.user, "admin", Authority.admin));
+ }
+
+ private PetriNet importPetriNet(String fileName) {
+ PetriNet testNet = importHelper.createNet(fileName).orElse(null);
+ assert testNet != null;
+ return testNet;
+ }
+
+ private IUser createUser(String name, String surname, String email) {
+ User user = new User(email, "password", name, surname);
+ Authority[] authorities = new Authority[]{auths.get("user")};
+ ProcessRole[] processRoles = new ProcessRole[]{};
+ return importHelper.createUser(user, authorities, processRoles);
+ }
+
+ private void searchAndCompare(String query, Case expectedResult) {
+ long count = searchService.count(query);
+ assert count == 1;
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, Case.class), expectedResult, Case::getStringId);
+ }
+
+ private void searchAndCompare(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, Case.class), expected, Case::getStringId);
+ }
+
+ private void searchAndCompareAsList(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObjectList(actual, Case.class), expected, Case::getStringId);
+ }
+
+ private void searchAndCompareAsListInOrder(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareByIdInOrder(convertToObjectList(actual, Case.class), expected, Case::getStringId);
+ }
+
+ @Test
+ public void testSearchById() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test2", net);
+
+ String query = String.format("case: id eq '%s'", case1.getStringId());
+
+ searchAndCompare(query, case1);
+
+ // in list
+ String queryInList = String.format("cases: id in ('%s', '%s')", case1.getStringId(), case2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(case1, case2));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier eq '%s' sort by id", net.getIdentifier());
+ String querySort2 = String.format("cases: processIdentifier eq '%s' sort by id desc", net.getIdentifier());
+
+ searchAndCompareAsListInOrder(querySort, List.of(case1, case2));
+ searchAndCompareAsListInOrder(querySort2, List.of(case2, case1));
+ }
+
+ @Test
+ public void testSearchByProcessId() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ PetriNet net2 = importPetriNet("search/search_test2.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test2", net2);
+
+ String queryEq = String.format("case: processId eq '%s'", net.getStringId());
+ String queryOther = String.format("case: processId eq '%s'", net2.getStringId());
+ String queryMore = String.format("cases: processId eq '%s'", net.getStringId());
+
+ searchAndCompare(queryEq, List.of(case1, case2));
+ searchAndCompare(queryOther, case3);
+ searchAndCompareAsList(queryMore, List.of(case1, case2));
+
+ // in list
+ String queryInList = String.format("cases: processId in ('%s', '%s')", net.getStringId(), net2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(case1, case2, case3));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier in ('%s', '%s') sort by processId", net.getIdentifier(), net2.getIdentifier());
+ String querySort2 = String.format("cases: processIdentifier in ('%s', '%s') sort by processId desc", net.getIdentifier(), net2.getIdentifier());
+
+ searchAndCompareAsListInOrder(querySort, List.of(case1, case2, case3));
+ searchAndCompareAsListInOrder(querySort2, List.of(case3, case1, case2));
+ }
+
+ @Test
+ public void testSearchByProcessIdentifier() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ PetriNet net2 = importPetriNet("search/search_test2.xml");
+ PetriNet net3 = importPetriNet("search/search_test3.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test2", net2);
+ Case case4 = importHelper.createCase("Search Test3", net3);
+
+ String query = String.format("case: processIdentifier eq '%s'", net.getIdentifier());
+ String queryOther = String.format("case: processIdentifier eq '%s'", net2.getIdentifier());
+ String queryMore = String.format("cases: processIdentifier eq '%s'", net.getIdentifier());
+
+ searchAndCompare(query, List.of(case1, case2));
+ searchAndCompare(queryOther, case3);
+ searchAndCompareAsList(queryMore, List.of(case1, case2));
+
+ // in list
+ String queryInList = String.format("cases: processIdentifier in ('%s', '%s', '%s')", net.getIdentifier(), net2.getIdentifier(), net3.getIdentifier());
+ searchAndCompareAsList(queryInList, List.of(case1, case2, case3, case4));
+
+ // in range
+ String queryInRange = String.format("cases: processIdentifier in ['%s' : '%s')", net.getIdentifier(), net3.getIdentifier());
+ searchAndCompareAsList(queryInRange, List.of(case1, case2, case3));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier in ('%s', '%s') sort by processIdentifier", net.getIdentifier(), net2.getIdentifier());
+ String querySort2 = String.format("cases: processIdentifier in ('%s', '%s') sort by processIdentifier desc", net.getIdentifier(), net2.getIdentifier());
+
+ searchAndCompareAsListInOrder(querySort, List.of(case1, case2, case3));
+ searchAndCompareAsListInOrder(querySort2, List.of(case3, case1, case2));
+ }
+
+ @Test
+ public void testSearchByTitle() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test2", net);
+
+ String query = String.format("case: title eq '%s'", case1.getTitle());
+ String queryOther = String.format("case: title eq '%s'", case3.getTitle());
+ String queryMore = String.format("cases: title eq '%s'", case1.getTitle());
+
+ searchAndCompare(query, List.of(case1, case2));
+ searchAndCompare(queryOther, case3);
+ searchAndCompareAsList(queryMore, List.of(case1, case2));
+
+ // in list
+ String queryInList = String.format("cases: processIdentifier eq '%s' and title in ('%s', '%s')", net.getIdentifier(), case1.getTitle(), case3.getTitle());
+ searchAndCompareAsList(queryInList, List.of(case1, case2, case3));
+
+ // in range
+ String queryInRange = String.format("cases: processIdentifier eq '%s' and title in ['%s' : '%s')", net.getIdentifier(), case1.getTitle(), case3.getTitle());
+ searchAndCompareAsList(queryInRange, List.of(case1, case2));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier eq '%s' sort by title", net.getIdentifier());
+ String querySort2 = String.format("cases: processIdentifier eq '%s' sort by title desc", net.getIdentifier());
+
+ searchAndCompareAsListInOrder(querySort, List.of(case1, case2, case3));
+ searchAndCompareAsListInOrder(querySort2, List.of(case3, case1, case2));
+ }
+
+ @Test
+ public void testSearchByCreationDate() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test", net);
+
+ String queryEq = String.format("case: creationDate eq %s", toDateTimeString(case1.getCreationDate()));
+ String queryLt = String.format("cases: processIdentifier eq '%s' and creationDate lt %s", net.getIdentifier(), toDateTimeString(case3.getCreationDate()));
+ String queryLte = String.format("cases: processIdentifier eq '%s' and creationDate lte %s", net.getIdentifier(), toDateTimeString(case3.getCreationDate()));
+ String queryGt = String.format("cases: processIdentifier eq '%s' and creationDate gt %s", net.getIdentifier(), toDateTimeString(case1.getCreationDate()));
+ String queryGte = String.format("cases: processIdentifier eq '%s' and creationDate gte %s", net.getIdentifier(), toDateTimeString(case1.getCreationDate()));
+
+ searchAndCompare(queryEq, case1);
+ searchAndCompareAsList(queryLt, List.of(case1, case2));
+ searchAndCompareAsList(queryLte, List.of(case1, case2, case3));
+ searchAndCompareAsList(queryGt, List.of(case2, case3));
+ searchAndCompareAsList(queryGte, List.of(case1, case2, case3));
+
+ // in list
+ String queryInList = String.format("cases: processIdentifier eq '%s' and creationDate in (%s, %s)", net.getIdentifier(), toDateTimeString(case1.getCreationDate()), toDateTimeString(case3.getCreationDate()));
+ searchAndCompareAsList(queryInList, List.of(case1, case3));
+
+ // in range
+ String queryInRange = String.format("cases: processIdentifier eq '%s' and creationDate in [%s : %s)", net.getIdentifier(), toDateTimeString(case1.getCreationDate()), toDateTimeString(case3.getCreationDate()));
+ searchAndCompareAsList(queryInRange, List.of(case1, case2));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier eq '%s' sort by creationDate", net.getIdentifier());
+ String querySort2 = String.format("cases: processIdentifier eq '%s' sort by creationDate desc", net.getIdentifier());
+
+ searchAndCompareAsListInOrder(querySort, List.of(case1, case2, case3));
+ searchAndCompareAsListInOrder(querySort2, List.of(case3, case2, case1));
+ }
+
+ @Test
+ public void testSearchByAuthor() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ IUser user2 = createUser("Name2", "Surname2", "Email2");
+ Case case1 = importHelper.createCase("Search Test", net, user1.transformToLoggedUser());
+ Case case2 = importHelper.createCase("Search Test", net, user1.transformToLoggedUser());
+ Case case3 = importHelper.createCase("Search Test2", net, user2.transformToLoggedUser());
+
+ String query = String.format("case: processIdentifier eq '%s' and author eq '%s'", net.getIdentifier(), user1.getStringId());
+ String queryOther = String.format("case: processIdentifier eq '%s' and author eq '%s'", net.getIdentifier(), user2.getStringId());
+ String queryMore = String.format("cases: processIdentifier eq '%s' and author eq '%s'", net.getIdentifier(), user1.getStringId());
+
+ searchAndCompare(query, List.of(case1, case2));
+ searchAndCompare(queryOther, case3);
+ searchAndCompareAsList(queryMore, List.of(case1, case2));
+
+ // in list
+ String queryInList = String.format("cases: author in ('%s', '%s')", user1.getStringId(), user2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(case1, case2, case3));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier eq '%s' sort by author", net.getIdentifier());
+ String querySort2 = String.format("cases: processIdentifier eq '%s' sort by author desc", net.getIdentifier());
+
+ searchAndCompareAsListInOrder(querySort, List.of(case1, case2, case3));
+ searchAndCompareAsListInOrder(querySort2, List.of(case3, case1, case2));
+ }
+
+ @Test
+ public void testSearchByPlaces() throws InterruptedException {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ Case case1 = importHelper.createCase("Search Test1", net);
+ Case case2 = importHelper.createCase("Search Test2", net);
+ Case case3 = importHelper.createCase("Search Test3", net);
+
+ importHelper.assignTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.finishTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ importHelper.finishTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+
+ String query = String.format("case: processIdentifier eq '%s' AND places.p2.marking eq %s", net.getIdentifier(), 1);
+ String queryOther = String.format("case: processIdentifier eq '%s' AND places.p1.marking eq %s", net.getIdentifier(), 1);
+ String queryMore = String.format("cases: processIdentifier eq '%s' AND places.p2.marking eq %s", net.getIdentifier(), 1);
+
+ Thread.sleep(3000);
+
+ searchAndCompare(query, List.of(case1, case2));
+ searchAndCompare(queryOther, case3);
+ searchAndCompareAsList(queryMore, List.of(case1, case2));
+
+ // in list
+ String queryInList = String.format("cases: processIdentifier eq '%s' and places.p1.marking in (1)", net.getIdentifier());
+ searchAndCompareAsList(queryInList, List.of(case3));
+
+ // in range
+ String queryInRange = String.format("cases: processIdentifier eq '%s' and places.p1.marking in [1 : 100]", net.getIdentifier());
+ searchAndCompareAsList(queryInRange, List.of(case3));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier eq '%s' sort by places.p2.marking", net.getIdentifier());
+ String querySort2 = String.format("cases: processIdentifier eq '%s' sort by places.p2.marking desc", net.getIdentifier());
+
+ searchAndCompareAsListInOrder(querySort, List.of(case1, case2, case3));
+ searchAndCompareAsListInOrder(querySort2, List.of(case1, case2, case3));
+ }
+
+ @Test
+ public void testSearchByTaskState() throws InterruptedException { // todo NAE-1997: not indexing tasks.state
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test2", net);
+
+ importHelper.assignTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.finishTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ importHelper.finishTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+
+ String query = String.format("case: tasks.%s.state eq %s", TEST_TRANSITION_ID, "disabled");
+ String queryOther = String.format("case: tasks.%s.state eq %s", TEST_TRANSITION_ID, "enabled");
+ String queryMore = String.format("cases: tasks.%s.state eq %s", TEST_TRANSITION_ID, "disabled");
+
+ Thread.sleep(3000);
+
+ searchAndCompare(query, List.of(case1, case2));
+ searchAndCompare(queryOther, case3);
+ searchAndCompareAsList(queryMore, List.of(case1, case2));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier eq '%s' sort by tasks.%s.state", net.getIdentifier(), TEST_TRANSITION_ID);
+ String querySort2 = String.format("cases: processIdentifier eq '%s' sort by tasks.%s.state desc", net.getIdentifier(), TEST_TRANSITION_ID);
+
+ searchAndCompareAsListInOrder(querySort, List.of(case3, case1, case2));
+ searchAndCompareAsListInOrder(querySort2, List.of(case1, case2, case3));
+ }
+
+ @Test
+ public void testSearchByTaskUserId() { // todo NAE-1997: not indexing tasks.userId
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ IUser user2 = createUser("Name2", "Surname2", "Email2");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test2", net);
+
+ importHelper.assignTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case3.getStringId(), user2.transformToLoggedUser());
+
+ String query = String.format("case: tasks.%s.userId eq '%s'", TEST_TRANSITION_ID, user1.getStringId());
+ String queryOther = String.format("case: tasks.%s.userId eq '%s'", TEST_TRANSITION_ID, user2.getStringId());
+ String queryMore = String.format("cases: tasks.%s.userId eq '%s'", TEST_TRANSITION_ID, user1.getStringId());
+
+ searchAndCompare(query, List.of(case1, case2));
+ searchAndCompare(queryOther, case3);
+ searchAndCompareAsList(queryMore, List.of(case1, case2));
+
+ // in list
+ String queryInList = String.format("cases: tasks.%s.userId in ('%s', '%s')", TEST_TRANSITION_ID, user1.getStringId(), user2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(case1, case2, case3));
+
+ // sort
+ String querySort = String.format("cases: processIdentifier eq '%s' sort by tasks.%s.userId", net.getIdentifier(), TEST_TRANSITION_ID);
+ String querySort2 = String.format("cases: processIdentifier eq '%s' sort by tasks.%s.userId desc", net.getIdentifier(), TEST_TRANSITION_ID);
+
+ searchAndCompareAsListInOrder(querySort, List.of(case3, case1, case2));
+ searchAndCompareAsListInOrder(querySort2, List.of(case1, case2, case3));
+ }
+
+ @Test
+ public void testSearchByDataValue() throws InterruptedException {
+ PetriNet net = importPetriNet("search/search_test.xml");
+
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test2", net);
+
+ BooleanField booleanFalse = new BooleanField();
+ booleanFalse.setRawValue(false);
+ TextField textField = new TextField();
+ textField.setRawValue("other");
+ Map options = Map.of("test1", new I18nString("Test1"), "test2", new I18nString("Test2"), "test3", new I18nString("Test3"));
+ EnumerationMapField enumerationMapField = new EnumerationMapField();
+ enumerationMapField.setOptions(options);
+ enumerationMapField.setRawValue("test1");
+ MultichoiceMapField multichoiceMapField = new MultichoiceMapField();
+ multichoiceMapField.setOptions(options);
+ multichoiceMapField.setRawValue(Set.of("test1", "test2"));
+ NumberField numberField = new NumberField();
+ numberField.setRawValue(2.0);
+ DateField dateField = new DateField();
+ dateField.setRawValue(LocalDate.now().minusDays(5));
+ DateTimeField dateTimeField = new DateTimeField();
+ dateTimeField.setRawValue(LocalDateTime.now().minusDays(5));
+
+ importHelper.assignTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ importHelper.setTaskData("Test", case2.getStringId(), new DataSet(Map.of(
+ "boolean_immediate", booleanFalse,
+ "text_immediate", textField,
+ "number_immediate", numberField,
+ "multichoice_map_immediate", multichoiceMapField,
+ "enumeration_map_immediate", enumerationMapField,
+ "date_immediate", dateField,
+ "date_time_immediate", dateTimeField
+ )));
+ case2 = importHelper.finishTask("Test", case2.getStringId(), user1.transformToLoggedUser()).getCase();
+
+ String queryTextEq = String.format("case: data.text_immediate.value eq %s", "'test'");
+ String queryTextContains = String.format("case: data.text_immediate.value contains %s", "'es'");
+ String queryBoolean = String.format("case: data.boolean_immediate.value eq %s", "true");
+ String queryEnumerationEq = String.format("case: data.enumeration_map_immediate.value eq %s", "'key2'");
+ String queryEnumerationContains = String.format("case: data.enumeration_map_immediate.value contains %s", "'ey2'");
+ String queryMultichoiceEq = String.format("case: data.multichoice_map_immediate.value eq %s", "'key2'");
+ String queryMultichoiceContains = String.format("case: data.multichoice_map_immediate.value contains %s", "'ey2'");
+ String queryNumberEq = String.format("case: data.number_immediate.value eq %s", 54);
+ String queryNumberLt = String.format("case: data.number_immediate.value lt %s", 55);
+ String queryNumberLte = String.format("case: data.number_immediate.value lte %s", 55);
+ String queryNumberGt = String.format("case: data.number_immediate.value gt %s", 53);
+ String queryNumberGte = String.format("case: data.number_immediate.value gte %s", 53);
+ String queryDateEq = String.format("case: data.date_immediate.value eq %s", SearchUtils.toDateString(LocalDate.now()));
+ String queryDateLt = String.format("case: data.date_immediate.value lt %s", SearchUtils.toDateString(LocalDate.now().plusDays(1)));
+ String queryDateLte = String.format("case: data.date_immediate.value lte %s", SearchUtils.toDateString(LocalDate.now().plusDays(1)));
+ String queryDateGt = String.format("case: data.date_immediate.value gt %s", SearchUtils.toDateString(LocalDate.now().minusDays(1)));
+ String queryDateGte = String.format("case: data.date_immediate.value gte %s", SearchUtils.toDateString(LocalDate.now().minusDays(1)));
+ String queryDateTimeEq = String.format("case: data.date_time_immediate.value eq %s", SearchUtils.toDateTimeString((LocalDateTime) case1.getDataSet().get("date_time_immediate").getRawValue()));
+ String queryDateTimeLt = String.format("case: data.date_time_immediate.value lt %s", SearchUtils.toDateTimeString(LocalDateTime.now().plusMinutes(1)));
+ String queryDateTimeLte = String.format("case: data.date_time_immediate.value lte %s", SearchUtils.toDateTimeString(LocalDateTime.now().plusMinutes(1)));
+ String queryDateTimeGt = String.format("case: data.date_time_immediate.value gt %s", SearchUtils.toDateTimeString(LocalDateTime.now().minusMinutes(1)));
+ String queryDateTimeGte = String.format("case: data.date_time_immediate.value gte %s", SearchUtils.toDateTimeString(LocalDateTime.now().minusMinutes(1)));
+
+ Thread.sleep(3000);
+
+ // text
+ searchAndCompare(queryTextEq, case1);
+ searchAndCompare(queryTextContains, case1);
+
+ // boolean
+ searchAndCompare(queryBoolean, case1);
+
+ // number
+ searchAndCompare(queryNumberEq, case1);
+ searchAndCompare(queryNumberLt, List.of(case1, case2));
+ searchAndCompare(queryNumberLte, List.of(case1, case2));
+ searchAndCompare(queryNumberGt, case1);
+ searchAndCompare(queryNumberGte, case1);
+
+ // date
+ searchAndCompare(queryDateEq, case1);
+ searchAndCompare(queryDateLt, List.of(case1, case2));
+ searchAndCompare(queryDateLte, List.of(case1, case2));
+ searchAndCompare(queryDateGt, case1);
+ searchAndCompare(queryDateGte, case1);
+
+ // datetime
+ searchAndCompare(queryDateTimeEq, case1);
+ searchAndCompare(queryDateTimeLt, List.of(case1, case2));
+ searchAndCompare(queryDateTimeLte, List.of(case1, case2));
+ searchAndCompare(queryDateTimeGt, case1);
+ searchAndCompare(queryDateTimeGte, case1);
+
+ // enumeration/multichoice
+ // todo NAE-1997: should use keyValue, textValue represents only value
+// searchAndCompare(queryEnumerationEq, case1);
+// searchAndCompare(queryEnumerationContains, case1);
+//
+// searchAndCompare(queryMultichoiceEq, case1);
+// searchAndCompare(queryMultichoiceContains, case1);
+
+ // in list text
+ String queryInList = String.format("cases: processIdentifier eq '%s' and data.text_immediate.value in ('test', 'other')", net.getIdentifier());
+ searchAndCompareAsList(queryInList, List.of(case1, case2));
+
+ // in range text
+ String queryInRange = String.format("cases: processIdentifier eq '%s' and data.text_immediate.value in ['other' : 'test')", net.getIdentifier());
+ searchAndCompareAsList(queryInRange, List.of(case2));
+
+ // in list number
+ queryInList = String.format("cases: processIdentifier eq '%s' and data.number_immediate.value in (2, 54)", net.getIdentifier());
+ searchAndCompareAsList(queryInList, List.of(case1, case2));
+
+ // in range number
+ queryInRange = String.format("cases: processIdentifier eq '%s' and data.number_immediate.value in [2 : 54)", net.getIdentifier());
+ searchAndCompareAsList(queryInRange, List.of(case2));
+
+ // in list date
+ queryInList = String.format("cases: processIdentifier eq '%s' and data.date_immediate.value in (%s, %s)", net.getIdentifier(), toDateString(LocalDateTime.now()), toDateString(LocalDateTime.now().minusDays(5)));
+ searchAndCompareAsList(queryInList, List.of(case1, case2));
+
+ // in range date
+ queryInRange = String.format("cases: processIdentifier eq '%s' and data.date_immediate.value in [%s : %s)", net.getIdentifier(), toDateString(LocalDateTime.now().minusDays(10)), toDateString(LocalDateTime.now().plusDays(1)));
+ searchAndCompareAsList(queryInRange, List.of(case1, case2));
+
+ // in list datetime
+ LocalDateTime localDateTime1 = (LocalDateTime) case1.getDataSet().get("date_time_immediate").getRawValue();
+ LocalDateTime localDateTime2 = (LocalDateTime) case2.getDataSet().get("date_time_immediate").getRawValue();
+ queryInList = String.format("cases: processIdentifier eq '%s' and data.date_time_immediate.value in (%s, %s)", net.getIdentifier(), toDateTimeString(localDateTime1), toDateTimeString(localDateTime2));
+ searchAndCompareAsList(queryInList, List.of(case1, case2));
+
+ // in range datetime
+ queryInRange = String.format("cases: processIdentifier eq '%s' and data.date_time_immediate.value in [%s : %s)", net.getIdentifier(), toDateTimeString(localDateTime2), toDateTimeString(localDateTime1));
+ searchAndCompareAsList(queryInRange, List.of(case2));
+
+ // todo NAE-1997: sort by data - indexation change needed
+ }
+
+ @Test
+ public void testSearchByDataOptions() throws InterruptedException {
+ PetriNet net = importPetriNet("search/search_test.xml");
+
+ Case case1 = importHelper.createCase("Search Test", net);
+
+ String queryEq = String.format("case: data.enumeration_map_immediate.options eq '%s'", "key1");
+ String queryContains = String.format("case: data.enumeration_map_immediate.options contains '%s'", "key1");
+
+ Thread.sleep(3000);
+
+ searchAndCompare(queryEq, case1);
+ searchAndCompare(queryContains, case1);
+
+ // in list
+ String queryInList = String.format("cases: processIdentifier eq '%s' and data.enumeration_map_immediate.options in ('key1')", net.getIdentifier());
+ searchAndCompareAsList(queryInList, List.of(case1));
+
+ // in range
+ String queryInRange = String.format("cases: processIdentifier eq '%s' and data.enumeration_map_immediate.options in ['key1' : 'key2')", net.getIdentifier());
+ searchAndCompareAsList(queryInRange, List.of(case1));
+
+ // todo NAE-1997: sort by data - indexation change needed
+ }
+
+ @Test
+ public void testPagination() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ List cases = new ArrayList<>();
+ for (int i = 0; i < 50; i++) {
+ cases.add(importHelper.createCase("Search Test", net));
+ }
+
+ String queryOne = String.format("case: processIdentifier eq '%s'", "search_test");
+ String queryMore = String.format("cases: processIdentifier eq '%s'", "search_test");
+ String queryMoreCustomPagination = String.format("cases: processIdentifier eq '%s' page 1 size 5", "search_test");
+
+ long count = searchService.count(queryOne);
+ assert count == 50;
+
+ Object actual = searchService.search(queryOne);
+ compareById(convertToObject(actual, Case.class), cases.get(0), Case::getStringId);
+
+ actual = searchService.search(queryMore);
+ compareById(convertToObjectList(actual, Case.class), cases.subList(0, 19), Case::getStringId);
+
+ actual = searchService.search(queryMoreCustomPagination);
+ compareById(convertToObjectList(actual, Case.class), cases.subList(5, 9), Case::getStringId);
+ }
+}
diff --git a/src/test/java/com/netgrif/application/engine/search/SearchProcessTest.java b/src/test/java/com/netgrif/application/engine/search/SearchProcessTest.java
new file mode 100644
index 00000000000..64971aa1025
--- /dev/null
+++ b/src/test/java/com/netgrif/application/engine/search/SearchProcessTest.java
@@ -0,0 +1,265 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.TestHelper;
+import com.netgrif.application.engine.petrinet.domain.PetriNet;
+import com.netgrif.application.engine.petrinet.domain.VersionType;
+import com.netgrif.application.engine.petrinet.domain.repositories.PetriNetRepository;
+import com.netgrif.application.engine.search.interfaces.ISearchService;
+import com.netgrif.application.engine.startup.ImportHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.netgrif.application.engine.search.utils.SearchTestUtils.*;
+import static com.netgrif.application.engine.search.utils.SearchUtils.toDateTimeString;
+
+@Slf4j
+@SpringBootTest
+@ActiveProfiles({"test"})
+@ExtendWith(SpringExtension.class)
+public class SearchProcessTest {
+ @Autowired
+ private PetriNetRepository petriNetRepository;
+
+ @Autowired
+ private ImportHelper importHelper;
+
+ @Autowired
+ private TestHelper testHelper;
+
+ @Autowired
+ private ISearchService searchService;
+
+ @BeforeEach
+ void setup() {
+ testHelper.truncateDbs();
+ List idsToDelete = new ArrayList<>();
+ idsToDelete.addAll(petriNetRepository.findAllByIdentifier("search_test").stream().map(PetriNet::getStringId).collect(Collectors.toList()));
+ idsToDelete.addAll(petriNetRepository.findAllByIdentifier("search_test2").stream().map(PetriNet::getStringId).collect(Collectors.toList()));
+ if (!idsToDelete.isEmpty()) {
+ petriNetRepository.deleteAllById(idsToDelete);
+ }
+ }
+
+ private PetriNet importPetriNet(String fileName, VersionType versionType) {
+ PetriNet testNet = importHelper.createNet(fileName, versionType).orElse(null);
+ assert testNet != null;
+ return testNet;
+ }
+
+ private void searchAndCompare(String query, PetriNet expected) {
+ long count = searchService.count(query);
+ assert count == 1;
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, PetriNet.class), expected, PetriNet::getStringId);
+ }
+
+ private void searchAndCompare(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, PetriNet.class), expected, PetriNet::getStringId);
+ }
+
+ private void searchAndCompareAsList(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObjectList(actual, PetriNet.class), expected, PetriNet::getStringId);
+ }
+
+ private void searchAndCompareAsListInOrder(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareByIdInOrder(convertToObjectList(actual, PetriNet.class), expected, PetriNet::getStringId);
+ }
+
+ @Test
+ public void testSearchById() {
+ PetriNet net = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet net2 = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+
+ String query = String.format("process: id eq '%s'", net.getStringId());
+
+ searchAndCompare(query, net);
+
+ // in list
+ String queryInList = String.format("processes: id in ('%s', '%s')", net.getStringId(), net2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(net, net2));
+
+ // sort
+ query = String.format("processes: identifier eq '%s' sort by id", net.getIdentifier());
+ searchAndCompareAsListInOrder(query, List.of(net, net2));
+
+ query = String.format("processes: identifier eq '%s' sort by id desc", net.getIdentifier());
+ searchAndCompareAsListInOrder(query, List.of(net2, net));
+ }
+
+ @Test
+ public void testSearchByIdentifier() {
+ PetriNet net = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet netNewer = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet net2 = importPetriNet("search/search_test2.xml", VersionType.MAJOR);
+ PetriNet net3 = importPetriNet("search/search_test3.xml", VersionType.MAJOR);
+
+ String query = String.format("process: identifier eq '%s'", net.getIdentifier());
+ String queryMore = String.format("processes: identifier eq '%s'", net.getIdentifier());
+
+ searchAndCompare(query, List.of(net, netNewer));
+ searchAndCompareAsList(queryMore, List.of(net, netNewer));
+
+ // in list
+ String queryInList = String.format("processes: identifier in ('%s', '%s', '%s')", net.getIdentifier(), net2.getIdentifier(), net3.getIdentifier());
+ searchAndCompareAsList(queryInList, List.of(net, netNewer, net2, net3));
+
+ // in range
+ String queryInRange = String.format("processes: identifier in ['%s' : '%s')", net.getIdentifier(), net3.getIdentifier());
+ searchAndCompareAsList(queryInRange, List.of(net, netNewer, net2));
+
+ // sort
+ queryMore = String.format("processes: identifier in ('%s', '%s') sort by identifier", net.getIdentifier(), net2.getIdentifier());
+ searchAndCompareAsListInOrder(queryMore, List.of(net, netNewer, net2));
+
+ queryMore = String.format("processes: identifier in ('%s', '%s') sort by identifier desc", net.getIdentifier(), net2.getIdentifier());
+ searchAndCompareAsListInOrder(queryMore, List.of(net2, net, netNewer));
+ }
+
+ @Test
+ public void testSearchByVersion() {
+ PetriNet net = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet netNewerPatch = importPetriNet("search/search_test.xml", VersionType.PATCH);
+ PetriNet netNewerMinor = importPetriNet("search/search_test.xml", VersionType.MINOR);
+ PetriNet netNewerMajor = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+
+ String queryEq = String.format("process: identifier eq '%s' and version eq %s", net.getIdentifier(), "1.0.0");
+ String queryLt = String.format("processes: identifier eq '%s' and version lt %s", net.getIdentifier(), "2.0.0");
+ String queryLte = String.format("processes: identifier eq '%s' and version lte %s", net.getIdentifier(), "2.0.0");
+ String queryGt = String.format("processes: identifier eq '%s' and version gt %s", net.getIdentifier(), "1.0.0");
+ String queryGte = String.format("processes: identifier eq '%s' and version gte %s", net.getIdentifier(), "1.0.0");
+
+ searchAndCompare(queryEq, net);
+ searchAndCompareAsList(queryLt, List.of(net, netNewerPatch, netNewerMinor));
+ searchAndCompareAsList(queryLte, List.of(net, netNewerPatch, netNewerMinor, netNewerMajor));
+ searchAndCompareAsList(queryGt, List.of(netNewerPatch, netNewerMinor, netNewerMajor));
+ searchAndCompareAsList(queryGte, List.of(net, netNewerPatch, netNewerMinor, netNewerMajor));
+
+ // in list
+ String queryInList = String.format("processes: identifier eq '%s' and version in (%s, %s, %s)", net.getIdentifier(), "1.0.0", "1.0.1", "1.1.0");
+ searchAndCompareAsList(queryInList, List.of(net, netNewerPatch, netNewerMinor));
+
+ // in range
+ String queryInRange = String.format("processes: identifier eq '%s' and version in [%s : %s)", net.getIdentifier(), "1.0.0", "1.1.0");
+ searchAndCompareAsList(queryInRange, List.of(net, netNewerPatch));
+
+ // sort
+ String query = String.format("processes: identifier eq '%s' sort by version", net.getIdentifier());
+ searchAndCompareAsListInOrder(query, List.of(net, netNewerPatch, netNewerMinor, netNewerMajor));
+
+ query = String.format("processes: identifier eq '%s' sort by version desc", net.getIdentifier());
+ searchAndCompareAsListInOrder(query, List.of(netNewerMajor, netNewerMinor, netNewerPatch, net));
+ }
+
+ @Test
+ public void testSearchByTitle() {
+ PetriNet net = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet netNewer = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet net2 = importPetriNet("search/search_test2.xml", VersionType.MAJOR);
+
+ String query = String.format("process: title eq '%s'", net.getTitle().toString());
+ String queryOther = String.format("process: title eq '%s'", net2.getTitle().toString());
+ String queryMore = String.format("processes: title eq '%s'", net.getTitle().toString());
+
+ searchAndCompare(query, List.of(net, netNewer));
+ searchAndCompare(queryOther, net2);
+ searchAndCompareAsList(queryMore, List.of(net, netNewer));
+
+ // in list
+ String queryInList = String.format("processes: identifier in ('%s', '%s') and title in ('%s', '%s')", net.getIdentifier(), net2.getIdentifier(), net.getTitle().getDefaultValue(), net2.getTitle().getDefaultValue());
+ searchAndCompareAsList(queryInList, List.of(net, netNewer, net2));
+
+ // in range
+ String queryInRange = String.format("processes: identifier in ('%s', '%s') and title in ['%s' : '%s')", net.getIdentifier(), net2.getIdentifier(), net.getTitle().getDefaultValue(), net2.getTitle().getDefaultValue());
+ searchAndCompareAsList(queryInRange, List.of(net, netNewer));
+
+ // sort
+ queryMore = String.format("processes: identifier in ('%s', '%s') sort by title", net.getIdentifier(), net2.getIdentifier());
+ searchAndCompareAsListInOrder(queryMore, List.of(net, netNewer, net2));
+
+ queryMore = String.format("processes: identifier in ('%s', '%s') sort by title desc", net.getIdentifier(), net2.getIdentifier());
+ searchAndCompareAsListInOrder(queryMore, List.of(net2, net, netNewer));
+ }
+
+ @Test
+ public void testSearchByCreationDate() {
+ PetriNet net = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet netNewer = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+ PetriNet netNewest = importPetriNet("search/search_test.xml", VersionType.MAJOR);
+
+ String queryEq = String.format("process: identifier eq '%s' and creationDate eq %s", net.getIdentifier(), toDateTimeString(net.getCreationDate()));
+ String queryLt = String.format("processes: identifier eq '%s' and creationDate lt %s", net.getIdentifier(), toDateTimeString(netNewest.getCreationDate()));
+ String queryLte = String.format("processes: identifier eq '%s' and creationDate lte %s", net.getIdentifier(), toDateTimeString(netNewest.getCreationDate()));
+ String queryGt = String.format("processes: identifier eq '%s' and creationDate gt %s", net.getIdentifier(), toDateTimeString(net.getCreationDate()));
+ String queryGte = String.format("processes: identifier eq '%s' and creationDate gte %s", net.getIdentifier(), toDateTimeString(net.getCreationDate()));
+
+ searchAndCompare(queryEq, net);
+ searchAndCompareAsList(queryLt, List.of(net, netNewer));
+ searchAndCompareAsList(queryLte, List.of(net, netNewer, netNewest));
+ searchAndCompareAsList(queryGt, List.of(netNewer, netNewest));
+ searchAndCompareAsList(queryGte, List.of(net, netNewer, netNewest));
+
+ // in list
+ String queryInList = String.format("processes: identifier eq '%s' and creationDate in (%s, %s)", net.getIdentifier(), toDateTimeString(net.getCreationDate()), toDateTimeString(netNewest.getCreationDate()));
+ searchAndCompareAsList(queryInList, List.of(net, netNewest));
+
+ // in range
+ String queryInRange = String.format("processes: identifier eq '%s' and creationDate in [%s : %s)", net.getIdentifier(), toDateTimeString(net.getCreationDate()), toDateTimeString(netNewest.getCreationDate()));
+ searchAndCompareAsList(queryInRange, List.of(net, netNewer));
+
+ // sort
+ String query = String.format("processes: identifier eq '%s' sort by creationDate", net.getIdentifier());
+ searchAndCompareAsListInOrder(query, List.of(net, netNewer, netNewest));
+
+ query = String.format("processes: identifier eq '%s' sort by creationDate desc", net.getIdentifier());
+ searchAndCompareAsListInOrder(query, List.of(netNewest, netNewer, net));
+ }
+
+ @Test
+ public void testPagination() {
+ List nets = new ArrayList<>();
+ for (int i = 0; i < 50; i++) {
+ nets.add(importPetriNet("search/search_test.xml", VersionType.MAJOR));
+ }
+
+ String queryOne = String.format("process: identifier eq '%s'", "search_test");
+ String queryMore = String.format("processes: identifier eq '%s'", "search_test");
+ String queryMoreCustomPagination = String.format("processes: identifier eq '%s' page 1 size 5", "search_test");
+
+ long count = searchService.count(queryOne);
+ assert count == 50;
+
+ Object process = searchService.search(queryOne);
+ compareById(convertToObject(process, PetriNet.class), nets.get(0), PetriNet::getStringId);
+
+ Object processes = searchService.search(queryMore);
+ compareById(convertToObjectList(processes, PetriNet.class), nets.subList(0, 19), PetriNet::getStringId);
+
+ processes = searchService.search(queryMoreCustomPagination);
+ compareById(convertToObjectList(processes, PetriNet.class), nets.subList(5, 9), PetriNet::getStringId);
+ }
+
+}
diff --git a/src/test/java/com/netgrif/application/engine/search/SearchTaskTest.java b/src/test/java/com/netgrif/application/engine/search/SearchTaskTest.java
new file mode 100644
index 00000000000..165c675866a
--- /dev/null
+++ b/src/test/java/com/netgrif/application/engine/search/SearchTaskTest.java
@@ -0,0 +1,504 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.TestHelper;
+import com.netgrif.application.engine.auth.domain.Authority;
+import com.netgrif.application.engine.auth.domain.IUser;
+import com.netgrif.application.engine.auth.domain.User;
+import com.netgrif.application.engine.petrinet.domain.I18nString;
+import com.netgrif.application.engine.petrinet.domain.PetriNet;
+import com.netgrif.application.engine.petrinet.domain.roles.ProcessRole;
+import com.netgrif.application.engine.search.interfaces.ISearchService;
+import com.netgrif.application.engine.search.utils.SearchUtils;
+import com.netgrif.application.engine.startup.ImportHelper;
+import com.netgrif.application.engine.workflow.domain.Case;
+import com.netgrif.application.engine.workflow.domain.State;
+import com.netgrif.application.engine.workflow.domain.Task;
+import com.netgrif.application.engine.workflow.service.interfaces.ITaskService;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static com.netgrif.application.engine.search.utils.SearchTestUtils.*;
+import static com.netgrif.application.engine.search.utils.SearchUtils.toDateTimeString;
+
+@Slf4j
+@SpringBootTest
+@ActiveProfiles({"test"})
+@ExtendWith(SpringExtension.class)
+public class SearchTaskTest {
+ public static final String TEST_TRANSITION_ID = "search_test_t1";
+ public static final String TEST_TRANSITION2_ID = "search_test_t2";
+
+ @Autowired
+ private ImportHelper importHelper;
+
+ @Autowired
+ private TestHelper testHelper;
+
+ @Autowired
+ private ISearchService searchService;
+
+ @Autowired
+ private ITaskService taskService;
+
+ private Map auths;
+
+ @BeforeEach
+ void setup() {
+ testHelper.truncateDbs();
+ auths = importHelper.createAuthorities(Map.of("user", Authority.user, "admin", Authority.admin));
+ }
+
+ private PetriNet importPetriNet(String fileName) {
+ PetriNet testNet = importHelper.createNet(fileName).orElse(null);
+ assert testNet != null;
+ return testNet;
+ }
+
+ private IUser createUser(String name, String surname, String email) {
+ User user = new User(email, "password", name, surname);
+ Authority[] authorities = new Authority[]{auths.get("user")};
+ ProcessRole[] processRoles = new ProcessRole[]{};
+ return importHelper.createUser(user, authorities, processRoles);
+ }
+
+ private void searchAndCompare(String query, Task expectedResult) {
+ long count = searchService.count(query);
+ assert count == 1;
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, Task.class), expectedResult, Task::getStringId);
+ }
+
+ private void searchAndCompare(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, Task.class), expected, Task::getStringId);
+ }
+
+ private void searchAndCompareAsList(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObjectList(actual, Task.class), expected, Task::getStringId);
+ }
+
+ private void searchAndCompareAsListInOrder(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareByIdInOrder(convertToObjectList(actual, Task.class), expected, Task::getStringId);
+ }
+
+ @Test
+ public void testSearchById() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ Case caze = importHelper.createCase("Search Test", net);
+ String taskId = caze.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task = taskService.findOne(taskId);
+ String task2Id = caze.getTasks().get(TEST_TRANSITION2_ID).getTaskStringId();
+ Task task2 = taskService.findOne(task2Id);
+
+ String query = String.format("task: id eq '%s'", taskId);
+
+ searchAndCompare(query, task);
+
+ // in list
+ String queryInList = String.format("tasks: id in ('%s', '%s')", task.getStringId(), task2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(task, task2));
+
+ // sort
+ String querySort = String.format("tasks: caseId eq '%s' sort by id", caze.getStringId());
+ String querySort2 = String.format("tasks: caseId eq '%s' sort by id desc", caze.getStringId());
+
+ List case1Tasks = caze.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .collect(Collectors.toList());
+ List asc = case1Tasks.stream()
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList());
+ List desc = case1Tasks.stream()
+ .sorted(Comparator.comparing(Task::getStringId).reversed())
+ .collect(Collectors.toList());
+
+ searchAndCompareAsListInOrder(querySort, asc);
+ searchAndCompareAsListInOrder(querySort2, desc);
+ }
+
+ @Test
+ public void testSearchByTransitionId() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ String taskId = case1.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task = taskService.findOne(taskId);
+ String task2Id = case1.getTasks().get(TEST_TRANSITION2_ID).getTaskStringId();
+ Task task2 = taskService.findOne(task2Id);
+
+ String query = String.format("task: transitionId eq '%s'", TEST_TRANSITION_ID);
+ String queryMore = String.format("tasks: transitionId eq '%s'", TEST_TRANSITION_ID);
+
+ searchAndCompare(query, task);
+ searchAndCompareAsList(queryMore, List.of(task));
+
+ // in list
+ String queryInList = String.format("tasks: transitionId in ('%s', '%s')", TEST_TRANSITION_ID, TEST_TRANSITION2_ID);
+
+ searchAndCompareAsList(queryInList, List.of(task, task2));
+
+ // in range
+ String queryInRange = String.format("tasks: transitionId in ('%s' : '%s']", TEST_TRANSITION_ID, TEST_TRANSITION2_ID);
+
+ searchAndCompareAsList(queryInRange, List.of(task2));
+
+ // sort
+ String querySort = String.format("tasks: caseId eq '%s' sort by transitionId", case1.getStringId());
+ String querySort2 = String.format("tasks: caseId eq '%s' sort by transitionId desc", case1.getStringId());
+
+ List case1Tasks = case1.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .collect(Collectors.toList());
+ List asc = case1Tasks.stream()
+ .sorted(Comparator.comparing(Task::getTransitionId))
+ .collect(Collectors.toList());
+ List desc = case1Tasks.stream()
+ .sorted(Comparator.comparing(Task::getTransitionId).reversed())
+ .collect(Collectors.toList());
+
+ searchAndCompareAsListInOrder(querySort, asc);
+ searchAndCompareAsListInOrder(querySort2, desc);
+ }
+
+ @Test
+ public void testSearchByTitle() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test2", net);
+ String taskId = case1.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task = taskService.findOne(taskId);
+ String task2Id = case2.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task2 = taskService.findOne(task2Id);
+ String task3Id = case3.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task3 = taskService.findOne(task3Id);
+ task3.setTitle(new I18nString("Zzz"));
+ taskService.save(task3);
+
+ String query = String.format("task: title eq '%s'", task.getTitle().getDefaultValue());
+ String queryMore = String.format("tasks: title eq '%s'", task.getTitle().getDefaultValue());
+
+ searchAndCompare(query, List.of(task, task2));
+ searchAndCompareAsList(queryMore, List.of(task, task2));
+
+ // in list
+ String queryInList = String.format("tasks: transitionId eq '%s' and title in ('%s', '%s')", TEST_TRANSITION_ID, task.getTitle().getDefaultValue(), task3.getTitle().getDefaultValue());
+
+ searchAndCompareAsList(queryInList, List.of(task, task2, task3));
+
+ // in range
+ String queryInRange = String.format("tasks: transitionId eq '%s' and title in ('%s' : '%s']", TEST_TRANSITION_ID, task.getTitle().getDefaultValue(), task3.getTitle().getDefaultValue());
+
+ searchAndCompareAsList(queryInRange, List.of(task3));
+
+ // sort
+ String querySort = String.format("tasks: transitionId eq '%s' sort by title", TEST_TRANSITION_ID);
+ String querySort2 = String.format("tasks: transitionId eq '%s' sort by title desc", TEST_TRANSITION_ID);
+
+ searchAndCompareAsListInOrder(querySort, List.of(task, task2, task3));
+ searchAndCompareAsListInOrder(querySort2, List.of(task3, task, task2));
+ }
+
+ @Test
+ public void testSearchByState() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ Case case1 = importHelper.createCase("Search Test", net);
+ importHelper.assignTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ case1 = importHelper.finishTask("Test", case1.getStringId(), user1.transformToLoggedUser()).getCase();
+ List case1Tasks = case1.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .collect(Collectors.toList());
+ List disabled = case1Tasks.stream()
+ .filter(task -> task.getState().equals(State.DISABLED))
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList());
+ List enabled = case1Tasks.stream()
+ .filter(task -> task.getState().equals(State.ENABLED))
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList());
+
+ String query = String.format("task: processId eq '%s' and state eq %s", net.getStringId(), "disabled");
+ String queryOther = String.format("tasks: processId eq '%s' and state eq %s", net.getStringId(), "enabled");
+ String queryMore = String.format("tasks: processId eq '%s' and state eq %s", net.getStringId(), "disabled");
+
+ searchAndCompare(query, disabled);
+ searchAndCompareAsList(queryOther, enabled);
+ searchAndCompareAsList(queryMore, disabled);
+
+ // sort
+ String querySort = String.format("tasks: processId eq '%s' sort by state", net.getStringId());
+ String querySort2 = String.format("tasks: processId eq '%s' sort by state desc", net.getStringId());
+
+ List asc = new ArrayList<>();
+ asc.addAll(disabled);
+ asc.addAll(enabled);
+ List desc = new ArrayList<>();
+ desc.addAll(enabled);
+ desc.addAll(disabled);
+ searchAndCompareAsListInOrder(querySort, asc);
+ searchAndCompareAsListInOrder(querySort2, desc);
+ }
+
+ @Test
+ public void testSearchByUserId() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ IUser user2 = createUser("Name2", "Surname2", "Email2");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ Case case3 = importHelper.createCase("Search Test2", net);
+ String taskId = case1.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task = taskService.findOne(taskId);
+ String task2Id = case2.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task2 = taskService.findOne(task2Id);
+ String task3Id = case3.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task3 = taskService.findOne(task3Id);
+ importHelper.assignTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case3.getStringId(), user2.transformToLoggedUser());
+
+ String query = String.format("task: userId eq '%s'", user1.getStringId());
+ String queryOther = String.format("task: userId eq '%s'", user2.getStringId());
+ String queryMore = String.format("tasks: userId eq '%s'", user1.getStringId());
+
+ searchAndCompare(query, List.of(task, task2));
+ searchAndCompare(queryOther, List.of(task3));
+ searchAndCompareAsList(queryMore, List.of(task, task2));
+
+ // in list
+ String queryInList = String.format("tasks: userId in ('%s', '%s')", user1.getStringId(), user2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(task, task2, task3));
+
+ // sort
+ String querySort = String.format("tasks: transitionId eq '%s' sort by userId", TEST_TRANSITION_ID);
+ String querySort2 = String.format("tasks: transitionId eq '%s' sort by userId desc", TEST_TRANSITION_ID);
+
+ searchAndCompareAsListInOrder(querySort, List.of(task, task2, task3));
+ searchAndCompareAsListInOrder(querySort2, List.of(task3, task, task2));
+ }
+
+ @Test
+ public void testSearchByCaseId() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ List case1Tasks = case1.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList());
+ List case2Tasks = case2.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList());
+
+ String query = String.format("task: caseId eq '%s'", case1.getStringId());
+ String queryOther = String.format("task: caseId eq '%s'", case2.getStringId());
+ String queryMore = String.format("tasks: caseId eq '%s'", case1.getStringId());
+
+ searchAndCompare(query, case1Tasks);
+ searchAndCompare(queryOther, case2Tasks);
+ searchAndCompareAsList(queryMore, case1Tasks);
+
+ // in list
+ String queryInList = String.format("tasks: caseId in ('%s', '%s')", case1.getStringId(), case2.getStringId());
+
+ List allTasks = new ArrayList<>();
+ allTasks.addAll(case1Tasks);
+ allTasks.addAll(case2Tasks);
+ searchAndCompareAsList(queryInList, allTasks);
+
+ // sort
+ String querySort = String.format("tasks: processId eq '%s' sort by caseId", net.getStringId());
+ String querySort2 = String.format("tasks: processId eq '%s' sort by caseId desc", net.getStringId());
+
+ List asc = new ArrayList<>();
+ asc.addAll(case1Tasks);
+ asc.addAll(case2Tasks);
+ List desc = new ArrayList<>();
+ desc.addAll(case2Tasks);
+ desc.addAll(case1Tasks);
+
+ searchAndCompareAsListInOrder(querySort, asc);
+ searchAndCompareAsListInOrder(querySort2, desc);
+ }
+
+ @Test
+ public void testSearchByProcessId() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ PetriNet net2 = importPetriNet("search/search_test2.xml");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test2", net2);
+ List netTasks = case1.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList());
+ List net2Tasks = case2.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList());
+
+ String query = String.format("task: processId eq '%s'", net.getStringId());
+ String queryOther = String.format("task: processId eq '%s'", net2.getStringId());
+ String queryMore = String.format("tasks: processId eq '%s'", net.getStringId());
+
+ searchAndCompare(query, netTasks);
+ searchAndCompare(queryOther, net2Tasks);
+ searchAndCompareAsList(queryMore, netTasks);
+
+ // in list
+ String queryInList = String.format("tasks: processId in ('%s', '%s')", net.getStringId(), net2.getStringId());
+
+ List allTasks = new ArrayList<>();
+ allTasks.addAll(netTasks);
+ allTasks.addAll(net2Tasks);
+ searchAndCompareAsList(queryInList, allTasks);
+
+ // sort
+ String querySort = String.format("tasks: processId in ('%s', '%s') sort by processId", net.getStringId(), net2.getStringId());
+ String querySort2 = String.format("tasks: processId in ('%s', '%s') sort by processId desc", net.getStringId(), net2.getStringId());
+
+ List asc = new ArrayList<>();
+ asc.addAll(netTasks);
+ asc.addAll(net2Tasks);
+ List desc = new ArrayList<>();
+ desc.addAll(net2Tasks);
+ desc.addAll(netTasks);
+
+ searchAndCompareAsListInOrder(querySort, asc);
+ searchAndCompareAsListInOrder(querySort2, desc);
+ }
+
+ @Test
+ public void testSearchByLastAssign() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ LocalDateTime before = LocalDateTime.now();
+ importHelper.assignTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ String taskId = case1.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task = taskService.findOne(taskId);
+ String task2Id = case2.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task2 = taskService.findOne(task2Id);
+
+ String query = String.format("task: lastAssign eq %s", SearchUtils.toDateTimeString(task.getLastAssigned()));
+ String queryBefore = String.format("task: lastAssign > %s", SearchUtils.toDateTimeString(before));
+ String queryMore = String.format("tasks: lastAssign > %s", SearchUtils.toDateTimeString(before));
+
+ searchAndCompare(query, task);
+ searchAndCompare(queryBefore, List.of(task, task2));
+ searchAndCompareAsList(queryMore, List.of(task, task2));
+
+ // in list
+ String queryInList = String.format("tasks: transitionId eq '%s' and lastAssign in (%s, %s)", TEST_TRANSITION_ID, toDateTimeString(task.getLastAssigned()), toDateTimeString(task2.getLastAssigned()));
+ searchAndCompareAsList(queryInList, List.of(task, task2));
+
+ // in range
+ String queryInRange = String.format("tasks: transitionId eq '%s' and lastAssign in [%s : %s)", TEST_TRANSITION_ID, toDateTimeString(task.getLastAssigned()), toDateTimeString(task2.getLastAssigned()));
+ searchAndCompareAsList(queryInRange, List.of(task));
+
+ // sort
+ String querySort = String.format("tasks: transitionId eq '%s' sort by lastAssign", TEST_TRANSITION_ID);
+ String querySort2 = String.format("tasks: transitionId eq '%s' sort by lastAssign desc", TEST_TRANSITION_ID);
+
+ searchAndCompareAsListInOrder(querySort, List.of(task, task2));
+ searchAndCompareAsListInOrder(querySort2, List.of(task2, task));
+ }
+
+ @Test
+ public void testSearchByLastFinish() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ IUser user1 = createUser("Name1", "Surname1", "Email1");
+ Case case1 = importHelper.createCase("Search Test", net);
+ Case case2 = importHelper.createCase("Search Test", net);
+ LocalDateTime before = LocalDateTime.now();
+ importHelper.assignTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.finishTask("Test", case1.getStringId(), user1.transformToLoggedUser());
+ importHelper.assignTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ importHelper.finishTask("Test", case2.getStringId(), user1.transformToLoggedUser());
+ String taskId = case1.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task = taskService.findOne(taskId);
+ String task2Id = case2.getTasks().get(TEST_TRANSITION_ID).getTaskStringId();
+ Task task2 = taskService.findOne(task2Id);
+
+ String query = String.format("task: lastFinish eq %s", SearchUtils.toDateTimeString(task.getLastFinished()));
+ String queryBefore = String.format("task: lastFinish gt %s", SearchUtils.toDateTimeString(before));
+ String queryMore = String.format("tasks: lastFinish gt %s", SearchUtils.toDateTimeString(before));
+
+ searchAndCompare(query, task);
+ searchAndCompare(queryBefore, List.of(task, task2));
+ searchAndCompareAsList(queryMore, List.of(task, task2));
+
+ // in list
+ String queryInList = String.format("tasks: transitionId eq '%s' and lastFinish in (%s, %s)", TEST_TRANSITION_ID, toDateTimeString(task.getLastFinished()), toDateTimeString(task2.getLastFinished()));
+ searchAndCompareAsList(queryInList, List.of(task, task2));
+
+ // in range
+ String queryInRange = String.format("tasks: transitionId eq '%s' and lastFinish in [%s : %s)", TEST_TRANSITION_ID, toDateTimeString(task.getLastFinished()), toDateTimeString(task2.getLastFinished()));
+ searchAndCompareAsList(queryInRange, List.of(task));
+
+ // sort
+ String querySort = String.format("tasks: transitionId eq '%s' sort by lastFinish", TEST_TRANSITION_ID);
+ String querySort2 = String.format("tasks: transitionId eq '%s' sort by lastFinish desc", TEST_TRANSITION_ID);
+
+ searchAndCompareAsListInOrder(querySort, List.of(task, task2));
+ searchAndCompareAsListInOrder(querySort2, List.of(task2, task));
+ }
+
+ @Test
+ public void testPagination() {
+ PetriNet net = importPetriNet("search/search_test.xml");
+ List tasks = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ Case c = importHelper.createCase("Search Test", net);
+ tasks.addAll(c.getTasks().values().stream()
+ .map(taskPair -> taskService.findOne(taskPair.getTaskStringId()))
+ .sorted(Comparator.comparing(Task::getStringId))
+ .collect(Collectors.toList()));
+ }
+
+ String queryOne = String.format("task: processId eq '%s'", net.getStringId());
+ String queryMore = String.format("tasks: processId eq '%s'", net.getStringId());
+ String queryMoreCustomPagination = String.format("tasks: processId eq '%s' page 1 size 5", net.getStringId());
+
+ long count = searchService.count(queryOne);
+ assert count == 30;
+
+ Object actual = searchService.search(queryOne);
+ compareById(convertToObject(actual, Task.class), tasks.get(0), Task::getStringId);
+
+ actual = searchService.search(queryMore);
+ compareById(convertToObjectList(actual, Task.class), tasks.subList(0, 19), Task::getStringId);
+
+ actual = searchService.search(queryMoreCustomPagination);
+ compareById(convertToObjectList(actual, Task.class), tasks.subList(5, 9), Task::getStringId);
+ }
+}
diff --git a/src/test/java/com/netgrif/application/engine/search/SearchUserTest.java b/src/test/java/com/netgrif/application/engine/search/SearchUserTest.java
new file mode 100644
index 00000000000..25d1ab86bd5
--- /dev/null
+++ b/src/test/java/com/netgrif/application/engine/search/SearchUserTest.java
@@ -0,0 +1,209 @@
+package com.netgrif.application.engine.search;
+
+import com.netgrif.application.engine.TestHelper;
+import com.netgrif.application.engine.auth.domain.Authority;
+import com.netgrif.application.engine.auth.domain.User;
+import com.netgrif.application.engine.petrinet.domain.roles.ProcessRole;
+import com.netgrif.application.engine.search.interfaces.ISearchService;
+import com.netgrif.application.engine.startup.ImportHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static com.netgrif.application.engine.search.utils.SearchTestUtils.*;
+
+@Slf4j
+@SpringBootTest
+@ActiveProfiles({"test"})
+@ExtendWith(SpringExtension.class)
+public class SearchUserTest {
+
+ @Autowired
+ private ImportHelper importHelper;
+
+ @Autowired
+ private TestHelper testHelper;
+
+ @Autowired
+ private ISearchService searchService;
+
+ private Map auths;
+
+ @BeforeEach
+ void setup() {
+ testHelper.truncateDbs();
+ auths = importHelper.createAuthorities(Map.of("user", Authority.user, "admin", Authority.admin));
+ }
+
+ private User createUser(String name, String surname, String email, String authority) {
+ User user = new User(email, "password", name, surname);
+ Authority[] authorities = new Authority[]{auths.get(authority)};
+ ProcessRole[] processRoles = new ProcessRole[]{};
+ return (User) importHelper.createUser(user, authorities, processRoles);
+ }
+
+ private void searchAndCompare(String query, User expected) {
+ long count = searchService.count(query);
+ assert count == 1;
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, User.class), expected, User::getStringId);
+ }
+
+ private void searchAndCompare(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObject(actual, User.class), expected, User::getStringId);
+ }
+
+ private void searchAndCompareAsList(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareById(convertToObjectList(actual, User.class), expected, User::getStringId);
+ }
+
+ private void searchAndCompareAsListInOrder(String query, List expected) {
+ long count = searchService.count(query);
+ assert count == expected.size();
+
+ Object actual = searchService.search(query);
+ compareByIdInOrder(convertToObjectList(actual, User.class), expected, User::getStringId);
+ }
+
+ @Test
+ public void testSearchById() {
+ User user1 = createUser("name1", "surname1", "email1", "user");
+ User user2 = createUser("name2", "surname2", "email2", "admin");
+
+ String query = String.format("user: id eq '%s'", user1.getStringId());
+
+ searchAndCompare(query, user1);
+
+ // in list
+ String queryInList = String.format("users: id in ('%s', '%s')", user1.getStringId(), user2.getStringId());
+
+ searchAndCompareAsList(queryInList, List.of(user1, user2));
+
+ // sort
+ String querySort = String.format("users: id in ('%s', '%s') sort by id", user1.getStringId(), user2.getStringId());
+ String querySort2 = String.format("users: id in ('%s', '%s') sort by id desc", user1.getStringId(), user2.getStringId());
+
+ searchAndCompareAsListInOrder(querySort, List.of(user1, user2));
+ searchAndCompareAsListInOrder(querySort2, List.of(user2, user1));
+ }
+
+ @Test
+ public void testSearchByEmail() {
+ User user1 = createUser("name1", "surname1", "email1", "user");
+ User user2 = createUser("name2", "surname2", "email2", "admin");
+
+ String query = String.format("user: email eq '%s'", user1.getEmail());
+
+ searchAndCompare(query, user1);
+
+ // in list
+ String queryInList = String.format("users: email in ('%s', '%s')", user1.getEmail(), user2.getEmail());
+
+ searchAndCompareAsList(queryInList, List.of(user1, user2));
+
+ // sort
+ String querySort = String.format("users: id in ('%s', '%s') sort by email", user1.getStringId(), user2.getStringId());
+ String querySort2 = String.format("users: id in ('%s', '%s') sort by email desc", user1.getStringId(), user2.getStringId());
+
+ searchAndCompareAsListInOrder(querySort, List.of(user1, user2));
+ searchAndCompareAsListInOrder(querySort2, List.of(user2, user1));
+ }
+
+ @Test
+ public void testSearchByName() {
+ User user1 = createUser("name1", "surname1", "email1", "user");
+ User user2 = createUser("name2", "surname2", "email2", "admin");
+ User user3 = createUser("name1", "surname1", "email3", "user");
+
+ String query = String.format("users: name eq '%s'", user1.getName());
+
+ searchAndCompareAsList(query, List.of(user1, user3));
+
+ // in list
+ String queryInList = String.format("users: name in ('%s', '%s')", user1.getName(), user2.getName());
+
+ searchAndCompareAsList(queryInList, List.of(user1, user2, user3));
+
+ // in range
+ String queryInRange = String.format("users: name in ('%s' : '%s']", user1.getName(), user2.getName());
+
+ searchAndCompareAsList(queryInRange, List.of(user2));
+
+ // sort
+ String querySort = String.format("users: id in ('%s', '%s', '%s') sort by name", user1.getStringId(), user2.getStringId(), user3.getStringId());
+ String querySort2 = String.format("users: id in ('%s', '%s', '%s') sort by name desc", user1.getStringId(), user2.getStringId(), user3.getStringId());
+
+ searchAndCompareAsListInOrder(querySort, List.of(user1, user3, user2));
+ searchAndCompareAsListInOrder(querySort2, List.of(user2, user1, user3));
+ }
+
+ @Test
+ public void testSearchBySurname() {
+ User user1 = createUser("name1", "surname1", "email1", "user");
+ User user2 = createUser("name2", "surname2", "email2", "admin");
+ User user3 = createUser("name1", "surname1", "email3", "user");
+
+ String query = String.format("users: surname eq '%s'", user1.getSurname());
+
+ searchAndCompareAsList(query, List.of(user1, user3));
+
+ // in list
+ String queryInList = String.format("users: surname in ('%s', '%s')", user1.getSurname(), user2.getSurname());
+
+ searchAndCompareAsList(queryInList, List.of(user1, user2, user3));
+
+ // in range
+ String queryInRange = String.format("users: surname in ('%s' : '%s']", user1.getSurname(), user2.getSurname());
+
+ searchAndCompareAsList(queryInRange, List.of(user2));
+
+ // sort
+ String querySort = String.format("users: id in ('%s', '%s', '%s') sort by surname", user1.getStringId(), user2.getStringId(), user3.getStringId());
+ String querySort2 = String.format("users: id in ('%s', '%s', '%s') sort by surname desc", user1.getStringId(), user2.getStringId(), user3.getStringId());
+
+ searchAndCompareAsListInOrder(querySort, List.of(user1, user3, user2));
+ searchAndCompareAsListInOrder(querySort2, List.of(user2, user1, user3));
+ }
+
+ @Test
+ public void testPagination() {
+ List users = new ArrayList<>();
+ for (int i = 0; i < 50; i++) {
+ users.add(createUser("name" + i, "surname" + i , "email" + i, "user"));
+ }
+
+ String queryOne = String.format("user: email contains '%s'", "email");
+ String queryMore = String.format("users: email contains '%s'", "email");
+ String queryMoreCustomPagination = String.format("users: email contains '%s' page 1 size 5", "email");
+
+ long count = searchService.count(queryOne);
+ assert count == 50;
+
+ Object actual = searchService.search(queryOne);
+ compareById(convertToObject(actual, User.class), users.get(0), User::getStringId);
+
+ actual = searchService.search(queryMore);
+ compareById(convertToObjectList(actual, User.class), users.subList(0, 19), User::getStringId);
+
+ actual = searchService.search(queryMoreCustomPagination);
+ compareById(convertToObjectList(actual, User.class), users.subList(5, 9), User::getStringId);
+ }
+}
diff --git a/src/test/java/com/netgrif/application/engine/search/utils/MongoDbUtils.java b/src/test/java/com/netgrif/application/engine/search/utils/MongoDbUtils.java
new file mode 100644
index 00000000000..e5ca2908ef4
--- /dev/null
+++ b/src/test/java/com/netgrif/application/engine/search/utils/MongoDbUtils.java
@@ -0,0 +1,18 @@
+package com.netgrif.application.engine.search.utils;
+
+import com.querydsl.core.types.Predicate;
+
+import org.bson.Document;
+import org.springframework.data.mongodb.core.MongoOperations;
+import org.springframework.data.mongodb.repository.support.SpringDataMongodbQuery;
+
+public class MongoDbUtils extends SpringDataMongodbQuery {
+
+ public MongoDbUtils(MongoOperations operations, Class extends T> type) {
+ super(operations, type, operations.getCollectionName(type));
+ }
+
+ public Document convertPredicateToDocument(Predicate predicate) {
+ return this.createQuery(predicate);
+ }
+}
diff --git a/src/test/java/com/netgrif/application/engine/search/utils/SearchTestUtils.java b/src/test/java/com/netgrif/application/engine/search/utils/SearchTestUtils.java
new file mode 100644
index 00000000000..93d9d80ecee
--- /dev/null
+++ b/src/test/java/com/netgrif/application/engine/search/utils/SearchTestUtils.java
@@ -0,0 +1,56 @@
+package com.netgrif.application.engine.search.utils;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class SearchTestUtils {
+
+ public static T convertToObject(Object object, Class targetClass) {
+ assert targetClass.isInstance(object);
+ return targetClass.cast(object);
+ }
+
+ public static List convertToObjectList(Object objectList, Class targetClass) {
+ assert objectList instanceof List>;
+ for (Object object : (List>) objectList) {
+ assert targetClass.isInstance(object);
+ }
+
+ return (List) objectList;
+ }
+
+ public static void compareById(T actual, T expected, Function getId) {
+ assert getId.apply(actual).equals(getId.apply(expected));
+ }
+
+ public static void compareById(T actual, List expected, Function getId) {
+ List expectedIds = expected.stream()
+ .map(getId)
+ .collect(Collectors.toList());
+
+ assert expectedIds.contains(getId.apply(actual));
+ }
+
+ public static void compareById(List actual, List expected, Function getId) {
+ List actualIds = actual.stream()
+ .map(getId)
+ .collect(Collectors.toList());
+ List expectedIds = expected.stream()
+ .map(getId)
+ .collect(Collectors.toList());
+
+ assert actualIds.containsAll(expectedIds);
+ }
+
+ public static void compareByIdInOrder(List actual, List expected, Function getId) {
+ List actualIds = actual.stream()
+ .map(getId)
+ .collect(Collectors.toList());
+ List expectedIds = expected.stream()
+ .map(getId)
+ .collect(Collectors.toList());
+
+ assert actualIds.equals(expectedIds);
+ }
+}
diff --git a/src/test/resources/petriNets/search/search_test.xml b/src/test/resources/petriNets/search/search_test.xml
new file mode 100644
index 00000000000..f28e3385c55
--- /dev/null
+++ b/src/test/resources/petriNets/search/search_test.xml
@@ -0,0 +1,220 @@
+
+ search_test
+ ST1
+ Search Test
+ check_circle
+ true
+ true
+ false
+
+ text_immediate
+
+ test
+
+
+ number_immediate
+
+ 54
+
+
+ enumeration_map_immediate
+
+
+
+
+
+
+ key2
+
+
+ multichoice_map_immediate
+
+
+
+
+
+
+
+ key1
+ key2
+
+
+
+ boolean_immediate
+
+ true
+
+
+ date_immediate
+
+ java.time.LocalDate.now()
+
+
+ date_time_immediate
+
+ java.time.LocalDateTime.now()
+
+
+ search_test_t1
+ 260
+ 100
+
+
+ search_test_t1_0
+ 4
+ grid
+
+ text_immediate
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 4
+ material
+ outline
+
+
+
+ number_immediate
+
+ editable
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ outline
+
+
+
+ enumeration_map_immediate
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 4
+ material
+ outline
+
+
+
+ multichoice_map_immediate
+
+ editable
+
+
+ 0
+ 3
+ 1
+ 4
+ material
+ outline
+
+
+
+ boolean_immediate
+
+ editable
+
+
+ 0
+ 4
+ 1
+ 4
+ material
+ outline
+
+
+
+ date_immediate
+
+ editable
+
+
+ 0
+ 5
+ 1
+ 4
+ material
+ outline
+
+
+
+ date_time_immediate
+
+ editable
+
+
+ 0
+ 6
+ 1
+ 4
+ material
+ outline
+
+
+
+
+ search_test_t1_assign
+
+
+ search_test_t1_finish
+
+
+ search_test_t1_cancel
+
+
+ search_test_t1_delegate
+
+
+
+ search_test_t2
+ 260
+ 180
+
+
+
+ p1
+ 180
+ 100
+ 1
+ false
+
+
+ p2
+ 340
+ 100
+ 0
+ false
+
+
+ a1
+ regular
+ p1
+ search_test_t1
+ 1
+
+
+ a2
+ regular
+ search_test_t1
+ p2
+ 1
+
+
+ a3
+ regular
+ p1
+ search_test_t2
+ 1
+
+
\ No newline at end of file
diff --git a/src/test/resources/petriNets/search/search_test2.xml b/src/test/resources/petriNets/search/search_test2.xml
new file mode 100644
index 00000000000..a2d9ed2def1
--- /dev/null
+++ b/src/test/resources/petriNets/search/search_test2.xml
@@ -0,0 +1,207 @@
+
+ search_test2
+ ST2
+ Search Test 2
+ check_circle
+ true
+ true
+ false
+
+ text_immediate
+
+ test
+
+
+ number_immediate
+
+ 54
+
+
+ enumeration_map_immediate
+
+
+
+
+
+
+ key2
+
+
+ multichoice_map_immediate
+
+
+
+
+
+
+
+ key1
+ key2
+
+
+
+ boolean_immediate
+
+ true
+
+
+ date_immediate
+
+ java.time.LocalDate.now()
+
+
+ date_time_immediate
+
+ java.time.LocalDateTime.now()
+
+
+ search_test_t1
+ 260
+ 100
+
+
+ search_test_t1_0
+ 4
+ grid
+
+ text_immediate
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 4
+ material
+ outline
+
+
+
+ number_immediate
+
+ editable
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ outline
+
+
+
+ enumeration_map_immediate
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 4
+ material
+ outline
+
+
+
+ multichoice_map_immediate
+
+ editable
+
+
+ 0
+ 3
+ 1
+ 4
+ material
+ outline
+
+
+
+ boolean_immediate
+
+ editable
+
+
+ 0
+ 4
+ 1
+ 4
+ material
+ outline
+
+
+
+ date_immediate
+
+ editable
+
+
+ 0
+ 5
+ 1
+ 4
+ material
+ outline
+
+
+
+ date_time_immediate
+
+ editable
+
+
+ 0
+ 6
+ 1
+ 4
+ material
+ outline
+
+
+
+
+ search_test_t1_asign
+
+
+ search_test_t1_fiish
+
+
+ search_test_t1_cacel
+
+
+ search_test_t1_deleate
+
+
+
+ p1
+ 180
+ 100
+ 1
+ false
+
+
+ p2
+ 340
+ 100
+ 0
+ false
+
+
+ a1
+ regular
+ p1
+ search_test_t1
+ 1
+
+
+ a2
+ regular
+ search_test_t1
+ p2
+ 1
+
+
\ No newline at end of file
diff --git a/src/test/resources/petriNets/search/search_test3.xml b/src/test/resources/petriNets/search/search_test3.xml
new file mode 100644
index 00000000000..9621b6295d1
--- /dev/null
+++ b/src/test/resources/petriNets/search/search_test3.xml
@@ -0,0 +1,207 @@
+
+ search_test3
+ ST3
+ Search Test 3
+ check_circle
+ true
+ true
+ false
+
+ text_immediate
+
+ test
+
+
+ number_immediate
+
+ 54
+
+
+ enumeration_map_immediate
+
+
+
+
+
+
+ key2
+
+
+ multichoice_map_immediate
+
+
+
+
+
+
+
+ key1
+ key2
+
+
+
+ boolean_immediate
+
+ true
+
+
+ date_immediate
+
+ java.time.LocalDate.now()
+
+
+ date_time_immediate
+
+ java.time.LocalDateTime.now()
+
+
+ search_test_t1
+ 260
+ 100
+
+
+ search_test_t1_0
+ 4
+ grid
+
+ text_immediate
+
+ editable
+
+
+ 0
+ 0
+ 1
+ 4
+ material
+ outline
+
+
+
+ number_immediate
+
+ editable
+
+
+ 0
+ 1
+ 1
+ 4
+ material
+ outline
+
+
+
+ enumeration_map_immediate
+
+ editable
+
+
+ 0
+ 2
+ 1
+ 4
+ material
+ outline
+
+
+
+ multichoice_map_immediate
+
+ editable
+
+
+ 0
+ 3
+ 1
+ 4
+ material
+ outline
+
+
+
+ boolean_immediate
+
+ editable
+
+
+ 0
+ 4
+ 1
+ 4
+ material
+ outline
+
+
+
+ date_immediate
+
+ editable
+
+
+ 0
+ 5
+ 1
+ 4
+ material
+ outline
+
+
+
+ date_time_immediate
+
+ editable
+
+
+ 0
+ 6
+ 1
+ 4
+ material
+ outline
+
+
+
+
+ search_test_t1_asign
+
+
+ search_test_t1_fiish
+
+
+ search_test_t1_cacel
+
+
+ search_test_t1_deleate
+
+
+
+ p1
+ 180
+ 100
+ 1
+ false
+
+
+ p2
+ 340
+ 100
+ 0
+ false
+
+
+ a1
+ regular
+ p1
+ search_test_t1
+ 1
+
+
+ a2
+ regular
+ search_test_t1
+ p2
+ 1
+
+
\ No newline at end of file