Skip to content

Commit

Permalink
Merge pull request #648 from scireum/feature/SIRI-951_optimize_querie…
Browse files Browse the repository at this point in the history
…s_casing

Optimizes some system generates queries based on casing annotations
  • Loading branch information
mkeckmkeck authored Apr 9, 2024
2 parents 1b62049 + 96a909a commit 23a3f98
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 13 deletions.
15 changes: 10 additions & 5 deletions src/main/java/sirius/db/jdbc/constraints/SQLQueryCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
Expand All @@ -42,22 +43,26 @@ public SQLQueryCompiler(FilterFactory<SQLConstraint> factory,

@Override
protected SQLConstraint compileSearchToken(Mapping field, QueryField.Mode mode, String value) {
Optional<String> 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();
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/sirius/db/mixing/annotations/LowerCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
/**
* Marks a string property as auto lower-cased.
* <p>
* 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)
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/sirius/db/mixing/annotations/UpperCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
/**
* Marks a string property as auto upper-cased.
* <p>
* 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)
Expand Down
24 changes: 22 additions & 2 deletions src/main/java/sirius/db/mixing/query/QueryCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<String> 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();
}
}
4 changes: 2 additions & 2 deletions src/main/java/sirius/db/mixing/query/QueryField.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class MongoQueryCompiler extends QueryCompiler<MongoConstraint> {
private static final Mapping FULLTEXT_MAPPING = Mapping.named("$text");

/**
* Represents an artificial field which generates a search using a <tt>$text</tt> filter. Therefore
* Represents an artificial field which generates a search using a <tt>$text</tt> filter. Therefore,
* an appropriate <b>text</b> index has to be present.
*/
public static final QueryField FULLTEXT = QueryField.contains(FULLTEXT_MAPPING);
Expand All @@ -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 {
Expand Down

0 comments on commit 23a3f98

Please sign in to comment.