Implemented all query operators in WHERE clause

This commit is contained in:
Darin Kelkhoff
2021-11-28 21:42:19 -06:00
parent 3ba42ccb69
commit cc5bfeeb08
4 changed files with 470 additions and 8 deletions

View File

@ -5,9 +5,11 @@ import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.QFilterCriteria;
@ -17,6 +19,7 @@ import com.kingsrook.qqq.backend.core.model.actions.QueryRequest;
import com.kingsrook.qqq.backend.core.model.actions.QueryResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
import com.kingsrook.qqq.backend.core.modules.interfaces.QueryInterface;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -95,7 +98,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
for(int i = 1; i <= metaData.getColumnCount(); i++)
{
QFieldMetaData qFieldMetaData = fieldList.get(i - 1);
String value = QueryManager.getString(resultSet, i); // todo - types!
Serializable value = getValue(qFieldMetaData, resultSet, i);
values.put(qFieldMetaData.getName(), value);
if(qFieldMetaData.getName().equals(table.getPrimaryKeyField()))
{
@ -117,6 +120,46 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
/*******************************************************************************
**
*******************************************************************************/
private Serializable getValue(QFieldMetaData qFieldMetaData, ResultSet resultSet, int i) throws SQLException
{
switch(qFieldMetaData.getType())
{
case STRING:
case TEXT:
case HTML:
case PASSWORD:
{
return (QueryManager.getString(resultSet, i));
}
case INTEGER:
{
return (QueryManager.getInteger(resultSet, i));
}
case DECIMAL:
{
return (QueryManager.getBigDecimal(resultSet, i));
}
case DATE:
{
return (QueryManager.getDate(resultSet, i));
}
case DATE_TIME:
{
return (QueryManager.getLocalDateTime(resultSet, i));
}
default:
{
throw new IllegalStateException("Unexpected field type: " + qFieldMetaData.getType());
}
}
}
/*******************************************************************************
**
*******************************************************************************/
@ -126,6 +169,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
for(QFilterCriteria criterion : criteria)
{
QFieldMetaData field = table.getField(criterion.getFieldName());
List<Serializable> values = criterion.getValues() == null ? new ArrayList<>() : new ArrayList<>(criterion.getValues());
String column = getColumnName(field);
String clause = column;
Integer expectedNoOfParams = null;
@ -145,7 +189,98 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
}
case IN:
{
clause += " IN (" + criterion.getValues().stream().map(x -> "?").collect(Collectors.joining(",")) + ") ";
clause += " IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") ";
break;
}
case NOT_IN:
{
clause += " NOT IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") ";
break;
}
case STARTS_WITH:
{
clause += " LIKE ? ";
editFirstValue(values, (s -> s + "%"));
expectedNoOfParams = 1;
break;
}
case ENDS_WITH:
{
clause += " LIKE ? ";
editFirstValue(values, (s -> "%" + s));
expectedNoOfParams = 1;
break;
}
case CONTAINS:
{
clause += " LIKE ? ";
editFirstValue(values, (s -> "%" + s + "%"));
expectedNoOfParams = 1;
break;
}
case NOT_STARTS_WITH:
{
clause += " NOT LIKE ? ";
editFirstValue(values, (s -> s + "%"));
expectedNoOfParams = 1;
break;
}
case NOT_ENDS_WITH:
{
clause += " NOT LIKE ? ";
editFirstValue(values, (s -> "%" + s));
expectedNoOfParams = 1;
break;
}
case NOT_CONTAINS:
{
clause += " NOT LIKE ? ";
editFirstValue(values, (s -> "%" + s + "%"));
expectedNoOfParams = 1;
break;
}
case LESS_THAN:
{
clause += " < ? ";
expectedNoOfParams = 1;
break;
}
case LESS_THAN_OR_EQUALS:
{
clause += " <= ? ";
expectedNoOfParams = 1;
break;
}
case GREATER_THAN:
{
clause += " > ? ";
expectedNoOfParams = 1;
break;
}
case GREATER_THAN_OR_EQUALS:
{
clause += " >= ? ";
expectedNoOfParams = 1;
break;
}
case IS_BLANK:
{
clause += " IS NULL ";
if(isString(field.getType()))
{
clause += " OR " + column + " = '' ";
}
expectedNoOfParams = 0;
break;
}
case IS_NOT_BLANK:
{
clause += " IS NOT NULL ";
if(isString(field.getType()))
{
clause += " AND " + column + " !+ '' ";
}
expectedNoOfParams = 0;
break;
}
default:
@ -153,12 +288,16 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
throw new IllegalArgumentException("Unexpected operator: " + criterion.getOperator());
}
}
clauses.add(clause);
if(expectedNoOfParams != null && criterion.getValues().size() != expectedNoOfParams)
clauses.add("(" + clause + ")");
if(expectedNoOfParams != null)
{
throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "]");
if(!expectedNoOfParams.equals(values.size()))
{
throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "]");
}
}
params.addAll(criterion.getValues());
params.addAll(values);
}
return (String.join(" AND ", clauses));
@ -166,6 +305,29 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
/*******************************************************************************
**
*******************************************************************************/
private void editFirstValue(List<Serializable> values, Function<String, String> editFunction)
{
if(values.size() > 0)
{
values.set(0, editFunction.apply(String.valueOf(values.get(0))));
}
}
/*******************************************************************************
**
*******************************************************************************/
private boolean isString(QFieldType fieldType)
{
return fieldType == QFieldType.STRING || fieldType == QFieldType.TEXT || fieldType == QFieldType.HTML || fieldType == QFieldType.PASSWORD;
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -1142,6 +1142,24 @@ public class QueryManager
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("deprecation")
public static LocalDateTime getLocalDateTime(ResultSet resultSet, int column) throws SQLException
{
Timestamp value = resultSet.getTimestamp(column);
if(resultSet.wasNull())
{
return (null);
}
LocalDateTime dateTime = LocalDateTime.of(value.getYear() + NINETEEN_HUNDRED, value.getMonth() + 1, value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), 0);
return (dateTime);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -29,6 +29,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
}
/*******************************************************************************
**
*******************************************************************************/
@ -58,12 +59,293 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
.withValues(List.of(email)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(1, queryResult.getRecords().size(), "Equals query should find 1 row");
Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertEquals(email, queryResult.getRecords().get(0).getValueString("email"), "Should find expected email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotEqualsQuery() throws QException
{
String email = "darin.kelkhoff@gmail.com";
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_EQUALS)
.withValues(List.of(email)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").equals(email)), "Should NOT find expected email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testInQuery() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.IN)
.withValues(List.of(2, 4)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(4)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotInQuery() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.NOT_IN)
.withValues(List.of(2, 3, 4)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testStartsWith() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.STARTS_WITH)
.withValues(List.of("darin")))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testContains() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.CONTAINS)
.withValues(List.of("kelkhoff")))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testEndsWith() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.ENDS_WITH)
.withValues(List.of("gmail.com")))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotStartsWith() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_STARTS_WITH)
.withValues(List.of("darin")))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotContains() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_CONTAINS)
.withValues(List.of("kelkhoff")))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testNotEndsWith() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("email")
.withOperator(QCriteriaOperator.NOT_ENDS_WITH)
.withValues(List.of("gmail.com")))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testLessThanQuery() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.LESS_THAN)
.withValues(List.of(3)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testLessThanOrEqualsQuery() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.LESS_THAN_OR_EQUALS)
.withValues(List.of(2)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testGreaterThanQuery() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.GREATER_THAN)
.withValues(List.of(3)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testGreaterThanOrEqualsQuery() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("id")
.withOperator(QCriteriaOperator.GREATER_THAN_OR_EQUALS)
.withValues(List.of(4)))
);
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testIsBlankQuery() throws QException
{
QueryRequest queryRequest = initQueryRequest();
queryRequest.setFilter(new QQueryFilter()
.withCriteria(new QFilterCriteria()
.withFieldName("birthDate")
.withOperator(QCriteriaOperator.IS_BLANK)
));
QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest);
Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows");
Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValue("birthDate") == null), "Should find expected row");
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -14,5 +14,5 @@ CREATE TABLE person
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (1, 'Darin', 'Kelkhoff', '1980-05-31', 'darin.kelkhoff@gmail.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (2, 'James', 'Maes', '1980-05-15', 'jmaes@mmltholdings.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (3, 'Tim', 'Chamberlain', '1976-05-28', 'tchamberlain@mmltholdings.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (4, 'Tyler', 'Samples', '1990-01-01', 'tsamples@mmltholdings.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (4, 'Tyler', 'Samples', NULL, 'tsamples@mmltholdings.com');
INSERT INTO person (id, first_name, last_name, birth_date, email) VALUES (5, 'Garret', 'Richardson', '1981-01-01', 'grichardson@mmltholdings.com');