Skip to content

Commit 96b2953

Browse files
committed
Fix calculation of certain aggregates from expressions when no
matching features exist Eg count should return 0 in this case rather than null (cherry-picked from 9ba41e9)
1 parent 1334524 commit 96b2953

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

src/core/qgsaggregatecalculator.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate ag
103103
//no matching features
104104
if ( ok )
105105
*ok = true;
106-
return QVariant();
106+
return defaultValue( aggregate );
107107
}
108108

109109
if ( context )
@@ -458,6 +458,41 @@ QVariant QgsAggregateCalculator::concatenateStrings( QgsFeatureIterator& fit, in
458458
return result;
459459
}
460460

461+
QVariant QgsAggregateCalculator::defaultValue( QgsAggregateCalculator::Aggregate aggregate ) const
462+
{
463+
// value to return when NO features are aggregated:
464+
switch ( aggregate )
465+
{
466+
// sensible values:
467+
case Count:
468+
case CountDistinct:
469+
case CountMissing:
470+
return 0;
471+
472+
case StringConcatenate:
473+
return ""; // zero length string - not null!
474+
475+
// undefined - nothing makes sense here
476+
case Sum:
477+
case Min:
478+
case Max:
479+
case Mean:
480+
case Median:
481+
case StDev:
482+
case StDevSample:
483+
case Range:
484+
case Minority:
485+
case Majority:
486+
case FirstQuartile:
487+
case ThirdQuartile:
488+
case InterQuartileRange:
489+
case StringMinimumLength:
490+
case StringMaximumLength:
491+
return QVariant();
492+
}
493+
return QVariant();
494+
}
495+
461496
QVariant QgsAggregateCalculator::calculateDateTimeAggregate( QgsFeatureIterator& fit, int attr, QgsExpression* expression,
462497
QgsExpressionContext* context, QgsDateTimeStatisticalSummary::Statistic stat )
463498
{

src/core/qgsaggregatecalculator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ class CORE_EXPORT QgsAggregateCalculator
169169
QgsExpressionContext* context, bool* ok = nullptr );
170170
static QVariant concatenateStrings( QgsFeatureIterator& fit, int attr, QgsExpression* expression,
171171
QgsExpressionContext* context, const QString& delimiter );
172+
173+
QVariant defaultValue( Aggregate aggregate ) const;
172174
};
173175

174176
#endif //QGSAGGREGATECALCULATOR_H

tests/src/python/test_qgsaggregatecalculator.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,36 @@ def testExpression(self):
351351
self.assertTrue(ok)
352352
self.assertEqual(val, 5)
353353

354+
def testExpressionNoMatch(self):
355+
""" test aggregate calculation using an expression with no features """
356+
357+
# no features
358+
layer = QgsVectorLayer("Point?field=fldint:integer", "layer", "memory")
359+
360+
# sum
361+
agg = QgsAggregateCalculator(layer)
362+
val, ok = agg.calculate(QgsAggregateCalculator.Sum, 'fldint * 2')
363+
self.assertTrue(ok)
364+
self.assertEqual(val, None)
365+
366+
# count
367+
agg = QgsAggregateCalculator(layer)
368+
val, ok = agg.calculate(QgsAggregateCalculator.Count, 'fldint * 2')
369+
self.assertTrue(ok)
370+
self.assertEqual(val, 0)
371+
372+
# count distinct
373+
agg = QgsAggregateCalculator(layer)
374+
val, ok = agg.calculate(QgsAggregateCalculator.CountDistinct, 'fldint * 2')
375+
self.assertTrue(ok)
376+
self.assertEqual(val, 0)
377+
378+
# count missing
379+
agg = QgsAggregateCalculator(layer)
380+
val, ok = agg.calculate(QgsAggregateCalculator.CountMissing, 'fldint * 2')
381+
self.assertTrue(ok)
382+
self.assertEqual(val, 0)
383+
354384
def testStringToAggregate(self):
355385
""" test converting strings to aggregate types """
356386

0 commit comments

Comments
 (0)