Skip to content

Commit

Permalink
Merge pull request #257 from xenit-eu/starts-with-search
Browse files Browse the repository at this point in the history
Add Text QuerydslPredicateFactories for starts with search [ACC-1519]
  • Loading branch information
NielsCW authored Aug 8, 2024
2 parents 7c8ccd5 + f843a0f commit e9455c3
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.contentgrid.spring.data.support.auditing.v1.AuditMetadata;
import com.contentgrid.spring.querydsl.annotation.CollectionFilterParam;
import com.contentgrid.spring.querydsl.predicate.EntityId;
import com.contentgrid.spring.querydsl.predicate.EqualsIgnoreCase;
import com.contentgrid.spring.querydsl.predicate.Text.EqualsIgnoreCase;
import com.contentgrid.spring.test.fixture.invoicing.model.support.Content;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.contentgrid.spring.data.support.auditing.v1.AuditMetadata;
import com.contentgrid.spring.querydsl.annotation.CollectionFilterParam;
import com.contentgrid.spring.querydsl.predicate.EntityId;
import com.contentgrid.spring.querydsl.predicate.EqualsIgnoreCase;
import com.contentgrid.spring.querydsl.predicate.Text.EqualsIgnoreCase;
import com.contentgrid.spring.querydsl.predicate.None;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.contentgrid.spring.test.fixture.invoicing.model;

import com.contentgrid.spring.querydsl.annotation.CollectionFilterParam;
import com.contentgrid.spring.querydsl.predicate.EqualsIgnoreCase;
import com.contentgrid.spring.querydsl.predicate.Text.EqualsIgnoreCase;
import com.contentgrid.spring.test.fixture.invoicing.model.support.Content;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
Expand Down
1 change: 1 addition & 0 deletions contentgrid-spring-querydsl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
implementation platform(project(':contentgrid-spring-boot-platform'))
implementation 'org.springframework.data:spring-data-commons'
implementation 'com.contentgrid.thunx:spring-data-querydsl-predicate-injector'
implementation 'org.hibernate.orm:hibernate-core'

testAnnotationProcessor platform(project(':contentgrid-spring-boot-platform'))
testAnnotationProcessor 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.contentgrid.spring.querydsl.hibernate;

import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.FunctionContributor;

