mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-17 20:50:44 +00:00
Adding QFieldType.LONG
This commit is contained in:
@ -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")
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
@ -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 ->
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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";
|
||||
};
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user