Adding QFieldType.LONG

This commit is contained in:
2023-12-22 18:59:08 -06:00
parent a8c30b1bed
commit 455ab69104
13 changed files with 309 additions and 7 deletions

View File

@ -73,6 +73,7 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.aggregates.AggregatesInterface;
import com.kingsrook.qqq.backend.core.utils.aggregates.BigDecimalAggregates;
import com.kingsrook.qqq.backend.core.utils.aggregates.IntegerAggregates;
import com.kingsrook.qqq.backend.core.utils.aggregates.LongAggregates;
/*******************************************************************************
@ -553,6 +554,12 @@ public class GenerateReportAction
AggregatesInterface<Integer> fieldAggregates = (AggregatesInterface<Integer>) aggregatesMap.computeIfAbsent(field.getName(), (name) -> new IntegerAggregates());
fieldAggregates.add(record.getValueInteger(field.getName()));
}
else if(field.getType().equals(QFieldType.LONG))
{
@SuppressWarnings("unchecked")
AggregatesInterface<Long> fieldAggregates = (AggregatesInterface<Long>) aggregatesMap.computeIfAbsent(field.getName(), (name) -> new LongAggregates());
fieldAggregates.add(record.getValueLong(field.getName()));
}
else if(field.getType().equals(QFieldType.DECIMAL))
{
@SuppressWarnings("unchecked")

View File

@ -270,6 +270,10 @@ public class QPossibleValueTranslator
{
value = ValueUtils.getValueAsInteger(value);
}
if(field.getType().equals(QFieldType.LONG) && !(value instanceof Long))
{
value = ValueUtils.getValueAsLong(value);
}
}
catch(QValueException e)
{

View File

@ -444,6 +444,16 @@ public class QRecord implements Serializable
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public Long getValueLong(String fieldName)
{
return (ValueUtils.getValueAsLong(values.get(fieldName)));
}
/*******************************************************************************
**

View File

@ -37,6 +37,7 @@ public enum QFieldType
{
STRING,
INTEGER,
LONG,
DECIMAL,
BOOLEAN,
DATE,
@ -65,6 +66,10 @@ public enum QFieldType
{
return (INTEGER);
}
if(c.equals(Long.class) || c.equals(long.class))
{
return (LONG);
}
if(c.equals(BigDecimal.class))
{
return (DECIMAL);
@ -110,7 +115,7 @@ public enum QFieldType
*******************************************************************************/
public boolean isNumeric()
{
return this == QFieldType.INTEGER || this == QFieldType.DECIMAL;
return this == QFieldType.INTEGER || this == QFieldType.LONG || this == QFieldType.DECIMAL;
}

View File

