Skip to content

Commit

Permalink
Add a aggregation method to concatenate unique values
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Apr 5, 2019
1 parent 4582cb8 commit 9fd5509
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 13 deletions.
3 changes: 2 additions & 1 deletion python/core/auto_generated/qgsaggregatecalculator.sip.in
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ to a data provider for remote calculation.
StringMaximumLength, StringMaximumLength,
StringConcatenate, StringConcatenate,
GeometryCollect, GeometryCollect,
ArrayAggregate ArrayAggregate,
StringConcatenateUnique
}; };


struct AggregateParameters struct AggregateParameters
Expand Down
31 changes: 23 additions & 8 deletions src/core/qgsaggregatecalculator.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ QgsAggregateCalculator::Aggregate QgsAggregateCalculator::stringToAggregate( con
return StringMaximumLength; return StringMaximumLength;
else if ( normalized == QLatin1String( "concatenate" ) ) else if ( normalized == QLatin1String( "concatenate" ) )
return StringConcatenate; return StringConcatenate;
else if ( normalized == QLatin1String( "concatenate_unique" ) )
return StringConcatenateUnique;
else if ( normalized == QLatin1String( "collect" ) ) else if ( normalized == QLatin1String( "collect" ) )
return GeometryCollect; return GeometryCollect;
else if ( normalized == QLatin1String( "array_agg" ) ) else if ( normalized == QLatin1String( "array_agg" ) )
Expand Down Expand Up @@ -470,6 +472,13 @@ QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate ag
*ok = true; *ok = true;
return concatenateStrings( fit, attr, expression, context, delimiter ); return concatenateStrings( fit, attr, expression, context, delimiter );
} }
else if ( aggregate == StringConcatenateUnique )
{
//special case
if ( ok )
*ok = true;
return concatenateStrings( fit, attr, expression, context, delimiter, true );
}


bool statOk = false; bool statOk = false;
QgsStringStatisticalSummary::Statistic stat = stringStatFromAggregate( aggregate, &statOk ); QgsStringStatisticalSummary::Statistic stat = stringStatFromAggregate( aggregate, &statOk );
Expand Down Expand Up @@ -529,6 +538,7 @@ QgsStatisticalSummary::Statistic QgsAggregateCalculator::numericStatFromAggregat
case StringMinimumLength: case StringMinimumLength:
case StringMaximumLength: case StringMaximumLength:
case StringConcatenate: case StringConcatenate:
case StringConcatenateUnique:
case GeometryCollect: case GeometryCollect:
case ArrayAggregate: case ArrayAggregate:
{ {
Expand Down Expand Up @@ -577,6 +587,7 @@ QgsStringStatisticalSummary::Statistic QgsAggregateCalculator::stringStatFromAgg
case ThirdQuartile: case ThirdQuartile:
case InterQuartileRange: case InterQuartileRange:
case StringConcatenate: case StringConcatenate:
case StringConcatenateUnique:
case GeometryCollect: case GeometryCollect:
case ArrayAggregate: case ArrayAggregate:
{ {
Expand Down Expand Up @@ -624,6 +635,7 @@ QgsDateTimeStatisticalSummary::Statistic QgsAggregateCalculator::dateTimeStatFro
case StringMinimumLength: case StringMinimumLength:
case StringMaximumLength: case StringMaximumLength:
case StringConcatenate: case StringConcatenate:
case StringConcatenateUnique:
case GeometryCollect: case GeometryCollect:
case ArrayAggregate: case ArrayAggregate:
{ {
Expand Down Expand Up @@ -712,30 +724,32 @@ QVariant QgsAggregateCalculator::calculateGeometryAggregate( QgsFeatureIterator
} }


QVariant QgsAggregateCalculator::concatenateStrings( QgsFeatureIterator &fit, int attr, QgsExpression *expression, QVariant QgsAggregateCalculator::concatenateStrings( QgsFeatureIterator &fit, int attr, QgsExpression *expression,
QgsExpressionContext *context, const QString &delimiter ) QgsExpressionContext *context, const QString &delimiter, bool unique )
{ {
Q_ASSERT( expression || attr >= 0 ); Q_ASSERT( expression || attr >= 0 );


QgsFeature f; QgsFeature f;
QString result; QStringList results;
while ( fit.nextFeature( f ) ) while ( fit.nextFeature( f ) )
{ {
if ( !result.isEmpty() ) QString result;
result += delimiter;

if ( expression ) if ( expression )
{ {
Q_ASSERT( context ); Q_ASSERT( context );
context->setFeature( f ); context->setFeature( f );
QVariant v = expression->evaluate( context ); QVariant v = expression->evaluate( context );
result += v.toString(); result = v.toString();
} }
else else
{ {
result += f.attribute( attr ).toString(); result = f.attribute( attr ).toString();
} }

if ( !unique || !results.contains( result ) )
results << result;
} }
return result;
return results.join( delimiter );
} }


QVariant QgsAggregateCalculator::defaultValue( QgsAggregateCalculator::Aggregate aggregate ) const QVariant QgsAggregateCalculator::defaultValue( QgsAggregateCalculator::Aggregate aggregate ) const
Expand All @@ -750,6 +764,7 @@ QVariant QgsAggregateCalculator::defaultValue( QgsAggregateCalculator::Aggregate
return 0; return 0;


case StringConcatenate: case StringConcatenate:
case StringConcatenateUnique:
return ""; // zero length string - not null! return ""; // zero length string - not null!


case ArrayAggregate: case ArrayAggregate:
Expand Down
5 changes: 3 additions & 2 deletions src/core/qgsaggregatecalculator.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ class CORE_EXPORT QgsAggregateCalculator
StringMaximumLength, //!< Maximum 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(). 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 ArrayAggregate, //!< Create an array of values
StringConcatenateUnique //!< Concatenate unique values with a joining string (string fields only). Specify the delimiter using setDelimiter().
}; };


//! A bundle of parameters controlling aggregate calculation //! A bundle of parameters controlling aggregate calculation
Expand Down Expand Up @@ -207,7 +208,7 @@ class CORE_EXPORT QgsAggregateCalculator
const QString &delimiter, const QString &delimiter,
QgsExpressionContext *context, bool *ok = nullptr ); QgsExpressionContext *context, bool *ok = nullptr );
static QVariant concatenateStrings( QgsFeatureIterator &fit, int attr, QgsExpression *expression, static QVariant concatenateStrings( QgsFeatureIterator &fit, int attr, QgsExpression *expression,
QgsExpressionContext *context, const QString &delimiter ); QgsExpressionContext *context, const QString &delimiter, bool unique = false );


QVariant defaultValue( Aggregate aggregate ) const; QVariant defaultValue( Aggregate aggregate ) const;
}; };
Expand Down
4 changes: 4 additions & 0 deletions tests/src/python/test_qgsaggregatecalculator.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ def testString(self):
val, ok = agg.calculate(QgsAggregateCalculator.StringConcatenate, 'fldstring') val, ok = agg.calculate(QgsAggregateCalculator.StringConcatenate, 'fldstring')
self.assertTrue(ok) self.assertTrue(ok)
self.assertEqual(val, 'cc,aaaa,bbbbbbbb,aaaa,eeee,,eeee,,dddd') self.assertEqual(val, 'cc,aaaa,bbbbbbbb,aaaa,eeee,,eeee,,dddd')
val, ok = agg.calculate(QgsAggregateCalculator.StringConcatenateUnique, 'fldstring')
self.assertTrue(ok)
self.assertEqual(val, 'cc,aaaa,bbbbbbbb,eeee,,dddd')


