diff --git a/src/main/java/sirius/db/jdbc/constraints/SQLQueryCompiler.java b/src/main/java/sirius/db/jdbc/constraints/SQLQueryCompiler.java index 32a48a641..d845d24e4 100644 --- a/src/main/java/sirius/db/jdbc/constraints/SQLQueryCompiler.java +++ b/src/main/java/sirius/db/jdbc/constraints/SQLQueryCompiler.java @@ -19,6 +19,7 @@ import sirius.kernel.commons.Tuple; import java.util.List; +import java.util.Optional; /** * Provides a query compiler for {@link sirius.db.jdbc.SmartQuery} and {@link SQLFilterFactory}. @@ -42,22 +43,26 @@ public SQLQueryCompiler(FilterFactory factory, @Override protected SQLConstraint compileSearchToken(Mapping field, QueryField.Mode mode, String value) { + Optional caseOptimizedValue = getCaseOptimizedValue(field, value); switch (mode) { case EQUAL: - return factory.eq(field, value); + return factory.eq(field, caseOptimizedValue.orElse(value)); case LIKE: - if (value.contains("*")) { + if (caseOptimizedValue.isEmpty() && value.contains("*")) { return OMA.FILTERS.like(field).matches(value).ignoreCase().build(); } else { - return factory.eq(field, value); + return factory.eq(field, caseOptimizedValue.orElse(value)); } case PREFIX: - if (value.contains("*")) { + if (caseOptimizedValue.isEmpty() && value.contains("*")) { return OMA.FILTERS.like(field).startsWith(value).ignoreCase().build(); } else { - return OMA.FILTERS.like(field).startsWith(value).build(); + return OMA.FILTERS.like(field).startsWith(caseOptimizedValue.orElse(value)).build(); } default: + if (caseOptimizedValue.isPresent()) { + return OMA.FILTERS.like(field).contains(caseOptimizedValue.get()).build(); + } return OMA.FILTERS.like(field).contains(value).ignoreCase().build(); } } diff --git a/src/main/java/sirius/db/mixing/annotations/LowerCase.java b/src/main/java/sirius/db/mixing/annotations/LowerCase.java index f7c926159..ffeb35bc8 100644 --- a/src/main/java/sirius/db/mixing/annotations/LowerCase.java +++ b/src/main/java/sirius/db/mixing/annotations/LowerCase.java @@ -17,7 +17,8 @@ /** * Marks a string property as auto lower-cased. *

- * The value of this property will be lower-cased before it is written to the database. + * The value of this property will be lower-cased before it is written to the database. Also, it is used to optimize + * some system generated queries. */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/sirius/db/mixing/annotations/UpperCase.java b/src/main/java/sirius/db/mixing/annotations/UpperCase.java index 10e8e305c..77bd4f27f 100644 --- a/src/main/java/sirius/db/mixing/annotations/UpperCase.java +++ b/src/main/java/sirius/db/mixing/annotations/UpperCase.java @@ -17,7 +17,8 @@ /** * Marks a string property as auto upper-cased. *

- * The value of this property will be upper-cased before it is written to the database. + * The value of this property will be upper-cased before it is written to the database. Also, it is used to optimize + * some system generated queries. */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/sirius/db/mixing/query/QueryCompiler.java b/src/main/java/sirius/db/mixing/query/QueryCompiler.java index 666ff7a76..b08369409 100644 --- a/src/main/java/sirius/db/mixing/query/QueryCompiler.java +++ b/src/main/java/sirius/db/mixing/query/QueryCompiler.java @@ -11,6 +11,8 @@ import sirius.db.mixing.EntityDescriptor; import sirius.db.mixing.Mapping; import sirius.db.mixing.Property; +import sirius.db.mixing.annotations.LowerCase; +import sirius.db.mixing.annotations.UpperCase; import sirius.db.mixing.properties.BaseEntityRefListProperty; import sirius.db.mixing.properties.BaseEntityRefProperty; import sirius.db.mixing.properties.LocalDateProperty; @@ -35,6 +37,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -257,8 +260,7 @@ private C parseExpression() { skipWhitespace(); if ((reader.current().is('!') || reader.current().is('-'))) { - if (reader.next().isWhitespace() ||reader.next() - .isEndOfInput()) { + if (reader.next().isWhitespace() || reader.next().isEndOfInput()) { // If there is a single "-" or "!" in a string like "foo - bar", we simly skip the dash // as it is ignored by the indexing tokenizer anyway... reader.consume(); @@ -789,4 +791,22 @@ private C parseTag() { return null; } + + /** + * Returns the optimized value for the given field respecting the Sirius DB casing annotations. + * + * @param field the field to query for + * @param value the value to query + * @return the optimized value to use in a constraint if any, or an Optional.empty() if no optimization is possible + */ + protected Optional getCaseOptimizedValue(Mapping field, String value) { + Property property = resolveProperty(field.toString()).getSecond(); + if (property.isAnnotationPresent(LowerCase.class)) { + return Optional.of(value.toLowerCase()); + } + if (property.isAnnotationPresent(UpperCase.class)) { + return Optional.of(value.toUpperCase()); + } + return Optional.empty(); + } } diff --git a/src/main/java/sirius/db/mixing/query/QueryField.java b/src/main/java/sirius/db/mixing/query/QueryField.java index 3fc7a3763..5e0c6e665 100644 --- a/src/main/java/sirius/db/mixing/query/QueryField.java +++ b/src/main/java/sirius/db/mixing/query/QueryField.java @@ -24,8 +24,8 @@ public enum Mode { EQUAL, LIKE, PREFIX, CONTAINS } - private Mapping field; - private Mode mode; + private final Mapping field; + private final Mode mode; private QueryField(Mapping field, Mode mode) { this.field = field; diff --git a/src/main/java/sirius/db/mongo/constraints/MongoQueryCompiler.java b/src/main/java/sirius/db/mongo/constraints/MongoQueryCompiler.java index c82e1a921..d14182039 100644 --- a/src/main/java/sirius/db/mongo/constraints/MongoQueryCompiler.java +++ b/src/main/java/sirius/db/mongo/constraints/MongoQueryCompiler.java @@ -31,7 +31,7 @@ public class MongoQueryCompiler extends QueryCompiler { private static final Mapping FULLTEXT_MAPPING = Mapping.named("$text"); /** - * Represents an artificial field which generates a search using a $text filter. Therefore + * Represents an artificial field which generates a search using a $text filter. Therefore, * an appropriate text index has to be present. */ public static final QueryField FULLTEXT = QueryField.contains(FULLTEXT_MAPPING); @@ -58,7 +58,7 @@ protected MongoConstraint compileSearchToken(Mapping field, QueryField.Mode mode } if (mode == QueryField.Mode.EQUAL) { - return factory.eq(field, value); + return factory.eq(field, getCaseOptimizedValue(field, value).orElse(value)); } else if (mode == QueryField.Mode.PREFIX) { return QueryBuilder.FILTERS.prefix(field, value); } else {