diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java index 281ee9cc..e59038e0 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java @@ -34,6 +34,7 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.logging.QLogger; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.CriteriaOption; import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; @@ -268,6 +269,11 @@ public class BackendQueryFilterUtils String regex = sqlLikeToRegex(criterionValue); + if(criterion.hasOption(CriteriaOption.CASE_INSENSITIVE)) + { + return (stringValue.toLowerCase().matches(regex.toLowerCase())); + } + return (stringValue.matches(regex)); } @@ -427,6 +433,23 @@ public class BackendQueryFilterUtils } } + if(criterion.hasOption(CriteriaOption.CASE_INSENSITIVE)) + { + if(CollectionUtils.nullSafeHasContents(criterion.getValues())) + { + if(criterion.getValues().get(0) instanceof String) + { + for(Serializable criterionValue : criterion.getValues()) + { + if(criterionValue instanceof String criterionValueString && value instanceof String valueString && criterionValueString.equalsIgnoreCase(valueString)) + { + return (true); + } + } + } + } + } + if(value == null || !criterion.getValues().contains(value)) { return (false); @@ -456,6 +479,14 @@ public class BackendQueryFilterUtils value = String.valueOf(value); } + if(criterion.hasOption(CriteriaOption.CASE_INSENSITIVE)) + { + if(value instanceof String valueString && criteriaValue instanceof String criteriaValueString && valueString.equalsIgnoreCase(criteriaValueString)) + { + return (true); + } + } + if(!value.equals(criteriaValue)) { return (false); @@ -473,6 +504,14 @@ public class BackendQueryFilterUtils String stringValue = getStringFieldValue(value, fieldName, criterion); String criterionValue = getFirstStringCriterionValue(criterion); + if(criterion.hasOption(CriteriaOption.CASE_INSENSITIVE)) + { + if(stringValue.toLowerCase().contains(criterionValue.toLowerCase())) + { + return (true); + } + } + if(!stringValue.contains(criterionValue)) { return (false); @@ -491,6 +530,14 @@ public class BackendQueryFilterUtils String stringValue = getStringFieldValue(value, fieldName, criterion); String criterionValue = getFirstStringCriterionValue(criterion); + if(criterion.hasOption(CriteriaOption.CASE_INSENSITIVE)) + { + if(stringValue.toLowerCase().startsWith(criterionValue.toLowerCase())) + { + return (true); + } + } + if(!stringValue.startsWith(criterionValue)) { return (false); @@ -509,6 +556,14 @@ public class BackendQueryFilterUtils String stringValue = getStringFieldValue(value, fieldName, criterion); String criterionValue = getFirstStringCriterionValue(criterion); + if(criterion.hasOption(CriteriaOption.CASE_INSENSITIVE)) + { + if(stringValue.toLowerCase().endsWith(criterionValue.toLowerCase())) + { + return (true); + } + } + if(!stringValue.endsWith(criterionValue)) { return (false); @@ -665,4 +720,5 @@ public class BackendQueryFilterUtils regex.append("$"); return regex.toString(); } + } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtilsTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtilsTest.java index afc3ad1f..1f922a2e 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtilsTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtilsTest.java @@ -22,8 +22,10 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.utils; +import java.io.Serializable; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.CriteriaOption; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; @@ -305,6 +307,73 @@ class BackendQueryFilterUtilsTest + /*************************************************************************** + ** + ***************************************************************************/ + private QFilterCriteria newCaseInsensitiveCriteria(String fieldName, QCriteriaOperator operator, Serializable... values) + { + return new QFilterCriteria(fieldName, operator, values).withOption(CriteriaOption.CASE_INSENSITIVE); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + private QFilterCriteria newCaseInsensitiveCriteria(String fieldName, QCriteriaOperator operator, List values) + { + return new QFilterCriteria(fieldName, operator, values).withOption(CriteriaOption.CASE_INSENSITIVE); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testDoesCriterionMatchCaseInsensitive() + { + //////////////// + // like & not // + //////////////// + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.LIKE, "Test"), "f", "test")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.LIKE, "test"), "f", "Test")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.LIKE, "T%"), "f", "test")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.LIKE, "t%"), "f", "Test")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.LIKE, "T_st"), "f", "test")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.LIKE, "t_st"), "f", "Test")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_LIKE, "Test"), "f", "Tst")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_LIKE, "Test"), "f", "tst")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_LIKE, "T%"), "f", "Rest")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_LIKE, "T_st"), "f", "Toast")); + + ////////////// + // IN & NOT // + ////////////// + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.IN, "A"), "f", "a")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.IN, "a"), "f", "A")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.IN, "A", "B"), "f", "a")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.IN, "A", "b"), "f", "B")); + assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.IN, List.of()), "f", "A")); + assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.IN, ListBuilder.of(null)), "f", "A")); + + assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_IN, "A"), "f", "A")); + assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_IN, "A", "B"), "f", "a")); + assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_IN, "A", "b"), "f", "B")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_IN, List.of()), "f", "A")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_IN, ListBuilder.of(null)), "f", "A")); + + /////////////////////////// + // NOT_EQUALS_OR_IS_NULL // + /////////////////////////// + assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, "A"), "f", "A")); + assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, "A"), "f", "a")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, "A"), "f", "B")); + assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(newCaseInsensitiveCriteria("f", QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, "A"), "f", null)); + } + + + /******************************************************************************* ** *******************************************************************************/