@ -366,7 +366,7 @@ public class MemoryRecordStore
/////////////////////////////////////////////////
// set the next serial in the record if needed //
/////////////////////////////////////////////////
if(recordToInsert.getValue(primaryKeyField.getName()) == null && primaryKeyField.getType().equals(QFieldType.INTEGER))
if(recordToInsert.getValue(primaryKeyField.getName()) == null && (primaryKeyField.getType().equals(QFieldType.INTEGER) || primaryKeyField.getType().equals(QFieldType.LONG)))
{
recordToInsert.setValue(primaryKeyField.getName(), nextSerial++);
}
@ -378,6 +378,13 @@ public class MemoryRecordStore
{
nextSerial = recordToInsert.getValueInteger(primaryKeyField.getName()) + 1;
}
else if(primaryKeyField.getType().equals(QFieldType.LONG) && recordToInsert.getValueLong(primaryKeyField.getName()) > nextSerial)
{
//////////////////////////////////////
// todo - mmm, could overflow here? //
//////////////////////////////////////
nextSerial = recordToInsert.getValueInteger(primaryKeyField.getName()) + 1;
}
tableData.put(recordToInsert.getValue(primaryKeyField.getName()), recordToInsert);
if(returnInsertedRecords)
@ -709,7 +716,7 @@ public class MemoryRecordStore
{
// todo - joins probably?
QFieldMetaData field = table.getField(fieldName);
if(field.getType().equals(QFieldType.INTEGER) && (operator.equals(AggregateOperator.AVG)))
if((field.getType().equals(QFieldType.INTEGER) || field.getType().equals(QFieldType.LONG)) && (operator.equals(AggregateOperator.AVG)))
{
fieldType = QFieldType.DECIMAL;
}
@ -745,6 +752,10 @@ public class MemoryRecordStore
.filter(r -> r.getValue(fieldName) != null)
.mapToInt(r -> r.getValueInteger(fieldName))
.sum();
case LONG -> records.stream()
.filter(r -> r.getValue(fieldName) != null)
.mapToLong(r -> r.getValueLong(fieldName))
.sum();
case DECIMAL -> records.stream()
.filter(r -> r.getValue(fieldName) != null)
.map(r -> r.getValueBigDecimal(fieldName))
@ -759,6 +770,11 @@ public class MemoryRecordStore
.mapToInt(r -> r.getValueInteger(fieldName))
.min()
.stream().boxed().findFirst().orElse(null);
case LONG -> records.stream()
.filter(r -> r.getValue(fieldName) != null)
.mapToLong(r -> r.getValueLong(fieldName))
.min()
.stream().boxed().findFirst().orElse(null);
case DECIMAL, STRING, DATE, DATE_TIME ->
{
Optional<Serializable> serializable = records.stream()
@ -775,7 +791,12 @@ public class MemoryRecordStore
{
case INTEGER -> records.stream()
.filter(r -> r.getValue(fieldName) != null)
.mapToInt(r -> r.getValueInteger(fieldName))
.mapToLong(r -> r.getValueInteger(fieldName))
.max()
.stream().boxed().findFirst().orElse(null);
case LONG -> records.stream()
.filter(r -> r.getValue(fieldName) != null)
.mapToLong(r -> r.getValueLong(fieldName))
.max()
.stream().boxed().findFirst().orElse(null);
case DECIMAL, STRING, DATE, DATE_TIME ->
@ -797,6 +818,11 @@ public class MemoryRecordStore
.mapToInt(r -> r.getValueInteger(fieldName))
.average()
.stream().boxed().findFirst().orElse(null);
case LONG -> records.stream()
.filter(r -> r.getValue(fieldName) != null)
.mapToLong(r -> r.getValueLong(fieldName))
.average()
.stream().boxed().findFirst().orElse(null);
case DECIMAL -> records.stream()
.filter(r -> r.getValue(fieldName) != null)
.mapToDouble(r -> r.getValueBigDecimal(fieldName).doubleValue())

View File

@ -103,6 +103,7 @@ public class MockQueryAction implements QueryInterface
{
case STRING -> UUID.randomUUID().toString();
case INTEGER -> 42;
case LONG -> 42L;
case DECIMAL -> new BigDecimal("3.14159");
case DATE -> LocalDate.of(1970, Month.JANUARY, 1);
case DATE_TIME -> LocalDateTime.of(1970, Month.JANUARY, 1, 0, 0);

View File

@ -362,6 +362,7 @@ public class JsonUtils
switch(metaData.getType())
{
case INTEGER -> record.setValue(fieldName, jsonObjectToUse.optInt(backendName));
case LONG -> record.setValue(fieldName, jsonObjectToUse.optLong(backendName));
case DECIMAL -> record.setValue(fieldName, jsonObjectToUse.optBigDecimal(backendName, null));
case BOOLEAN -> record.setValue(fieldName, jsonObjectToUse.optBoolean(backendName));
case DATE_TIME ->

View File

@ -114,6 +114,113 @@ public class ValueUtils
/*******************************************************************************
** Type-safely make an Long from any Object.
** null and empty-string inputs return null.
** We try to strip away commas and decimals (as long as they are exactly equal to the int value)
** We may throw if the input can't be converted to an integer.
*******************************************************************************/
public static Long getValueAsLong(Object value) throws QValueException
{
try
{
if(value == null)
{
return (null);
}
else if(value instanceof Integer i)
{
return Long.valueOf((i));
}
else if(value instanceof Long l)
{
return (l);
}
else if(value instanceof BigInteger b)
{
return (b.longValue());
}
else if(value instanceof Float f)
{
if(f.longValue() != f)
{
throw (new QValueException(f + " does not have an exact integer representation."));
}
return (f.longValue());
}
else if(value instanceof Double d)
{
if(d.longValue() != d)
{
throw (new QValueException(d + " does not have an exact integer representation."));
}
return (d.longValue());
}
else if(value instanceof BigDecimal bd)
{
return bd.longValueExact();
}
else if(value instanceof PossibleValueEnum<?> pve)
{
return getValueAsLong(pve.getPossibleValueId());
}
else if(value instanceof String s)
{
if(!StringUtils.hasContent(s))
{
return (null);
}
try
{
return (Long.parseLong(s));
}
catch(NumberFormatException nfe)
{
if(s.contains(","))
{
String sWithoutCommas = s.replaceAll(",", "");
try
{
return (getValueAsLong(sWithoutCommas));
}
catch(Exception ignore)
{
throw (nfe);
}
}
if(s.matches(".*\\.\\d+$"))
{
String sWithoutDecimal = s.replaceAll("\\.\\d+$", "");
try
{
return (getValueAsLong(sWithoutDecimal));
}
catch(Exception ignore)
{
throw (nfe);
}
}
throw (nfe);
}
}
else
{
throw (new QValueException("Unsupported class " + value.getClass().getName() + " for converting to Long."));
}
}
catch(QValueException qve)
{
throw (qve);
}
catch(Exception e)
{
throw (new QValueException("Value [" + value + "] could not be converted to a Long.", e));
}
}
/*******************************************************************************
** Type-safely make an Integer from any Object.
** null and empty-string inputs return null.
@ -693,6 +800,7 @@ public class ValueUtils
{
case STRING, TEXT, HTML, PASSWORD -> getValueAsString(value);
case INTEGER -> getValueAsInteger(value);
case LONG -> getValueAsLong(value);
case DECIMAL -> getValueAsBigDecimal(value);
case BOOLEAN -> getValueAsBoolean(value);
case DATE -> getValueAsLocalDate(value);

View File

@ -0,0 +1,135 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.utils.aggregates;
import java.math.BigDecimal;
/*******************************************************************************
** Long version of data aggregator
*******************************************************************************/
public class LongAggregates implements AggregatesInterface<Long>
{
private int count = 0;
// private Long countDistinct;
private Long sum;
private Long min;
private Long max;
/*******************************************************************************
** Add a new value to this aggregate set
*******************************************************************************/
public void add(Long input)
{
if(input == null)
{
return;
}
count++;
if(sum == null)
{
sum = input;
}
else
{
sum = sum + input;
}
if(min == null || input < min)
{
min = input;
}
if(max == null || input > max)
{
max = input;
}
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public int getCount()
{
return (count);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Long getSum()
{
return (sum);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Long getMin()
{
return (min);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public Long getMax()
{
return (max);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public BigDecimal getAverage()
{
if(this.count > 0)
{
return (BigDecimal.valueOf(this.sum.doubleValue() / (double) this.count));
}
else
{
return (null);
}
}
}

View File

@ -154,7 +154,7 @@ public abstract class AbstractRDBMSAction implements QActionInterface
if("".equals(value))
{
QFieldType type = field.getType();
if(type.equals(QFieldType.INTEGER) || type.equals(QFieldType.DECIMAL) || type.equals(QFieldType.DATE) || type.equals(QFieldType.DATE_TIME) || type.equals(QFieldType.BOOLEAN))
if(type.equals(QFieldType.INTEGER) || type.equals(QFieldType.LONG) || type.equals(QFieldType.DECIMAL) || type.equals(QFieldType.DATE) || type.equals(QFieldType.DATE_TIME) || type.equals(QFieldType.BOOLEAN))
{
value = null;
}
@ -875,6 +875,10 @@ public abstract class AbstractRDBMSAction implements QActionInterface
{
return (QueryManager.getInteger(resultSet, i));
}
case LONG:
{
return (QueryManager.getLong(resultSet, i));
}
case DECIMAL:
{
return (QueryManager.getBigDecimal(resultSet, i));

View File

@ -143,7 +143,7 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
QFieldType fieldType = aggregate.getFieldType();
if(fieldType == null)
{
if(field.getType().equals(QFieldType.INTEGER) && (aggregate.getOperator().equals(AggregateOperator.AVG)))
if((field.getType().equals(QFieldType.INTEGER) || field.getType().equals(QFieldType.LONG)) && (aggregate.getOperator().equals(AggregateOperator.AVG)))
{
fieldType = QFieldType.DECIMAL;
}

View File

@ -1680,7 +1680,7 @@ public class GenerateOpenApiSpecAction extends AbstractQActionFunction<GenerateO
return switch(type)
{
case STRING, DATE, TIME, DATE_TIME, TEXT, HTML, PASSWORD, BLOB -> "string";
case INTEGER -> "integer";
case INTEGER, LONG -> "integer"; // todo - we could give 'format' w/ int32 & int64 to further specify
case DECIMAL -> "number";
case BOOLEAN -> "boolean";
};

View File

@ -415,6 +415,7 @@ public class QCommandBuilder
{
case STRING, TEXT, HTML, PASSWORD -> String.class;
case INTEGER -> Integer.class;
case LONG -> Long.class;
case DECIMAL -> BigDecimal.class;
case DATE -> LocalDate.class;
case TIME -> LocalTime.class;