public class PostgresNormalizeFunctionContributor implements FunctionContributor {

@Override
public void contributeFunctions(FunctionContributions functionContributions) {
var returnType = functionContributions.getTypeConfiguration().getBasicTypeForJavaType(String.class);
functionContributions.getFunctionRegistry().registerPattern("normalize", "normalize(?1, NFKC)", returnType);
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
package com.contentgrid.spring.querydsl.predicate;

import com.contentgrid.spring.querydsl.mapping.UnsupportedCollectionFilterPredicatePathTypeException;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.StringPath;
import java.util.Collection;
import java.util.Optional;

/**
* Compares strings in a case-insensitive way.
*
* This predicate only supports strings and can not be used with other types.
* @deprecated Use {@link Text.EqualsIgnoreCase} instead.
*/
public class EqualsIgnoreCase extends AbstractSimpleQuerydslPredicateFactory<StringPath, String> {

@Override
public StringPath coercePath(Path<?> path) {
if(path instanceof StringPath stringPath) {
return stringPath;
}
throw new UnsupportedCollectionFilterPredicatePathTypeException(this, path, StringPath.class);
}

@Override
protected Optional<Predicate> bindCoerced(StringPath path, Collection<? extends String> values) {
if(values.isEmpty()) {
return Optional.empty();
}
if(values.size() == 1) {
var value = values.iterator().next();
return Optional.of(path.equalsIgnoreCase(value));
}
return Optional.of(path.lower().in(values.stream().map(String::toLowerCase).toList()));
}
@Deprecated(since = "v0.15.2", forRemoval = true)
public class EqualsIgnoreCase extends Text.EqualsIgnoreCase {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package com.contentgrid.spring.querydsl.predicate;

import com.contentgrid.spring.querydsl.mapping.UnsupportedCollectionFilterPredicatePathTypeException;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.core.types.dsl.StringPath;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.Collection;
import java.util.Optional;
import java.util.function.BiFunction;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.UtilityClass;

@UtilityClass
public class Text {

@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
private abstract static class AbstractStringPredicateFactory extends AbstractSimpleQuerydslPredicateFactory<StringPath, String> {
private final BiFunction<StringPath, String, BooleanExpression> stringExpressionMapper;

@Override
protected StringPath coercePath(Path<?> path) {
if(path instanceof StringPath stringPath) {
return stringPath;
}
throw new UnsupportedCollectionFilterPredicatePathTypeException(this, path, StringPath.class);
}

@Override
protected Optional<Predicate> bindCoerced(StringPath path, Collection<? extends String> values) {
if(values.isEmpty()) {
return Optional.empty();
}
if(values.size() == 1) {
var value = values.iterator().next();
return Optional.of(stringExpressionMapper.apply(path, value));
}

// If there are multiple values, return whether any of the provided values matches
BooleanBuilder builder = new BooleanBuilder();
values.forEach(value -> builder.or(stringExpressionMapper.apply(path, value)));

return Optional.ofNullable(builder.getValue());
}
}

/**
* Filters items down to only items matching the supplied value in a case-insensitive way.
* <p>
* This predicate only supports {@link String}s, and can not be used with other types.
*/
public static class EqualsIgnoreCase extends AbstractStringPredicateFactory {

public EqualsIgnoreCase() {
super(StringExpression::equalsIgnoreCase);
}

@Override
protected Optional<Predicate> bindCoerced(StringPath path, Collection<? extends String> values) {
if (values.size() <= 1) {
return super.bindCoerced(path, values);
}
return Optional.of(path.lower().in(values.stream().map(String::toLowerCase).toList()));
}
}

/**
* Filters items down to only items starting with the supplied value.
* <p>
* This predicate only supports {@link String}s, and can not be used with other types.
*/
public static class StartsWith extends AbstractStringPredicateFactory {

public StartsWith() {
super(StringExpression::startsWith);
}
}

/**
* Filters items down to only items starting with the supplied value in a case-insensitive way.
* <p>
* This predicate only supports {@link String}s, and can not be used with other types.
*/
public static class StartsWithIgnoreCase extends AbstractStringPredicateFactory {

public StartsWithIgnoreCase() {
super(StringExpression::startsWithIgnoreCase);
}
}

/**
* Filters items down to only items matching the supplied value in a NFKC normalized way.
* <p>
* This predicate only supports {@link String}s, and can not be used with other types.
*/
public static class EqualsNormalized extends AbstractStringPredicateFactory {

public EqualsNormalized() {
super((expr, value) -> normalize(expr).eq(Normalizer.normalize(value, Form.NFKC)));
}

@Override
protected Optional<Predicate> bindCoerced(StringPath path, Collection<? extends String> values) {
if (values.size() <= 1) {
return super.bindCoerced(path, values);
}

return Optional.of(normalize(path).in(values.stream()
.map(value -> Normalizer.normalize(value, Form.NFKC))
.toList()));
}
}

/**
* Filters items down to only items matching the supplied value in a case-insensitive, NFKC normalized way.
* <p>
* This predicate only supports {@link String}s, and can not be used with other types.
*/
public static class EqualsIgnoreCaseNormalized extends AbstractStringPredicateFactory {

public EqualsIgnoreCaseNormalized() {
super((expr, value) -> normalize(expr).equalsIgnoreCase(Normalizer.normalize(value, Form.NFKC)));
}

@Override
protected Optional<Predicate> bindCoerced(StringPath path, Collection<? extends String> values) {
if (values.size() <= 1) {
return super.bindCoerced(path, values);
}

return Optional.of(normalize(path).lower().in(values.stream()
.map(value -> Normalizer.normalize(value, Form.NFKC).toLowerCase())
.toList()));
}
}

/**
* Filters items down to only items starting with the supplied value in a NFKC normalized way.
* <p>
* This predicate only supports {@link String}s, and can not be used with other types.
*/
public static class StartsWithIgnoreCaseNormalized extends AbstractStringPredicateFactory {

protected StartsWithIgnoreCaseNormalized() {
super((expr, value) -> normalize(expr).startsWithIgnoreCase(Normalizer.normalize(value, Form.NFKC)));
}
}

/**
* Filters items down to only items starting with the supplied value in a case-insensitive, NFKC normalized way.
* <p>
* This predicate only supports {@link String}s, and can not be used with other types.
*/
public static class StartsWithNormalized extends AbstractStringPredicateFactory {

protected StartsWithNormalized() {
super((expr, value) -> normalize(expr).startsWith(Normalizer.normalize(value, Form.NFKC)));
}
}

static StringExpression normalize(StringExpression expr) {
return Expressions.stringTemplate("normalize({0s})", expr);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.contentgrid.spring.querydsl.hibernate.PostgresNormalizeFunctionContributor

This file was deleted.

Loading

0 comments on commit e9455c3

Please sign in to comment.