mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-07 00:15:48 -04:00
[FEATURE] Add expression function array_agg
This commit is contained in:
parent
c8876f2c23
commit
e34a593c65
@ -47,7 +47,8 @@ class QgsAggregateCalculator
|
||||
StringMinimumLength,
|
||||
StringMaximumLength,
|
||||
StringConcatenate,
|
||||
GeometryCollect
|
||||
GeometryCollect,
|
||||
ArrayAggregate
|
||||
};
|
||||
|
||||
struct AggregateParameters
|
||||
|
13
resources/function_help/json/array_agg
Normal file
13
resources/function_help/json/array_agg
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "array_agg",
|
||||
"type": "function",
|
||||
"description": "Returns an array of aggregated values from a field or expression.",
|
||||
"arguments": [
|
||||
{"arg": "expression", "description": "sub expression of field to aggregate"},
|
||||
{"arg": "group_by", "optional": true, "description": "optional expression to use to group aggregate calculations"},
|
||||
{"arg": "filter", "optional": true, "description": "optional expression to use to filter features used to calculate aggregate"}
|
||||
],
|
||||
"examples": [
|
||||
{ "expression": "array_agg(\"name\",group_by:=\"state\")", "returns":"list of name values, grouped by state field"}
|
||||
]
|
||||
}
|
@ -830,6 +830,11 @@ static QVariant fcnAggregateStringConcat( const QVariantList &values, const QgsE
|
||||
return fcnAggregateGeneric( QgsAggregateCalculator::StringConcatenate, values, parameters, context, parent );
|
||||
}
|
||||
|
||||
static QVariant fcnAggregateArray( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent )
|
||||
{
|
||||
return fcnAggregateGeneric( QgsAggregateCalculator::ArrayAggregate, values, QgsAggregateCalculator::AggregateParameters(), context, parent );
|
||||
}
|
||||
|
||||
static QVariant fcnClamp( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent )
|
||||
{
|
||||
double minValue = QgsExpressionUtils::getDoubleValue( values.at( 0 ), parent );
|
||||
@ -3918,6 +3923,7 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "max_length" ), aggParams, fcnAggregateMaxLength, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "collect" ), aggParams, fcnAggregateCollectGeometry, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "concatenate" ), aggParams << QgsExpressionFunction::Parameter( QStringLiteral( "concatenator" ), true ), fcnAggregateStringConcat, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "array_agg" ), aggParams, fcnAggregateArray, QStringLiteral( "Aggregates" ), QString(), false, QSet<QString>(), true )
|
||||
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "regexp_match" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ), fcnRegexpMatch, QStringList() << QStringLiteral( "Conditionals" ) << QStringLiteral( "String" ) )
|
||||
<< new QgsStaticExpressionFunction( QStringLiteral( "regexp_matches" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "string" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "regex" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "emptyvalue" ), true, "" ), fcnRegexpMatches, QStringLiteral( "Arrays" ) )
|
||||
|
@ -165,6 +165,8 @@ QgsAggregateCalculator::Aggregate QgsAggregateCalculator::stringToAggregate( con
|
||||
return StringConcatenate;
|
||||
else if ( normalized == QLatin1String( "collect" ) )
|
||||
return GeometryCollect;
|
||||
else if ( normalized == QLatin1String( "array_agg" ) )
|
||||
return ArrayAggregate;
|
||||
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
@ -178,6 +180,13 @@ QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate ag
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
|
||||
if ( aggregate == QgsAggregateCalculator::ArrayAggregate )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
return calculateArrayAggregate( fit, attr, expression, context );
|
||||
}
|
||||
|
||||
switch ( resultType )
|
||||
{
|
||||
case QVariant::Int:
|
||||
@ -293,6 +302,7 @@ QgsStatisticalSummary::Statistic QgsAggregateCalculator::numericStatFromAggregat
|
||||
case StringMaximumLength:
|
||||
case StringConcatenate:
|
||||
case GeometryCollect:
|
||||
case ArrayAggregate:
|
||||
{
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
@ -340,6 +350,7 @@ QgsStringStatisticalSummary::Statistic QgsAggregateCalculator::stringStatFromAgg
|
||||
case InterQuartileRange:
|
||||
case StringConcatenate:
|
||||
case GeometryCollect:
|
||||
case ArrayAggregate:
|
||||
{
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
@ -386,6 +397,7 @@ QgsDateTimeStatisticalSummary::Statistic QgsAggregateCalculator::dateTimeStatFro
|
||||
case StringMaximumLength:
|
||||
case StringConcatenate:
|
||||
case GeometryCollect:
|
||||
case ArrayAggregate:
|
||||
{
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
@ -512,6 +524,9 @@ QVariant QgsAggregateCalculator::defaultValue( QgsAggregateCalculator::Aggregate
|
||||
case StringConcatenate:
|
||||
return ""; // zero length string - not null!
|
||||
|
||||
case ArrayAggregate:
|
||||
return QVariantList(); // empty list
|
||||
|
||||
// undefined - nothing makes sense here
|
||||
case Sum:
|
||||
case Min:
|
||||
@ -559,3 +574,29 @@ QVariant QgsAggregateCalculator::calculateDateTimeAggregate( QgsFeatureIterator
|
||||
s.finalize();
|
||||
return s.statistic( stat );
|
||||
}
|
||||
|
||||
QVariant QgsAggregateCalculator::calculateArrayAggregate( QgsFeatureIterator &fit, int attr, QgsExpression *expression,
|
||||
QgsExpressionContext *context )
|
||||
{
|
||||
Q_ASSERT( expression || attr >= 0 );
|
||||
|
||||
QgsFeature f;
|
||||
|
||||
QVariantList array;
|
||||
|
||||
while ( fit.nextFeature( f ) )
|
||||
{
|
||||
if ( expression )
|
||||
{
|
||||
Q_ASSERT( context );
|
||||
context->setFeature( f );
|
||||
QVariant v = expression->evaluate( context );
|
||||
array.append( v );
|
||||
}
|
||||
else
|
||||
{
|
||||
array.append( f.attribute( attr ) );
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
@ -65,7 +65,8 @@ class CORE_EXPORT QgsAggregateCalculator
|
||||
StringMinimumLength, //!< Minimum length of string (string fields only)
|
||||
StringMaximumLength, //!< Maximum length of string (string fields only)
|
||||
StringConcatenate, //! Concatenate values with a joining string (string fields only). Specify the delimiter using setDelimiter().
|
||||
GeometryCollect //! Create a multipart geometry from aggregated geometries
|
||||
GeometryCollect, //! Create a multipart geometry from aggregated geometries
|
||||
ArrayAggregate //! Create an array of values
|
||||
};
|
||||
|
||||
//! A bundle of parameters controlling aggregate calculation
|
||||
@ -165,6 +166,9 @@ class CORE_EXPORT QgsAggregateCalculator
|
||||
QgsExpressionContext *context, QgsDateTimeStatisticalSummary::Statistic stat );
|
||||
static QVariant calculateGeometryAggregate( QgsFeatureIterator &fit, QgsExpression *expression, QgsExpressionContext *context );
|
||||
|
||||
static QVariant calculateArrayAggregate( QgsFeatureIterator &fit, int attr, QgsExpression *expression,
|
||||
QgsExpressionContext *context );
|
||||
|
||||
static QVariant calculate( Aggregate aggregate, QgsFeatureIterator &fit, QVariant::Type resultType,
|
||||
int attr, QgsExpression *expression,
|
||||
const QString &delimiter,
|
||||
|
@ -1375,6 +1375,10 @@ class TestQgsExpression: public QObject
|
||||
|
||||
QTest::newRow( "geometry collect" ) << "geom_to_wkt(aggregate('aggregate_layer','collect',$geometry))" << false << QVariant( QStringLiteral( "MultiPoint ((0 0),(1 0),(2 0),(3 0),(5 0))" ) );
|
||||
|
||||
QVariantList array;
|
||||
array << "test" << QVariant( QVariant::String ) << "test333" << "test4" << QVariant( QVariant::String ) << "test4";
|
||||
QTest::newRow( "array aggregate" ) << "aggregate('aggregate_layer','array_agg',\"col2\")" << false << QVariant( array );
|
||||
|
||||
QTest::newRow( "sub expression" ) << "aggregate('test','sum',\"col1\" * 2)" << false << QVariant( 65 * 2 );
|
||||
QTest::newRow( "bad sub expression" ) << "aggregate('test','sum',\"xcvxcv\" * 2)" << true << QVariant();
|
||||
|
||||
|
@ -130,13 +130,15 @@ class TestQgsAggregateCalculator(unittest.TestCase):
|
||||
[QgsAggregateCalculator.ThirdQuartile, 'flddbl', 7.5],
|
||||
[QgsAggregateCalculator.InterQuartileRange, 'fldint', 3.0],
|
||||
[QgsAggregateCalculator.InterQuartileRange, 'flddbl', 2.5],
|
||||
[QgsAggregateCalculator.ArrayAggregate, 'fldint', int_values],
|
||||
[QgsAggregateCalculator.ArrayAggregate, 'flddbl', dbl_values],
|
||||
]
|
||||
|
||||
agg = QgsAggregateCalculator(layer)
|
||||
for t in tests:
|
||||
val, ok = agg.calculate(t[0], t[1])
|
||||
self.assertTrue(ok)
|
||||
if isinstance(t[2], int):
|
||||
if isinstance(t[2], (int, list)):
|
||||
self.assertEqual(val, t[2])
|
||||
else:
|
||||
self.assertAlmostEqual(val, t[2], 3)
|
||||
@ -171,6 +173,7 @@ class TestQgsAggregateCalculator(unittest.TestCase):
|
||||
[QgsAggregateCalculator.Max, 'fldstring', 'eeee'],
|
||||
[QgsAggregateCalculator.StringMinimumLength, 'fldstring', 0],
|
||||
[QgsAggregateCalculator.StringMaximumLength, 'fldstring', 8],
|
||||
[QgsAggregateCalculator.ArrayAggregate, 'fldstring', values],
|
||||
]
|
||||
|
||||
agg = QgsAggregateCalculator(layer)
|
||||
@ -251,6 +254,8 @@ class TestQgsAggregateCalculator(unittest.TestCase):
|
||||
[QgsAggregateCalculator.Range, 'flddatetime', QgsInterval(693871147)],
|
||||
[QgsAggregateCalculator.Range, 'flddate', QgsInterval(693792000)],
|
||||
|
||||
[QgsAggregateCalculator.ArrayAggregate, 'flddatetime', [None if v.isNull() else v for v in datetime_values]],
|
||||
[QgsAggregateCalculator.ArrayAggregate, 'flddate', [None if v.isNull() else v for v in date_values]],
|
||||
]
|
||||
|
||||
agg = QgsAggregateCalculator(layer)
|
||||
@ -425,6 +430,12 @@ class TestQgsAggregateCalculator(unittest.TestCase):
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(val, None)
|
||||
|
||||
# array_agg
|
||||
agg = QgsAggregateCalculator(layer)
|
||||
val, ok = agg.calculate(QgsAggregateCalculator.ArrayAggregate, 'fldint * 2')
|
||||
self.assertTrue(ok)
|
||||
self.assertEqual(val, [])
|
||||
|
||||
def testStringToAggregate(self):
|
||||
""" test converting strings to aggregate types """
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user