diff --git a/README.md b/README.md index a32d9d0..4cd5524 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Currently the plugin supports array, hstore and json fields as well as some quer * [Is Empty or Contains](#is-empty-or-contains) * [Equals](#equals) * [Not Equals](#not-equals) + * [Ilike](#ilike) * [Hstore](#hstore) * [Grails 2.2.5 and 2.3.1+](#grails-225-and-231) * [Old Grails versions](#old-grails-versions) @@ -292,6 +293,20 @@ def result = Like.withCriteria { } ``` +#### ILike + +With this criteria you can get all the rows that are ilike to a value. To use it just use the new criteria `pgArrayILike`. + +It only can be used on arrays of string. + +It uses the ilike syntaxis, so you can do for example: + +```groovy +def result = Like.withCriteria { + pgArrayILike 'favoriteMovies', "%tarwar%" +} +``` + ### Hstore diff --git a/src/groovy/net/kaleidos/hibernate/postgresql/criteria/ArrayCriterias.groovy b/src/groovy/net/kaleidos/hibernate/postgresql/criteria/ArrayCriterias.groovy index 9639ff3..6f20f13 100644 --- a/src/groovy/net/kaleidos/hibernate/postgresql/criteria/ArrayCriterias.groovy +++ b/src/groovy/net/kaleidos/hibernate/postgresql/criteria/ArrayCriterias.groovy @@ -1,7 +1,7 @@ package net.kaleidos.hibernate.postgresql.criteria - import grails.orm.HibernateCriteriaBuilder import net.kaleidos.hibernate.criterion.array.PgArrayExpression +import net.kaleidos.hibernate.criterion.array.PgArrayILikeFunction import net.kaleidos.hibernate.criterion.array.PgEmptinessExpression class ArrayCriterias { @@ -15,6 +15,7 @@ class ArrayCriterias { addIsEmptyOrContainsOperator() addEqualsOperator() addNotEqualsOperator() + addILikeOperator() } private void addContainsOperator() { @@ -179,4 +180,24 @@ class ArrayCriterias { return addToCriteria(new PgArrayExpression(propertyName, propertyValue, "<>")) } } + + private void addILikeOperator() { + /** + * Creates a "ilike in native array" Criterion based on the specified property name and value + * @param propertyName The property name + * @param propertyValue The property value + * @return A Criterion instance + */ + HibernateCriteriaBuilder.metaClass.pgArrayILike = { String propertyName, String propertyValue -> + if (!validateSimpleExpression()) { + throwRuntimeException(new IllegalArgumentException("Call to [pgArrayILike] with propertyName [" + + propertyName + "] and value [" + propertyValue + "] not allowed here.")) + } + + propertyName = calculatePropertyName(propertyName) + propertyValue = calculatePropertyValue(propertyValue) + + return addToCriteria(new PgArrayILikeFunction(propertyName, propertyValue)) + } + } } diff --git a/src/java/net/kaleidos/hibernate/criterion/array/PgArrayILikeFunction.java b/src/java/net/kaleidos/hibernate/criterion/array/PgArrayILikeFunction.java new file mode 100644 index 0000000..57b3efb --- /dev/null +++ b/src/java/net/kaleidos/hibernate/criterion/array/PgArrayILikeFunction.java @@ -0,0 +1,41 @@ +package net.kaleidos.hibernate.criterion.array; + +import org.hibernate.Criteria; +import org.hibernate.HibernateException; +import org.hibernate.annotations.common.util.StringHelper; +import org.hibernate.criterion.CriteriaQuery; +import org.hibernate.criterion.Criterion; +import org.hibernate.engine.spi.TypedValue; +import org.hibernate.type.StringType; + +/** + * Constrains a value "ilike" in an array + */ +public class PgArrayILikeFunction implements Criterion { + + private static final long serialVersionUID = 7475136611436979257L; + + private final String propertyName; + private final String value; + + protected PgArrayILikeFunction(String propertyName, String value) { + this.propertyName = propertyName; + this.value = value; + } + + @Override + public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { + String[] columns = StringHelper.suffix(criteriaQuery.findColumns(propertyName, criteria), ""); + for (int i = 0; i < columns.length; i++) { + columns[i] = "text(" + columns[i] + ") ilike ?"; + } + return StringHelper.join(" and ", columns); + } + + @Override + public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { + return new TypedValue[]{ + new TypedValue(new StringType(), value) + }; + } +} diff --git a/test/integration/net/kaleidos/hibernate/array/PgILikeCriteriaTestServiceIntegrationSpec.groovy b/test/integration/net/kaleidos/hibernate/array/PgILikeCriteriaTestServiceIntegrationSpec.groovy new file mode 100644 index 0000000..5bc593f --- /dev/null +++ b/test/integration/net/kaleidos/hibernate/array/PgILikeCriteriaTestServiceIntegrationSpec.groovy @@ -0,0 +1,35 @@ +package net.kaleidos.hibernate.array + +import spock.lang.Specification +import spock.lang.Unroll +import test.criteria.array.Like + +class PgILikeCriteriaTestServiceIntegrationSpec extends Specification { + + def pgArrayTestSearchService + + @Unroll + void "check ilike for #movie in an array of strings"() { + setup: + new Like(favoriteMovies: ["The Matrix", "The Lord of the Rings"]).save() + new Like(favoriteMovies: ["Spiderman", "Blade Runner", "Starwars"]).save() + new Like(favoriteMovies: ["Starwars"]).save() + new Like(favoriteMovies: ["Romeo & Juliet", "Blade Runner", "The Lord of the Rings"]).save() + new Like(favoriteMovies: []).save() + + when: + def result = pgArrayTestSearchService.search('favoriteMovies', 'pgArrayILike', movie) + + then: + result.size() == resultSize + + where: + movie | resultSize + "%tarwar%" | 2 + "%ider%" | 1 + "%Suspects%" | 0 + "" | 0 + "%" | 5 + } + +}