diff --git a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/EnumerationMapField.groovy b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/EnumerationMapField.groovy
index 127fa3b499..01d147feb8 100644
--- a/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/EnumerationMapField.groovy
+++ b/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/EnumerationMapField.groovy
@@ -11,12 +11,12 @@ class EnumerationMapField extends MapOptionsField
+ * This method retrieves the {@link I18nString} from the options map that corresponds to the
+ * current value of this field.
+ *
* searchCase("case: processIdentifier eq 'query_test' and data.number_0.value == 3")
* searchCase("case: id eq '5f9b1c2d3e4f5a6b7c8d9e0f'")
+ * searchCase("id eq '5f9b1c2d3e4f5a6b7c8d9e0f'")
*
*
* @param query query language string starting with {@code case:}
@@ -2560,6 +2561,7 @@ class ActionDelegate {
*
* pagedSearchCases("cases: processIdentifier eq 'query_test' page 1 size 5 sort by title desc")
* pagedSearchCases("cases: author eq 'user@mail.com' and creationDate gt 2020-03-03")
+ * pagedSearchCases("author eq 'user@mail.com' and creationDate gt 2020-03-03")
*
*
* @param query query language string starting with {@code cases:}
@@ -2579,6 +2581,7 @@ class ActionDelegate {
*
* searchCases("cases: processIdentifier eq 'query_test' and data.boolean_0.value == true")
* searchCases("cases: title contains 'Test' sort by creationDate desc")
+ * searchCases("title contains 'Test' sort by creationDate desc")
*
*
* @param query query language string starting with {@code cases:}
@@ -2597,6 +2600,7 @@ class ActionDelegate {
*
* countCases("cases: processIdentifier eq 'query_test'")
* countCases("cases: data.boolean_0.value == true and data.text_0.value != '4'")
+ * countCases("data.boolean_0.value == true and data.text_0.value != '4'")
*
*
* @param query query language string starting with {@code cases:}
@@ -2615,6 +2619,7 @@ class ActionDelegate {
*
* existsCase("cases: processIdentifier eq 'query_test'")
* existsCase("cases: id in ('5f9b1c2d3e4f5a6b7c8d9e0f', '5f9b1c2d3e4f5a6b7c8d9e10')")
+ * existsCase("id in ('5f9b1c2d3e4f5a6b7c8d9e0f', '5f9b1c2d3e4f5a6b7c8d9e10')")
*
*
* @param query query language string starting with {@code cases:}
@@ -2633,6 +2638,7 @@ class ActionDelegate {
*
* searchTask("task: transitionId eq 't1' and caseId eq '5f9b1c2d3e4f5a6b7c8d9e0f'")
* searchTask("task: id eq '5f9b1c2d3e4f5a6b7c8d9e0f'")
+ * searchTask("id eq '5f9b1c2d3e4f5a6b7c8d9e0f'")
*
*
* @param query query language string starting with {@code task:}
@@ -2652,6 +2658,7 @@ class ActionDelegate {
*
* pagedSearchTasks("tasks: title eq 'test' page 0 size 10 sort by lastFinish desc")
* pagedSearchTasks("tasks: userId eq 'user1' and state eq enabled")
+ * pagedSearchTasks("userId eq 'user1' and state eq enabled")
*
*
* @param query query language string starting with {@code tasks:}
@@ -2671,6 +2678,7 @@ class ActionDelegate {
*
* searchTasks("tasks: processId eq 'my_process' and userId in ('user1', 'user2')")
* searchTasks("tasks: title contains 'Approve' sort by title asc")
+ * searchTasks("title contains 'Approve' sort by title asc")
*
*
* @param query query language string starting with {@code tasks:}
@@ -2689,6 +2697,7 @@ class ActionDelegate {
*
* countTasks("tasks: caseId eq '5f9b1c2d3e4f5a6b7c8d9e0f'")
* countTasks("tasks: transitionId eq 't1' and userId eq 'user1'")
+ * countTasks("transitionId eq 't1' and userId eq 'user1'")
*
*
* @param query query language string starting with {@code tasks:}
@@ -2707,6 +2716,7 @@ class ActionDelegate {
*
* existsTask("tasks: caseId eq '5f9b1c2d3e4f5a6b7c8d9e0f'")
* existsTask("tasks: transitionId eq 't1' and userId not eq 'user1'")
+ * existsTask("transitionId eq 't1' and userId not eq 'user1'")
*
*
* @param query query language string starting with {@code tasks:}
@@ -2725,6 +2735,7 @@ class ActionDelegate {
*
* searchProcess("process: identifier == 'query_test'")
* searchProcess("process: identifier eq 'my_process' and version eq 1.0.0")
+ * searchProcess("identifier eq 'my_process' and version eq 1.0.0")
*
*
* @param query query language string starting with {@code process:}
@@ -2744,6 +2755,7 @@ class ActionDelegate {
*
* pagedSearchProcesses("processes: identifier eq 'my_process' page 0 size 10 sort by version desc")
* pagedSearchProcesses("processes: version in (1.0.0 : 2.0.0)")
+ * pagedSearchProcesses("version in (1.0.0 : 2.0.0)")
*
*
* @param query query language string starting with {@code processes:}
@@ -2763,6 +2775,7 @@ class ActionDelegate {
*
* searchProcesses("processes: title contains 'Test' sort by identifier asc")
* searchProcesses("processes: identifier in ('process_a', 'process_b')")
+ * searchProcesses("identifier in ('process_a', 'process_b')")
*
*
* @param query query language string starting with {@code processes:}
@@ -2781,6 +2794,7 @@ class ActionDelegate {
*
* countProcesses("processes: identifier eq 'my_process'")
* countProcesses("processes: version gte 1.0.0")
+ * countProcesses("version gte 1.0.0")
*
*
* @param query query language string starting with {@code processes:}
@@ -2799,6 +2813,7 @@ class ActionDelegate {
*
* existsProcess("processes: identifier eq 'my_process'")
* existsProcess("processes: version eq 1.0.0")
+ * existsProcess("version eq 1.0.0")
*
*
* @param query query language string starting with {@code processes:}
@@ -2817,6 +2832,7 @@ class ActionDelegate {
*
* searchUser("user: email eq 'user@mail.com'")
* searchUser("user: name eq 'John' and surname eq 'Doe'")
+ * searchUser("name eq 'John' and surname eq 'Doe'")
*
*
* @param query query language string starting with {@code user:}
@@ -2836,6 +2852,7 @@ class ActionDelegate {
*
* pagedSearchUsers("users: name eq 'John' page 0 size 25 sort by surname asc")
* pagedSearchUsers("users: email contains '@company.com'")
+ * pagedSearchUsers("email contains '@company.com'")
*
*
* @param query query language string starting with {@code users:}
@@ -2855,6 +2872,7 @@ class ActionDelegate {
*
* searchUsers("users: surname eq 'Doe' sort by name asc")
* searchUsers("users: email in ('a@mail.com', 'b@mail.com')")
+ * searchUsers("email in ('a@mail.com', 'b@mail.com')")
*
*
* @param query query language string starting with {@code users:}
@@ -2873,6 +2891,7 @@ class ActionDelegate {
*
* countUsers("users: email contains '@company.com'")
* countUsers("users: name eq 'John'")
+ * countUsers("name eq 'John'")
*
*
* @param query query language string starting with {@code users:}
@@ -2891,6 +2910,7 @@ class ActionDelegate {
*
* existsUser("users: email eq 'user@mail.com'")
* existsUser("users: name eq 'John' and surname eq 'Doe'")
+ * existsUser("name eq 'John' and surname eq 'Doe'")
*
*
* @param query query language string starting with {@code users:}
diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/AbstractResourceSearchService.java b/src/main/java/com/netgrif/application/engine/pfql/service/AbstractResourceSearchService.java
new file mode 100644
index 0000000000..1c2c33319c
--- /dev/null
+++ b/src/main/java/com/netgrif/application/engine/pfql/service/AbstractResourceSearchService.java
@@ -0,0 +1,126 @@
+package com.netgrif.application.engine.pfql.service;
+
+import com.netgrif.application.engine.pfql.service.formatters.QueryLangPlaceholderHandler;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.Page;
+
+import java.util.List;
+
+import static com.netgrif.application.engine.pfql.service.utils.SearchUtils.*;
+import static com.netgrif.application.engine.pfql.service.utils.SearchUtils.buildResourcePrefix;
+
+/**
+ * Abstract base class for resource search services providing shared query pre-processing.
+ * + * Handles common pre-processing steps: + *
+ * This method identifies the specific {@link QueryType} associated with the resource + * managed by implementations of this service. It is used for validation to ensure + * that query evaluators match the expected resource type. + *
+ * + * @return the {@link QueryType} representing the resource type handled by this service + */ QueryType getQueryResourceType(); - Resource searchOne(String queryString); + /** + * Searches for a single resource using a query string with optional placeholder arguments. + *+ * The query string may contain placeholders that will be replaced with the provided arguments. + * This method parses and evaluates the query string, then executes the search operation. + *
+ * + * @param queryString the query language expression to evaluate and execute + * @param args optional arguments to substitute into query placeholders + * @return the first resource matching the query, or null if no resource is found + * @throws IllegalArgumentException if the query is invalid or expects multiple results + */ + Resource searchOne(String queryString, Object... args); + + /** + * Searches for a single resource using a pre-evaluated query. + *+ * This method executes a search operation using a {@link QueryLangEvaluator} that has + * already been evaluated and validated. The evaluator must be configured to expect + * a single result and match the service's resource type. + *
+ * + * @param evaluator the evaluated query object containing the search criteria and metadata + * @return the first resource matching the query, or null if no resource is found + * @throws IllegalArgumentException if the evaluator is null, not configured for single results, + * or has a resource type mismatch + */ Resource searchOne(QueryLangEvaluator evaluator); - Page+ * The query string may contain placeholders that will be replaced with the provided arguments. + * This method parses and evaluates the query string, then executes a paginated search operation. + * Results are returned in pages according to the pagination settings in the query. + *
+ * + * @param queryString the query language expression to evaluate and execute + * @param args optional arguments to substitute into query placeholders + * @return a page of resources matching the query with pagination information + * @throws IllegalArgumentException if the query is invalid or expects a single result + */ + Page+ * This method executes a paginated search operation using a {@link QueryLangEvaluator} that has + * already been evaluated and validated. The evaluator must be configured to expect + * multiple results and match the service's resource type. Pagination settings from the + * evaluator determine the page size and number. + *
+ * + * @param evaluator the evaluated query object containing the search criteria, pagination settings, and metadata + * @return a page of resources matching the query with pagination information + * @throws IllegalArgumentException if the evaluator is null, not configured for multiple results, + * or has a resource type mismatch + */ Page+ * The query string may contain placeholders that will be replaced with the provided arguments. + * This method parses and evaluates the query string, then counts the matching resources + * without retrieving them. + *
+ * + * @param queryString the query language expression to evaluate and execute + * @param args optional arguments to substitute into query placeholders + * @return the number of resources matching the query + * @throws IllegalArgumentException if the query is invalid + */ + long count(String queryString, Object... args); + + /** + * Counts the number of resources matching a pre-evaluated query. + *+ * This method executes a count operation using a {@link QueryLangEvaluator} that has + * already been evaluated and validated. The evaluator must match the service's resource type. + * This operation counts matching resources without retrieving them. + *
+ * + * @param evaluator the evaluated query object containing the search criteria and metadata + * @return the number of resources matching the query + * @throws IllegalArgumentException if the evaluator is null or has a resource type mismatch + */ long count(QueryLangEvaluator evaluator); - boolean exists(String queryString); + /** + * Checks if any resource exists that matches a query string with optional placeholder arguments. + *+ * The query string may contain placeholders that will be replaced with the provided arguments. + * This method parses and evaluates the query string, then checks for the existence of at least + * one matching resource without retrieving it. + *
+ * + * @param queryString the query language expression to evaluate and execute + * @param args optional arguments to substitute into query placeholders + * @return true if at least one resource matching the query exists, false otherwise + * @throws IllegalArgumentException if the query is invalid + */ + boolean exists(String queryString, Object... args); + + /** + * Checks if any resource exists that matches a pre-evaluated query. + *+ * This method executes an existence check using a {@link QueryLangEvaluator} that has + * already been evaluated and validated. The evaluator must match the service's resource type. + * This operation checks for the existence of at least one matching resource without retrieving it. + *
+ * + * @param evaluator the evaluated query object containing the search criteria and metadata + * @return true if at least one resource matching the query exists, false otherwise + * @throws IllegalArgumentException if the evaluator is null or has a resource type mismatch + */ boolean exists(QueryLangEvaluator evaluator); /** diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/ISearchService.java b/src/main/java/com/netgrif/application/engine/pfql/service/ISearchService.java index 1ea796443f..2e9847056c 100644 --- a/src/main/java/com/netgrif/application/engine/pfql/service/ISearchService.java +++ b/src/main/java/com/netgrif/application/engine/pfql/service/ISearchService.java @@ -1,11 +1,11 @@ package com.netgrif.application.engine.pfql.service; public interface ISearchService { - String explainQuery(String query); + String explainQuery(String query, Object... args); - Object search(String query); + Object search(String query, Object... args); - long count(String query); + long count(String query, Object... args); - boolean exists(String query); + boolean exists(String query, Object... args); } diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/SearchService.java b/src/main/java/com/netgrif/application/engine/pfql/service/SearchService.java index f9a2701701..0ef4f71c34 100644 --- a/src/main/java/com/netgrif/application/engine/pfql/service/SearchService.java +++ b/src/main/java/com/netgrif/application/engine/pfql/service/SearchService.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.pfql.service; import com.netgrif.application.engine.pfql.domain.enums.QueryType; +import com.netgrif.application.engine.pfql.service.formatters.QueryLangPlaceholderHandler; import com.netgrif.application.engine.pfql.service.utils.SearchUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -11,17 +12,19 @@ import java.util.stream.Collectors; import static com.netgrif.application.engine.pfql.service.utils.SearchUtils.evaluateQuery; +import static com.netgrif.application.engine.pfql.service.utils.SearchUtils.formatPlaceholders; @Slf4j @Service public class SearchService implements ISearchService { - private final Map+ * This formatter handles the conversion of Boolean objects into their string representation + * for use in query language placeholders. It supports both {@code true} and {@code false} values, + * converting them to their corresponding string literals. + *
+ * + * @see QueryLangPlaceholderFormatter + */ +public class BooleanPlaceholderFormatter implements QueryLangPlaceholderFormatter { + + @Override + public boolean supports(Object value) { + return value instanceof Boolean; + } + + @Override + public String format(Object value) { + return String.valueOf(value); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/CaseRefPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/CaseRefPlaceholderFormatter.java new file mode 100644 index 0000000000..090b2c1507 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/CaseRefPlaceholderFormatter.java @@ -0,0 +1,74 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import com.netgrif.application.engine.petrinet.domain.Component; +import com.netgrif.application.engine.petrinet.domain.dataset.CaseField; +import com.netgrif.application.engine.petrinet.domain.dataset.MapOptionsField; + +import java.util.stream.Collectors; + +/** + * Formatter implementation for CaseRef field placeholder values in PFQL queries. + *+ * This formatter handles the conversion of case reference fields into their string representation + * for use in query language placeholders. It supports two types of case reference values: + *
+ *+ * The formatter converts case reference values into a comma-separated list of single-quoted + * strings enclosed in brackets, e.g., {@code ('case-id-1', 'case-id-2')}. + * Empty or null values are formatted as empty brackets {@code ()}. + *
+ * + * @see QueryLangPlaceholderFormatter + * @see CaseField + * @see MapOptionsField + */ +public class CaseRefPlaceholderFormatter implements QueryLangPlaceholderFormatter { + + @Override + public boolean supports(Object value) { + return isValueCaseRef(value) || isOptionsCaseRef(value); + } + + @Override + public String format(Object value) { + if (isValueCaseRef(value)) { + CaseField field = (CaseField) value; + if (field.getValue() == null) { + return "()"; + } + return wrapInBrackets(field.getValue().stream() + .map(this::wrapInSingleQuotes) + .collect(Collectors.joining(", "))); + } + + MapOptionsField, ?> field = (MapOptionsField, ?>) value; + if (field.getOptions() == null) { + return "()"; + } + + return wrapInBrackets(field.getOptions().keySet().stream() + .map(this::wrapInSingleQuotes) + .collect(Collectors.joining(", "))); + } + + protected boolean isValueCaseRef(Object value) { + return value instanceof CaseField; + } + + protected boolean isOptionsCaseRef(Object value) { + boolean isOptionsField = value instanceof MapOptionsField; + if (isOptionsField) { + MapOptionsField, ?> field = (MapOptionsField, ?>) value; + Component component = field.getComponent(); + if (component == null) { + return false; + } + return component.getName() != null && component.getName().equals("caseref"); + } + return false; + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateListPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateListPlaceholderFormatter.java new file mode 100644 index 0000000000..79636e6b61 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateListPlaceholderFormatter.java @@ -0,0 +1,37 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * Formatter for collections of date values in PFQL placeholders. + *+ * This formatter extends {@link DatePlaceholderFormatter} to handle collections of date objects. + * It validates that all items in the collection are supported date types and formats them as a + * comma-separated list wrapped in brackets suitable for MongoDB query syntax. + *
+ *+ * Example output: {@code [2023-01-15T10:30:00Z, 2023-02-20T14:45:00Z, 2023-03-25T08:15:00Z]} + *
+ * + * @see DatePlaceholderFormatter + */ +public class DateListPlaceholderFormatter extends DatePlaceholderFormatter { + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public boolean supports(Object value) { + return value instanceof Collection> + && !((Collection>) value).isEmpty() + && ((Collection>) value).stream().allMatch(item -> super.supports(item)); + } + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public String format(Object value) { + Collection> collOfDates = (Collection>) value; + return wrapInBrackets(collOfDates.stream() + .map(item -> super.format(item)) + .collect(Collectors.joining(", "))); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DatePlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DatePlaceholderFormatter.java new file mode 100644 index 0000000000..5c6c28bc8b --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DatePlaceholderFormatter.java @@ -0,0 +1,61 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.regex.Pattern; + +/** + * Formatter for date placeholders in PFQL queries. + *+ * This formatter handles date values that can be either {@link LocalDate} instances or + * string representations matching the ISO date format (yyyy-MM-dd). It validates and + * formats date values according to the {@code DATE} token specification defined in the + * QueryLang.g4 grammar. + *
+ *+ * Supported date formats: + *
+ * DATE: DIGIT DIGIT DIGIT DIGIT '-' ('0' [1-9] | '1' [0-2]) '-' ('0' [1-9] | [12] DIGIT | '3' [01])
+ *
+ * Example: {@code 2020-03-03}
+ * + * Note: If the grammar changes, this pattern must be updated accordingly. + *
+ */ + protected static final Pattern DATE_PATTERN = Pattern.compile("\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])"); + protected static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + @Override + public boolean supports(Object value) { + return isOfLocalDateType(value) || isOfStringType(value); + } + + @Override + public String format(Object value) { + if (isOfLocalDateType(value)) { + return ((LocalDate) value).format(DATE_FORMATTER); + } + return (String) value; + } + + protected boolean isOfStringType(Object value) { + return value instanceof String && DATE_PATTERN.matcher((String) value).matches(); + } + + protected boolean isOfLocalDateType(Object value) { + return value instanceof LocalDate; + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateTimeListPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateTimeListPlaceholderFormatter.java new file mode 100644 index 0000000000..f35f57b65d --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateTimeListPlaceholderFormatter.java @@ -0,0 +1,37 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * Formatter for handling collections of date-time values in PFQL placeholders. + *+ * This formatter extends {@link DateTimePlaceholderFormatter} to support formatting of collections + * containing date-time objects. It validates that all items in the collection are supported date-time + * types and formats them into a bracketed, comma-separated string representation suitable for + * MongoDB queries. + *
+ *+ * The formatter only supports non-empty collections where every element passes the parent class's + * {@link DateTimePlaceholderFormatter#supports(Object)} validation. + *
+ */ +public class DateTimeListPlaceholderFormatter extends DateTimePlaceholderFormatter { + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public boolean supports(Object value) { + return value instanceof Collection> + && !((Collection>) value).isEmpty() + && ((Collection>) value).stream().allMatch(item -> super.supports(item)); + } + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public String format(Object value) { + Collection> collOfDateTimes = (Collection>) value; + return wrapInBrackets(collOfDateTimes.stream() + .map(item -> super.format(item)) + .collect(Collectors.joining(", "))); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateTimePlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateTimePlaceholderFormatter.java new file mode 100644 index 0000000000..2a232ec59b --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/DateTimePlaceholderFormatter.java @@ -0,0 +1,87 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.util.Date; +import java.util.regex.Pattern; + +/** + * Formatter for datetime placeholders in PFQL queries. + *+ * This formatter handles datetime values in queries by converting them to a standardized string format + * that matches the {@code DATETIME} token defined in the QueryLang grammar. It supports conversion from + * multiple Java time types including {@link LocalDateTime}, {@link Date}, and pre-formatted datetime strings. + *
+ *+ * The formatter produces datetime strings in ISO 8601-like format: {@code yyyy-MM-dd'T'HH:mm:ss[.nnnnnnnnn]}, + * where the fractional seconds part is optional and can have 1 to 9 digits. + *
+ *+ * Examples of supported formats: + *
+ * DATETIME: DATE 'T' ([01] DIGIT | '2' [0-3]) ':' [0-5] DIGIT ':' [0-5] DIGIT ('.' DIGIT+)?
+ *
+ * Example: {@code 2020-03-03T20:00:00} or {@code 2020-03-03T20:00:00.055}
+ * + * Note: If the grammar changes, this pattern must be updated accordingly. + *
+ */ + protected static final Pattern DATETIME_PATTERN = Pattern.compile( + "\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(\\.\\d+)?" + ); + protected static final DateTimeFormatter DATETIME_FORMATTER = new DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd'T'HH:mm:ss") + .optionalStart() + .appendFraction(ChronoField.NANO_OF_SECOND, 1, 9, true) + .optionalEnd() + .toFormatter(); + + @Override + public boolean supports(Object value) { + return isOfLocalDateTimeType(value) || isOfDateType(value) || isOfStringType(value); + } + + @Override + public String format(Object value) { + if (isOfLocalDateTimeType(value)) { + return ((LocalDateTime) value).format(DATETIME_FORMATTER); + } else if (isOfDateType(value)) { + return ((Date) value).toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDateTime() + .format(DATETIME_FORMATTER); + } + + return (String) value; + } + + protected boolean isOfStringType(Object value) { + return value instanceof String && DATETIME_PATTERN.matcher((String) value).matches(); + } + + protected boolean isOfLocalDateTimeType(Object value) { + return value instanceof LocalDateTime; + } + + protected boolean isOfDateType(Object value) { + return value instanceof Date; + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/NumberListPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/NumberListPlaceholderFormatter.java new file mode 100644 index 0000000000..b9b76fbfb3 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/NumberListPlaceholderFormatter.java @@ -0,0 +1,35 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * Formatter for collections of numeric values in PFQL placeholders. + *+ * This formatter extends {@link NumberPlaceholderFormatter} to handle collections of numbers. + * It formats a collection of numeric values by converting each number individually using the + * parent formatter and joining them with commas, wrapped in brackets. + *
+ *+ * Example: A collection [1, 2.5, 3] would be formatted as "(1, 2.5, 3)" + *
+ */ +public class NumberListPlaceholderFormatter extends NumberPlaceholderFormatter { + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public boolean supports(Object value) { + return value instanceof Collection> + && !((Collection>) value).isEmpty() + && ((Collection>) value).stream().allMatch(item -> super.supports(item)); + } + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public String format(Object value) { + Collection> collOfNumbers = (Collection>) value; + return wrapInBrackets(collOfNumbers.stream() + .map(item -> super.format(item)) + .collect(Collectors.joining(", "))); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/NumberPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/NumberPlaceholderFormatter.java new file mode 100644 index 0000000000..5782e1d559 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/NumberPlaceholderFormatter.java @@ -0,0 +1,22 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +/** + * Formatter for converting Number values to their string representation in PFQL queries. + *+ * This formatter handles all numeric types (Integer, Long, Double, Float, etc.) by converting + * them to their string representation using {@link String#valueOf(Object)}. It is used during + * placeholder substitution in PFQL query processing to safely embed numeric values into queries. + *
+ */ +public class NumberPlaceholderFormatter implements QueryLangPlaceholderFormatter { + + @Override + public boolean supports(Object value) { + return value instanceof Number; + } + + @Override + public String format(Object value) { + return String.valueOf(value); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/ObjectIdListPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/ObjectIdListPlaceholderFormatter.java new file mode 100644 index 0000000000..199fdfa238 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/ObjectIdListPlaceholderFormatter.java @@ -0,0 +1,39 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * Formatter for handling collections of ObjectId values in PFQL queries. + *+ * This formatter extends {@link ObjectIdPlaceholderFormatter} to support formatting collections of ObjectId + * values. It validates that all items in the collection are valid + * ObjectId values and formats them as a comma-separated list wrapped in brackets. + *
+ *+ * Example transformation: A collection containing ObjectId("507f1f77bcf86cd799439011") and + * ObjectId("507f191e810c19729de860ea") would be formatted as: + * ('507f1f77bcf86cd799439011', '507f191e810c19729de860ea') + *
+ * + * @see ObjectIdPlaceholderFormatter + */ +public class ObjectIdListPlaceholderFormatter extends ObjectIdPlaceholderFormatter { + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public boolean supports(Object value) { + return value instanceof Collection> + && !((Collection>) value).isEmpty() + && ((Collection>) value).stream().allMatch(item -> super.supports(item)); + } + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public String format(Object value) { + Collection> collOfNumbers = (Collection>) value; + return wrapInBrackets(collOfNumbers.stream() + .map(item -> super.format(item)) + .collect(Collectors.joining(", "))); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/ObjectIdPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/ObjectIdPlaceholderFormatter.java new file mode 100644 index 0000000000..c9a1eb411c --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/ObjectIdPlaceholderFormatter.java @@ -0,0 +1,31 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import org.bson.types.ObjectId; + +/** + * Formatter for converting MongoDB {@link ObjectId} instances into PFQL query string format. + *+ * This formatter is part of the PFQL placeholder formatting system. + * It handles the conversion of {@link ObjectId} objects into their hexadecimal string representation + * wrapped in single quotes, making them suitable for use in MongoDB queries generated from PFQL expressions. + *
+ *+ * When a PFQL query contains placeholder values that are {@link ObjectId} instances, this formatter + * ensures they are properly converted to their string format (e.g., {@code '507f1f77bcf86cd799439011'}) + *
+ * + * @see QueryLangPlaceholderFormatter + * @see ObjectId + */ +public class ObjectIdPlaceholderFormatter implements QueryLangPlaceholderFormatter { + + @Override + public boolean supports(Object value) { + return value instanceof ObjectId; + } + + @Override + public String format(Object value) { + return wrapInSingleQuotes(((ObjectId) value).toHexString()); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/QueryLangPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/QueryLangPlaceholderFormatter.java new file mode 100644 index 0000000000..87f99763d0 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/QueryLangPlaceholderFormatter.java @@ -0,0 +1,34 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +/** + * Interface for formatting placeholder values in PFQL queries. + *+ * Implementations of this interface are responsible for converting Java objects into their + * string representations. Each formatter implementation + * supports specific types of objects and provides custom formatting logic for those types. + *
+ *+ * The formatter provides utility methods for wrapping values in brackets and single quotes, + * which are commonly needed when constructing query strings. + *
+ * + * @see QueryLangPlaceholderHandler + */ +public interface QueryLangPlaceholderFormatter { + boolean supports(Object value); + String format(Object value); + + default String wrapInBrackets(String valueInBrackets) { + if (valueInBrackets == null) { + return "()"; + } + return "(" + valueInBrackets + ")"; + } + + default String wrapInSingleQuotes(Object valueToWrap) { + if (valueToWrap == null) { + return "''"; + } + return "'" + valueToWrap + "'"; + } +} \ No newline at end of file diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/QueryLangPlaceholderHandler.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/QueryLangPlaceholderHandler.java new file mode 100644 index 0000000000..df1960367f --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/QueryLangPlaceholderHandler.java @@ -0,0 +1,60 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * Service responsible for formatting placeholder values in PFQL queries based on their type. + *+ * This handler maintains a list of specialized formatters for different data types and selects + * the appropriate formatter based on the runtime type of the value being formatted. Supported + * types include primitives (boolean, number), collections (lists), temporal types (date, datetime), + * identifiers (ObjectId, version), and reference types (case ref, task ref). + *
+ */ +@Service +public class QueryLangPlaceholderHandler { + private final List+ * This method iterates through the registered formatters and uses the first one that + * supports the given value type. The formatted result is a string representation suitable + * for use in MongoDB queries. + *
+ * + * @param value the placeholder value to format; can be of various types including primitives, + * collections, temporal types, or reference types + * @return the formatted string representation of the value suitable for MongoDB queries + * @throws IllegalArgumentException if no formatter supports the given value type + */ + public String format(Object value) { + return formatters.stream() + .filter(formatter -> formatter.supports(value)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Unsupported placeholder value: " + value)) + .format(value); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/StringListPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/StringListPlaceholderFormatter.java new file mode 100644 index 0000000000..78c4b846d4 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/StringListPlaceholderFormatter.java @@ -0,0 +1,36 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * Formatter for collections of strings in PFQL placeholder substitution. + *+ * This formatter handles collections of string values by formatting each individual string + * using the parent {@link StringPlaceholderFormatter} logic and then combining them into + * a comma-separated list wrapped in brackets. + *
+ *+ * The formatter only supports non-empty collections where all items are supported by the + * parent string formatter. + *
+ */ +public class StringListPlaceholderFormatter extends StringPlaceholderFormatter { + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public boolean supports(Object value) { + return value instanceof Collection> + && !((Collection>) value).isEmpty() + && ((Collection>) value).stream().allMatch(item -> super.supports(item)); + } + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public String format(Object value) { + Collection> collOfStrings = (Collection>) value; + return wrapInBrackets(collOfStrings.stream() + .map(item -> super.format(item)) + .collect(Collectors.joining(", "))); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/StringPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/StringPlaceholderFormatter.java new file mode 100644 index 0000000000..4359c1eb95 --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/StringPlaceholderFormatter.java @@ -0,0 +1,30 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +/** + * Formatter implementation for handling String value placeholders in PFQL queries. + *+ * This formatter is responsible for converting String placeholder values into properly formatted + * query string representations by wrapping them in single quotes. It is part of the placeholder + * handling mechanism that ensures type-safe query construction. + *
+ *+ * The formatter implements {@link QueryLangPlaceholderFormatter} and is automatically selected + * by {@link com.netgrif.application.engine.pfql.service.formatters.QueryLangPlaceholderHandler} + * when processing String-typed placeholder values during query evaluation. + *
+ * + * @see QueryLangPlaceholderFormatter + * @see com.netgrif.application.engine.pfql.service.formatters.QueryLangPlaceholderHandler + */ +public class StringPlaceholderFormatter implements QueryLangPlaceholderFormatter { + + @Override + public boolean supports(Object value) { + return value instanceof String; + } + + @Override + public String format(Object value) { + return wrapInSingleQuotes(value); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/TaskRefPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/TaskRefPlaceholderFormatter.java new file mode 100644 index 0000000000..f811ffc00e --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/TaskRefPlaceholderFormatter.java @@ -0,0 +1,36 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import com.netgrif.application.engine.petrinet.domain.dataset.TaskField; + +import java.util.stream.Collectors; + +/** + * Formatter for TaskField placeholders in PFQL queries. + *+ * This formatter handles the conversion of {@link TaskField} values into properly formatted + * query strings. It wraps task reference values in brackets and quotes. + *
+ *+ * If the TaskField contains null values, it returns an empty bracket pair "()". + * Otherwise, it formats each task reference value by wrapping it in single quotes and + * joining them with commas within brackets. + *
+ */ +public class TaskRefPlaceholderFormatter implements QueryLangPlaceholderFormatter { + + @Override + public boolean supports(Object value) { + return value instanceof TaskField; + } + + @Override + public String format(Object value) { + TaskField field = (TaskField) value; + if (field.getValue() == null) { + return "()"; + } + return wrapInBrackets(field.getValue().stream() + .map(this::wrapInSingleQuotes) + .collect(Collectors.joining(", "))); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/VersionListPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/VersionListPlaceholderFormatter.java new file mode 100644 index 0000000000..8040d9c99a --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/VersionListPlaceholderFormatter.java @@ -0,0 +1,38 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * Formatter for collections of version values in PFQL queries. + *+ * This formatter extends {@link VersionPlaceholderFormatter} to handle collections of version objects. + * It validates that all items in the collection are supported version values and formats them as a + * comma-separated list wrapped in brackets. + *
+ *+ * The formatter only supports non-empty collections where every element is a valid version value + * as determined by the parent {@link VersionPlaceholderFormatter#supports(Object)} method. + *
+ * + * @see VersionPlaceholderFormatter + */ +public class VersionListPlaceholderFormatter extends VersionPlaceholderFormatter { + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public boolean supports(Object value) { + return value instanceof Collection> + && !((Collection>) value).isEmpty() + && ((Collection>) value).stream().allMatch(item -> super.supports(item)); + } + + @Override + @SuppressWarnings("Convert2MethodRef") // method reference does not work to super calls + public String format(Object value) { + Collection> collOfStrings = (Collection>) value; + return wrapInBrackets(collOfStrings.stream() + .map(item -> super.format(item)) + .collect(Collectors.joining(", "))); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/formatters/VersionPlaceholderFormatter.java b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/VersionPlaceholderFormatter.java new file mode 100644 index 0000000000..1f6777acdd --- /dev/null +++ b/src/main/java/com/netgrif/application/engine/pfql/service/formatters/VersionPlaceholderFormatter.java @@ -0,0 +1,24 @@ +package com.netgrif.application.engine.pfql.service.formatters; + +import com.netgrif.application.engine.petrinet.domain.version.Version; + +/** + * Formatter for Version objects used in PFQL queries. + *+ * This formatter handles the conversion of {@link Version} objects into their string + * representation for use in PFQL query placeholders. It supports Version objects and + * formats them by calling their {@code toString()} method. + *
+ */ +public class VersionPlaceholderFormatter implements QueryLangPlaceholderFormatter { + + @Override + public boolean supports(Object value) { + return value instanceof Version; + } + + @Override + public String format(Object value) { + return value.toString(); + } +} diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/processresource/ProcessSearchService.java b/src/main/java/com/netgrif/application/engine/pfql/service/processresource/ProcessSearchService.java index 4f94533803..690b0ebfe4 100644 --- a/src/main/java/com/netgrif/application/engine/pfql/service/processresource/ProcessSearchService.java +++ b/src/main/java/com/netgrif/application/engine/pfql/service/processresource/ProcessSearchService.java @@ -2,25 +2,34 @@ import com.netgrif.application.engine.petrinet.domain.PetriNet; import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService; +import com.netgrif.application.engine.pfql.domain.antlr4.QueryLangParser; import com.netgrif.application.engine.pfql.domain.enums.QueryType; -import com.netgrif.application.engine.pfql.service.IResourceSearchService; +import com.netgrif.application.engine.pfql.service.AbstractResourceSearchService; import com.netgrif.application.engine.pfql.service.QueryLangEvaluator; -import lombok.RequiredArgsConstructor; +import com.netgrif.application.engine.pfql.service.formatters.QueryLangPlaceholderHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; -import java.util.Optional; - -import static com.netgrif.application.engine.pfql.service.utils.SearchUtils.evaluateQuery; - +/** + * Service for searching and querying process resources using PFQL. + *+ * This service provides methods to search for processes, count processes, and check process existence + * based on PFQL query strings or evaluated query objects. It delegates the actual MongoDB + * queries to the {@link IPetriNetService}. Future implementations will support Elasticsearch as an alternative + * search backend. + *
+ */ @Slf4j @Service -@RequiredArgsConstructor -public class ProcessSearchService implements IResourceSearchService- * This method parses the query string into an evaluator and delegates to - * {@link #searchOne(QueryLangEvaluator)} for execution. - *
- * - * @param queryString the query string to be evaluated and executed - * @return the matching {@link PetriNet} process, or null if no match is found - * @throws IllegalArgumentException if the query string results in a multiple-results query - */ @Override - public PetriNet searchOne(String queryString) { - log.debug("Searching for single process with query: {}", queryString); - return searchOne(evaluateQuery(queryString)); + protected String ensurePrefix(String query, boolean isMulti) { + return doEnsurePrefix(query, isMulti, QueryLangParser.PROCESSES, QueryLangParser.PROCESS); } /** @@ -62,11 +59,7 @@ public PetriNet searchOne(String queryString) { * @throws IllegalArgumentException if evaluator is null or configured for multiple results */ @Override - public PetriNet searchOne(QueryLangEvaluator evaluator) { - checkEvaluatorNotNull(evaluator); - checkEvaluatorIsSingle(evaluator); - checkEvaluatorResourceType(evaluator); - + protected PetriNet doSearchOne(QueryLangEvaluator evaluator) { // todo implement Elasticsearch search (service layer and evaluator layer) log.debug("Searching for single process using MongoDB"); @@ -76,23 +69,6 @@ public PetriNet searchOne(QueryLangEvaluator evaluator) { return result; } - /** - * Searches for all processes that match the provided query string. - *- * This method parses the query string into an evaluator and delegates to - * {@link #searchAll(QueryLangEvaluator)} for execution. - *
- * - * @param queryString the query string to be evaluated and executed - * @return a page of matching {@link PetriNet} processes - * @throws IllegalArgumentException if the query string results in a single-result query - */ - @Override - public Page
@@ -106,11 +82,7 @@ public Page
- * This method parses the query string into an evaluator and delegates to
- * {@link #count(QueryLangEvaluator)} for execution.
- *
@@ -149,10 +105,7 @@ public long count(String queryString) {
* @throws IllegalArgumentException if evaluator is null
*/
@Override
- public long count(QueryLangEvaluator evaluator) {
- checkEvaluatorNotNull(evaluator);
- checkEvaluatorResourceType(evaluator);
-
+ protected long doCount(QueryLangEvaluator evaluator) {
// todo implement Elasticsearch search (service layer and evaluator layer)
log.debug("Counting processes using MongoDB");
@@ -162,22 +115,6 @@ public long count(QueryLangEvaluator evaluator) {
return result;
}
- /**
- * Checks whether any processes exist that match the provided query string.
- *
- * This method parses the query string into an evaluator and delegates to
- * {@link #exists(QueryLangEvaluator)} for execution.
- *
@@ -192,10 +129,7 @@ public boolean exists(String queryString) {
* @throws IllegalArgumentException if evaluator is null
*/
@Override
- public boolean exists(QueryLangEvaluator evaluator) {
- checkEvaluatorNotNull(evaluator);
- checkEvaluatorResourceType(evaluator);
-
+ protected boolean doExists(QueryLangEvaluator evaluator) {
// todo implement Elasticsearch search (service layer and evaluator layer)
log.debug("Checking existence of processes using MongoDB");
diff --git a/src/main/java/com/netgrif/application/engine/pfql/service/taskresource/TaskSearchService.java b/src/main/java/com/netgrif/application/engine/pfql/service/taskresource/TaskSearchService.java
index 4c53bba621..9e87c245bb 100644
--- a/src/main/java/com/netgrif/application/engine/pfql/service/taskresource/TaskSearchService.java
+++ b/src/main/java/com/netgrif/application/engine/pfql/service/taskresource/TaskSearchService.java
@@ -3,12 +3,13 @@
import com.netgrif.application.engine.auth.service.interfaces.IUserService;
import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService;
import com.netgrif.application.engine.elastic.web.requestbodies.ElasticTaskSearchRequest;
+import com.netgrif.application.engine.pfql.domain.antlr4.QueryLangParser;
import com.netgrif.application.engine.pfql.domain.enums.QueryType;
-import com.netgrif.application.engine.pfql.service.IResourceSearchService;
+import com.netgrif.application.engine.pfql.service.AbstractResourceSearchService;
import com.netgrif.application.engine.pfql.service.QueryLangEvaluator;
+import com.netgrif.application.engine.pfql.service.formatters.QueryLangPlaceholderHandler;
import com.netgrif.application.engine.workflow.domain.Task;
import com.netgrif.application.engine.workflow.service.interfaces.ITaskService;
-import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.Page;
@@ -18,8 +19,6 @@
import java.util.List;
-import static com.netgrif.application.engine.pfql.service.utils.SearchUtils.evaluateQuery;
-
/**
* Service implementation for searching Task resources using query language expressions.
*
@@ -31,12 +30,18 @@
*/
@Slf4j
@Service
-@RequiredArgsConstructor
-public class TaskSearchService implements IResourceSearchService
@@ -118,11 +100,7 @@ public Page
@@ -163,10 +129,7 @@ public long count(String queryString) {
* @throws IllegalArgumentException if evaluator is null
*/
@Override
- public long count(QueryLangEvaluator evaluator) {
- checkEvaluatorNotNull(evaluator);
- checkEvaluatorResourceType(evaluator);
-
+ protected long doCount(QueryLangEvaluator evaluator) {
log.debug("Counting tasks using {}", evaluator.getSearchWithElastic() ? "Elasticsearch" : "MongoDB");
if (evaluator.getSearchWithElastic()) {
log.trace("Executing Elasticsearch count query: {}", evaluator.getFullElasticQuery());
@@ -181,18 +144,6 @@ public long count(QueryLangEvaluator evaluator) {
}
}
- /**
- * Checks whether any tasks exist that match the provided query string.
- *
- * @param queryString the query string to be evaluated and executed
- * @return true if at least one matching task exists, false otherwise
- */
- @Override
- public boolean exists(String queryString) {
- log.debug("Checking existence of task with query: {}", queryString);
- return exists(evaluateQuery(queryString));
- }
-
/**
* Checks whether any tasks exist using a pre-evaluated query expression.
*
@@ -205,10 +156,7 @@ public boolean exists(String queryString) {
* @throws IllegalArgumentException if evaluator is null
*/
@Override
- public boolean exists(QueryLangEvaluator evaluator) {
- checkEvaluatorNotNull(evaluator);
- checkEvaluatorResourceType(evaluator);
-
+ protected boolean doExists(QueryLangEvaluator evaluator) {
log.debug("Checking existence of tasks using {}", evaluator.getSearchWithElastic() ? "Elasticsearch" : "MongoDB");
if (evaluator.getSearchWithElastic()) {
log.trace("Executing Elasticsearch exists query: {}", evaluator.getFullElasticQuery());
@@ -229,7 +177,7 @@ public boolean exists(QueryLangEvaluator evaluator) {
* @param elasticQuery the Elasticsearch query string
* @return the count of matching tasks
*/
- private long countTasksElastic(String elasticQuery) {
+ protected long countTasksElastic(String elasticQuery) {
ElasticTaskSearchRequest taskSearchRequest = new ElasticTaskSearchRequest();
taskSearchRequest.query = elasticQuery;
return elasticTaskService.count(List.of(taskSearchRequest), userService.getLoggedOrSystem().transformToLoggedUser(),
@@ -243,7 +191,7 @@ private long countTasksElastic(String elasticQuery) {
* @param pageable the pagination information
* @return a page of matching tasks
*/
- private Page
@@ -19,17 +18,17 @@
* based on PFQL query strings or evaluated query objects. It delegates the actual MongoDB
* queries to the {@link IUserService}.
*