mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 21:20:45 +00:00
Update handling of criteria in format "table.field" when the "table" portion equals the record's tableName; fix applyBooleanOperator to always update the accumulator;
This commit is contained in:
@ -75,14 +75,16 @@ public class BackendQueryFilterUtils
|
|||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if the value isn't in the record - check, if it looks like a table.fieldName, but none of the //
|
// if the value isn't in the record - check, if it looks like a table.fieldName, but none of the //
|
||||||
// field names in the record are fully qualified, then just use the field-name portion... //
|
// field names in the record are fully qualified - OR - the table name portion of the field name //
|
||||||
|
// matches the record's field name, then just use the field-name portion... //
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
if(fieldName.contains("."))
|
if(fieldName.contains("."))
|
||||||
{
|
{
|
||||||
|
String[] parts = fieldName.split("\\.");
|
||||||
Map<String, Serializable> values = qRecord.getValues();
|
Map<String, Serializable> values = qRecord.getValues();
|
||||||
if(values.keySet().stream().noneMatch(n -> n.contains(".")))
|
if(values.keySet().stream().noneMatch(n -> n.contains(".")) || parts[0].equals(qRecord.getTableName()))
|
||||||
{
|
{
|
||||||
value = qRecord.getValue(fieldName.substring(fieldName.indexOf(".") + 1));
|
value = qRecord.getValue(parts[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,12 +192,13 @@ public class BackendQueryFilterUtils
|
|||||||
** operator, update the accumulator, and if we can then short-circuit remaining
|
** operator, update the accumulator, and if we can then short-circuit remaining
|
||||||
** operations, return a true or false. Returning null means to keep going.
|
** operations, return a true or false. Returning null means to keep going.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static Boolean applyBooleanOperator(AtomicBoolean accumulator, boolean newValue, QQueryFilter.BooleanOperator booleanOperator)
|
static Boolean applyBooleanOperator(AtomicBoolean accumulator, boolean newValue, QQueryFilter.BooleanOperator booleanOperator)
|
||||||
{
|
{
|
||||||
boolean accumulatorValue = accumulator.getPlain();
|
boolean accumulatorValue = accumulator.getPlain();
|
||||||
if(booleanOperator.equals(QQueryFilter.BooleanOperator.AND))
|
if(booleanOperator.equals(QQueryFilter.BooleanOperator.AND))
|
||||||
{
|
{
|
||||||
accumulatorValue &= newValue;
|
accumulatorValue &= newValue;
|
||||||
|
accumulator.set(accumulatorValue);
|
||||||
if(!accumulatorValue)
|
if(!accumulatorValue)
|
||||||
{
|
{
|
||||||
return (false);
|
return (false);
|
||||||
@ -204,6 +207,7 @@ public class BackendQueryFilterUtils
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
accumulatorValue |= newValue;
|
accumulatorValue |= newValue;
|
||||||
|
accumulator.set(accumulatorValue);
|
||||||
if(accumulatorValue)
|
if(accumulatorValue)
|
||||||
{
|
{
|
||||||
return (true);
|
return (true);
|
||||||
|
@ -23,11 +23,16 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.utils;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
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.QFilterCriteria;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
@ -37,6 +42,182 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
class BackendQueryFilterUtilsTest
|
class BackendQueryFilterUtilsTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_emptyFilters()
|
||||||
|
{
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(null, new QRecord().withValue("a", 1)));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(new QQueryFilter(), new QRecord().withValue("a", 1)));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(new QQueryFilter().withSubFilters(ListBuilder.of(null)), new QRecord().withValue("a", 1)));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(new QQueryFilter().withSubFilters(List.of(new QQueryFilter())), new QRecord().withValue("a", 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_singleAnd()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_singleOr()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||||
|
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_multipleAnd()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1))
|
||||||
|
.withCriteria(new QFilterCriteria("b", QCriteriaOperator.EQUALS, 2));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 2)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 2).withValue("b", 2)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_multipleOr()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||||
|
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1))
|
||||||
|
.withCriteria(new QFilterCriteria("b", QCriteriaOperator.EQUALS, 2));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 2)));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 2).withValue("b", 2)));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 3).withValue("b", 4)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_subFilterAnd()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withSubFilters(List.of(
|
||||||
|
new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1)),
|
||||||
|
new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withCriteria(new QFilterCriteria("b", QCriteriaOperator.EQUALS, 2))
|
||||||
|
));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 2)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 2).withValue("b", 2)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_subFilterOr()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||||
|
.withSubFilters(List.of(
|
||||||
|
new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||||
|
.withCriteria(new QFilterCriteria("a", QCriteriaOperator.EQUALS, 1)),
|
||||||
|
new QQueryFilter()
|
||||||
|
.withCriteria(new QFilterCriteria("b", QCriteriaOperator.EQUALS, 2))
|
||||||
|
));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 2)));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 2).withValue("b", 2)));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("b", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 3).withValue("b", 4)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_criteriaHasTableNameNoFieldsDo()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withCriteria(new QFilterCriteria("t.a", QCriteriaOperator.EQUALS, 1));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_criteriaHasTableNameSomeFieldsDo()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withCriteria(new QFilterCriteria("t.a", QCriteriaOperator.EQUALS, 1));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// shouldn't find the "a", because "some" fields in here have a prefix (e.g., 's' was a join table, selected with 't' as the main table, which didn't prefix) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("a", 1).withValue("s.b", 2)));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// but this case (contrasted with above) set the record's tableName to "t", so criteria on "t.a" should find field "a" //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withTableName("t").withValue("a", 1).withValue("s.b", 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesRecordMatch_criteriaHasTableNameMatchingField()
|
||||||
|
{
|
||||||
|
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||||
|
.withCriteria(new QFilterCriteria("t.a", QCriteriaOperator.EQUALS, 1));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("t.a", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("t.b", 1)));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesRecordMatch(filter, new QRecord().withValue("s.a", 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -184,4 +365,94 @@ class BackendQueryFilterUtilsTest
|
|||||||
assertFalse("Not Darin".matches(pattern));
|
assertFalse("Not Darin".matches(pattern));
|
||||||
assertFalse("David".matches(pattern));
|
assertFalse("David".matches(pattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testApplyBooleanOperator()
|
||||||
|
{
|
||||||
|
/////////////////////////////
|
||||||
|
// tests for operator: AND //
|
||||||
|
/////////////////////////////
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was true; new value is true. //
|
||||||
|
// result should be true, and we should not be short-circuited (return value null) //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(true);
|
||||||
|
assertNull(BackendQueryFilterUtils.applyBooleanOperator(accumulator, true, QQueryFilter.BooleanOperator.AND));
|
||||||
|
assertTrue(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was true; new value is false. //
|
||||||
|
// result should be false, and we should be short-circuited (return value not-null) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(true);
|
||||||
|
assertEquals(Boolean.FALSE, BackendQueryFilterUtils.applyBooleanOperator(accumulator, false, QQueryFilter.BooleanOperator.AND));
|
||||||
|
assertFalse(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was false; new value is true. //
|
||||||
|
// result should be false, and we should be short-circuited (return value not-null) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(false);
|
||||||
|
assertEquals(Boolean.FALSE, BackendQueryFilterUtils.applyBooleanOperator(accumulator, true, QQueryFilter.BooleanOperator.AND));
|
||||||
|
assertFalse(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was false; new value is false. //
|
||||||
|
// result should be false, and we should be short-circuited (return value not-null) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(false);
|
||||||
|
assertEquals(Boolean.FALSE, BackendQueryFilterUtils.applyBooleanOperator(accumulator, false, QQueryFilter.BooleanOperator.AND));
|
||||||
|
assertFalse(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// tests for operator: OR //
|
||||||
|
////////////////////////////
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was true; new value is true. //
|
||||||
|
// result should be true, and we should be short-circuited (return value not-null) //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(true);
|
||||||
|
assertEquals(Boolean.TRUE, BackendQueryFilterUtils.applyBooleanOperator(accumulator, true, QQueryFilter.BooleanOperator.OR));
|
||||||
|
assertTrue(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was true; new value is false. //
|
||||||
|
// result should be true, and we should be short-circuited (return value not-null) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(true);
|
||||||
|
assertEquals(Boolean.TRUE, BackendQueryFilterUtils.applyBooleanOperator(accumulator, false, QQueryFilter.BooleanOperator.OR));
|
||||||
|
assertTrue(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was false; new value is true. //
|
||||||
|
// result should be false, and we should be short-circuited (return value not-null) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(false);
|
||||||
|
assertEquals(Boolean.TRUE, BackendQueryFilterUtils.applyBooleanOperator(accumulator, true, QQueryFilter.BooleanOperator.OR));
|
||||||
|
assertTrue(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// old value was false; new value is false. //
|
||||||
|
// result should be false, and we should not be short-circuited (return value null) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AtomicBoolean accumulator = new AtomicBoolean(false);
|
||||||
|
assertNull(BackendQueryFilterUtils.applyBooleanOperator(accumulator, false, QQueryFilter.BooleanOperator.OR));
|
||||||
|
assertFalse(accumulator.getPlain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user