Skip to content
Permalink
Browse files

Add a aggregation method to concatenate unique values

  • Loading branch information
nirvn committed Apr 5, 2019
1 parent 4582cb8 commit 9fd5509ad0b147c043cd0a871f781081bd940993
@@ -56,7 +56,8 @@ to a data provider for remote calculation.
StringMaximumLength,
StringConcatenate,
GeometryCollect,
ArrayAggregate
ArrayAggregate,
StringConcatenateUnique
};

struct AggregateParameters
@@ -163,6 +163,8 @@ QgsAggregateCalculator::Aggregate QgsAggregateCalculator::stringToAggregate( con
return StringMaximumLength;
else if ( normalized == QLatin1String( "concatenate" ) )
return StringConcatenate;
else if ( normalized == QLatin1String( "concatenate_unique" ) )
return StringConcatenateUnique;
else if ( normalized == QLatin1String( "collect" ) )
return GeometryCollect;
else if ( normalized == QLatin1String( "array_agg" ) )
@@ -470,6 +472,13 @@ QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate ag
*ok = true;
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;
QgsStringStatisticalSummary::Statistic stat = stringStatFromAggregate( aggregate, &statOk );
@@ -529,6 +538,7 @@ QgsStatisticalSummary::Statistic QgsAggregateCalculator::numericStatFromAggregat
case StringMinimumLength:
case StringMaximumLength:
case StringConcatenate:
case StringConcatenateUnique:
case GeometryCollect:
case ArrayAggregate:
{
@@ -577,6 +587,7 @@ QgsStringStatisticalSummary::Statistic QgsAggregateCalculator::stringStatFromAgg
case ThirdQuartile:
case InterQuartileRange:
case StringConcatenate:
case StringConcatenateUnique:
case GeometryCollect:
case ArrayAggregate:
{
@@ -624,6 +635,7 @@ QgsDateTimeStatisticalSummary::Statistic QgsAggregateCalculator::dateTimeStatFro
case StringMinimumLength:
case StringMaximumLength:
case StringConcatenate:
case StringConcatenateUnique:
case GeometryCollect:
case ArrayAggregate:
{
@@ -712,30 +724,32 @@ QVariant QgsAggregateCalculator::calculateGeometryAggregate( QgsFeatureIterator
}

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 );

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

QString result;
if ( expression )
{
Q_ASSERT( context );
context->setFeature( f );
QVariant v = expression->evaluate( context );
result += v.toString();
result = v.toString();
}
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
@@ -750,6 +764,7 @@ QVariant QgsAggregateCalculator::defaultValue( QgsAggregateCalculator::Aggregate
return 0;

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

case ArrayAggregate:
@@ -81,7 +81,8 @@ class CORE_EXPORT QgsAggregateCalculator
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
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
@@ -207,7 +208,7 @@ class CORE_EXPORT QgsAggregateCalculator
const QString &delimiter,
QgsExpressionContext *context, bool *ok = nullptr );
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;
};
@@ -188,6 +188,9 @@ def testString(self):
val, ok = agg.calculate(QgsAggregateCalculator.StringConcatenate, 'fldstring')
self.assertTrue(ok)
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
for t in [QgsAggregateCalculator.Sum,
@@ -458,6 +461,7 @@ def testStringToAggregate(self):
[QgsAggregateCalculator.StringMinimumLength, 'min_length'],
[QgsAggregateCalculator.StringMaximumLength, 'max_length'],
[QgsAggregateCalculator.StringConcatenate, 'concatenate'],
[QgsAggregateCalculator.StringConcatenateUnique, 'concatenate_unique'],
[QgsAggregateCalculator.GeometryCollect, 'collect']]

for t in tests:
@@ -2074,7 +2074,7 @@ def testAggregate(self):
layer = QgsVectorLayer("Point?field=fldstring:string", "layer", "memory")
pr = layer.dataProvider()

string_values = ['this', 'is', 'a', 'test']
string_values = ['this', 'is', 'a', 'test', 'a', 'nice', 'test']
features = []
for s in string_values:
f = QgsFeature()
@@ -2086,7 +2086,10 @@ def testAggregate(self):
params.delimiter = ' '
val, ok = layer.aggregate(QgsAggregateCalculator.StringConcatenate, 'fldstring', params)
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):
"""
Binary file not shown.

0 comments on commit 9fd5509

Please sign in to comment.
You can’t perform that action at this time.