# bad tests - the following stats should not be calculatable for string fields # bad tests - the following stats should not be calculatable for string fields
for t in [QgsAggregateCalculator.Sum, for t in [QgsAggregateCalculator.Sum,
Expand Down Expand Up @@ -458,6 +461,7 @@ def testStringToAggregate(self):
[QgsAggregateCalculator.StringMinimumLength, 'min_length'], [QgsAggregateCalculator.StringMinimumLength, 'min_length'],
[QgsAggregateCalculator.StringMaximumLength, 'max_length'], [QgsAggregateCalculator.StringMaximumLength, 'max_length'],
[QgsAggregateCalculator.StringConcatenate, 'concatenate'], [QgsAggregateCalculator.StringConcatenate, 'concatenate'],
[QgsAggregateCalculator.StringConcatenateUnique, 'concatenate_unique'],
[QgsAggregateCalculator.GeometryCollect, 'collect']] [QgsAggregateCalculator.GeometryCollect, 'collect']]


for t in tests: for t in tests:
Expand Down
7 changes: 5 additions & 2 deletions tests/src/python/test_qgsvectorlayer.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2074,7 +2074,7 @@ def testAggregate(self):
layer = QgsVectorLayer("Point?field=fldstring:string", "layer", "memory") layer = QgsVectorLayer("Point?field=fldstring:string", "layer", "memory")
pr = layer.dataProvider() pr = layer.dataProvider()


string_values = ['this', 'is', 'a', 'test'] string_values = ['this', 'is', 'a', 'test', 'a', 'nice', 'test']
features = [] features = []
for s in string_values: for s in string_values:
f = QgsFeature() f = QgsFeature()
Expand All @@ -2086,7 +2086,10 @@ def testAggregate(self):
params.delimiter = ' ' params.delimiter = ' '
val, ok = layer.aggregate(QgsAggregateCalculator.StringConcatenate, 'fldstring', params) val, ok = layer.aggregate(QgsAggregateCalculator.StringConcatenate, 'fldstring', params)
self.assertTrue(ok) self.assertTrue(ok)
self.assertEqual(val, 'this is a test') self.assertEqual(val, 'this is a test a nice test')
val, ok = layer.aggregate(QgsAggregateCalculator.StringConcatenateUnique, 'fldstring', params)
self.assertTrue(ok)
self.assertEqual(val, 'this is a test nice')


def testAggregateInVirtualField(self): def testAggregateInVirtualField(self):
""" """
Expand Down
Binary file modified tests/testdata/polys_overlapping_with_id.dbf
Binary file not shown.

0 comments on commit 9fd5509

Please sign in to comment.