mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Adding booleanOperator and subFilters to QQueryFilter
This commit is contained in:
@ -34,10 +34,12 @@ import com.kingsrook.qqq.backend.core.actions.interfaces.QActionInterface;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
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.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
@ -161,7 +163,42 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String makeWhereClause(QTableMetaData table, List<QFilterCriteria> criteria, List<Serializable> params) throws IllegalArgumentException
|
||||
protected String makeWhereClause(QTableMetaData table, QQueryFilter filter, List<Serializable> params) throws IllegalArgumentException
|
||||
{
|
||||
String clause = makeWhereClause(table, filter.getCriteria(), filter.getBooleanOperator(), params);
|
||||
if(!CollectionUtils.nullSafeHasContents(filter.getSubFilters()))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////
|
||||
// if there are no sub-clauses, then just return this clause //
|
||||
///////////////////////////////////////////////////////////////
|
||||
return (clause);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else, build a list of clauses - recursively expanding the sub-filters into clauses, then return them joined with our operator //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
List<String> clauses = new ArrayList<>();
|
||||
if(StringUtils.hasContent(clause))
|
||||
{
|
||||
clauses.add("(" + clause + ")");
|
||||
}
|
||||
for(QQueryFilter subFilter : filter.getSubFilters())
|
||||
{
|
||||
String subClause = makeWhereClause(table, subFilter, params);
|
||||
if(StringUtils.hasContent(subClause))
|
||||
{
|
||||
clauses.add("(" + subClause + ")");
|
||||
}
|
||||
}
|
||||
return (String.join(" " + filter.getBooleanOperator().toString() + " ", clauses));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String makeWhereClause(QTableMetaData table, List<QFilterCriteria> criteria, QQueryFilter.BooleanOperator booleanOperator, List<Serializable> params) throws IllegalArgumentException
|
||||
{
|
||||
List<String> clauses = new ArrayList<>();
|
||||
for(QFilterCriteria criterion : criteria)
|
||||
@ -175,13 +212,13 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
{
|
||||
case EQUALS:
|
||||
{
|
||||
clause += " = ? ";
|
||||
clause += " = ?";
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case NOT_EQUALS:
|
||||
{
|
||||
clause += " != ? ";
|
||||
clause += " != ?";
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
@ -196,7 +233,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
}
|
||||
else
|
||||
{
|
||||
clause += " IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") ";
|
||||
clause += " IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ")";
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -211,105 +248,105 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
}
|
||||
else
|
||||
{
|
||||
clause += " NOT IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") ";
|
||||
clause += " NOT IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ")";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STARTS_WITH:
|
||||
{
|
||||
clause += " LIKE ? ";
|
||||
clause += " LIKE ?";
|
||||
editFirstValue(values, (s -> s + "%"));
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case ENDS_WITH:
|
||||
{
|
||||
clause += " LIKE ? ";
|
||||
clause += " LIKE ?";
|
||||
editFirstValue(values, (s -> "%" + s));
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case CONTAINS:
|
||||
{
|
||||
clause += " LIKE ? ";
|
||||
clause += " LIKE ?";
|
||||
editFirstValue(values, (s -> "%" + s + "%"));
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case NOT_STARTS_WITH:
|
||||
{
|
||||
clause += " NOT LIKE ? ";
|
||||
clause += " NOT LIKE ?";
|
||||
editFirstValue(values, (s -> s + "%"));
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case NOT_ENDS_WITH:
|
||||
{
|
||||
clause += " NOT LIKE ? ";
|
||||
clause += " NOT LIKE ?";
|
||||
editFirstValue(values, (s -> "%" + s));
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case NOT_CONTAINS:
|
||||
{
|
||||
clause += " NOT LIKE ? ";
|
||||
clause += " NOT LIKE ?";
|
||||
editFirstValue(values, (s -> "%" + s + "%"));
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case LESS_THAN:
|
||||
{
|
||||
clause += " < ? ";
|
||||
clause += " < ?";
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case LESS_THAN_OR_EQUALS:
|
||||
{
|
||||
clause += " <= ? ";
|
||||
clause += " <= ?";
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case GREATER_THAN:
|
||||
{
|
||||
clause += " > ? ";
|
||||
clause += " > ?";
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case GREATER_THAN_OR_EQUALS:
|
||||
{
|
||||
clause += " >= ? ";
|
||||
clause += " >= ?";
|
||||
expectedNoOfParams = 1;
|
||||
break;
|
||||
}
|
||||
case IS_BLANK:
|
||||
{
|
||||
clause += " IS NULL ";
|
||||
clause += " IS NULL";
|
||||
if(isString(field.getType()))
|
||||
{
|
||||
clause += " OR " + column + " = '' ";
|
||||
clause += " OR " + column + " = ''";
|
||||
}
|
||||
expectedNoOfParams = 0;
|
||||
break;
|
||||
}
|
||||
case IS_NOT_BLANK:
|
||||
{
|
||||
clause += " IS NOT NULL ";
|
||||
clause += " IS NOT NULL";
|
||||
if(isString(field.getType()))
|
||||
{
|
||||
clause += " AND " + column + " != '' ";
|
||||
clause += " AND " + column + " != ''";
|
||||
}
|
||||
expectedNoOfParams = 0;
|
||||
break;
|
||||
}
|
||||
case BETWEEN:
|
||||
{
|
||||
clause += " BETWEEN ? AND ? ";
|
||||
clause += " BETWEEN ? AND ?";
|
||||
expectedNoOfParams = 2;
|
||||
break;
|
||||
}
|
||||
case NOT_BETWEEN:
|
||||
{
|
||||
clause += " NOT BETWEEN ? AND ? ";
|
||||
clause += " NOT BETWEEN ? AND ?";
|
||||
expectedNoOfParams = 2;
|
||||
break;
|
||||
}
|
||||
@ -330,7 +367,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
params.addAll(values);
|
||||
}
|
||||
|
||||
return (String.join(" AND ", clauses));
|
||||
return (String.join(" " + booleanOperator.toString() + " ", clauses));
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,7 +33,6 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -62,9 +61,9 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
||||
|
||||
QQueryFilter filter = countInput.getFilter();
|
||||
List<Serializable> params = new ArrayList<>();
|
||||
if(filter != null && CollectionUtils.nullSafeHasContents(filter.getCriteria()))
|
||||
if(filter != null && filter.hasAnyCriteria())
|
||||
{
|
||||
sql += " WHERE " + makeWhereClause(table, filter.getCriteria(), params);
|
||||
sql += " WHERE " + makeWhereClause(table, filter, params);
|
||||
}
|
||||
|
||||
// todo sql customization - can edit sql and/or param list
|
||||
|
@ -48,6 +48,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
|
||||
private static final Logger LOG = LogManager.getLogger(RDBMSDeleteAction.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -258,7 +259,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
|
||||
QTableMetaData table = deleteInput.getTable();
|
||||
|
||||
String tableName = getTableName(table);
|
||||
String whereClause = makeWhereClause(table, filter.getCriteria(), params);
|
||||
String whereClause = makeWhereClause(table, filter, params);
|
||||
|
||||
// todo sql customization - can edit sql and/or param list?
|
||||
String sql = "DELETE FROM "
|
||||
|
@ -76,9 +76,9 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
|
||||
QQueryFilter filter = queryInput.getFilter();
|
||||
List<Serializable> params = new ArrayList<>();
|
||||
if(filter != null && CollectionUtils.nullSafeHasContents(filter.getCriteria()))
|
||||
if(filter != null && filter.hasAnyCriteria())
|
||||
{
|
||||
sql += " WHERE " + makeWhereClause(table, filter.getCriteria(), params);
|
||||
sql += " WHERE " + makeWhereClause(table, filter, params);
|
||||
}
|
||||
|
||||
if(filter != null && CollectionUtils.nullSafeHasContents(filter.getOrderBys()))
|
||||
|
@ -519,7 +519,81 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
||||
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.NOT_IN, List.of())));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
Assertions.assertEquals(5, queryOutput.getRecords().size(), "NOT_IN empty list should find everything.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testOr() throws QException
|
||||
{
|
||||
QueryInput queryInput = initQueryRequest();
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("Darin")))
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("Tim")))
|
||||
);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
Assertions.assertEquals(2, queryOutput.getRecords().size(), "OR should find 2 rows");
|
||||
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin"));
|
||||
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tim"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNestedFilterAndOrOr() throws QException
|
||||
{
|
||||
QueryInput queryInput = initQueryRequest();
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
.withSubFilters(List.of(
|
||||
new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("James")))
|
||||
.withCriteria(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, List.of("Maes"))),
|
||||
new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("Darin")))
|
||||
.withCriteria(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, List.of("Kelkhoff")))
|
||||
))
|
||||
);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
Assertions.assertEquals(2, queryOutput.getRecords().size(), "Complex query should find 2 rows");
|
||||
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("James") && r.getValueString("lastName").equals("Maes"));
|
||||
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Darin") && r.getValueString("lastName").equals("Kelkhoff"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNestedFilterOrAndAnd() throws QException
|
||||
{
|
||||
QueryInput queryInput = initQueryRequest();
|
||||
queryInput.setFilter(new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.AND)
|
||||
.withSubFilters(List.of(
|
||||
new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("James")))
|
||||
.withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, List.of("Tim"))),
|
||||
new QQueryFilter()
|
||||
.withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
.withCriteria(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, List.of("Kelkhoff")))
|
||||
.withCriteria(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, List.of("Chamberlain")))
|
||||
))
|
||||
);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
Assertions.assertEquals(1, queryOutput.getRecords().size(), "Complex query should find 1 row");
|
||||
assertThat(queryOutput.getRecords()).anyMatch(r -> r.getValueString("firstName").equals("Tim") && r.getValueString("lastName").equals("Chamberlain"));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user