Skip to content

Commit f80a33b

Browse files
committedOct 27, 2016
[FEATURE] Expose @parent variable in aggregate functions
This makes it possible to access attributes and geometry from the parent feature when in the filter of the "aggregate" expression function. With this in place aggregates can be calculated per feature. E.g. max "measurement" for each point_station per polygon_research_area. Or a default attribute value when digitizing features: aggregate(layer:='countries', aggregate:='max', expression:=\"code\", filter:=intersects( $geometry, geometry(@parent) ) )
1 parent fb45781 commit f80a33b

File tree

5 files changed

+159
-15
lines changed

5 files changed

+159
-15
lines changed
 

‎python/core/qgsexpression.sip

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,20 @@ class QgsExpression
4545

4646
/**
4747
* Get list of columns referenced by the expression.
48-
* @note if the returned list contains the QgsFeatureRequest::AllAttributes constant then
48+
*
49+
* @note If the returned list contains the QgsFeatureRequest::AllAttributes constant then
4950
* all attributes from the layer are required for evaluation of the expression.
5051
* QgsFeatureRequest::setSubsetOfAttributes automatically handles this case.
5152
*
52-
* TODO QGIS3: Return QSet<QString>
53+
* @see referencedAttributeIndexes()
5354
*/
5455
QSet<QString> referencedColumns() const;
5556

57+
/**
58+
* Return a list of all variables which are used in this expression.
59+
*/
60+
QSet<QString> referencedVariables() const;
61+
5662
/**
5763
* Return a list of field name indexes obtained from the provided fields.
5864
*
@@ -560,6 +566,11 @@ class QgsExpression
560566
*/
561567
virtual QSet<QString> referencedColumns() const = 0;
562568

569+
/**
570+
* Return a list of all variables which are used in this expression.
571+
*/
572+
virtual QSet<QString> referencedVariables() const = 0;
573+
563574
/**
564575
* Abstract virtual method which returns if the geometry is required to evaluate
565576
* this expression.
@@ -638,6 +649,7 @@ class QgsExpression
638649
virtual QString dump() const;
639650

640651
virtual QSet<QString> referencedColumns() const;
652+
virtual QSet<QString> referencedVariables() const;
641653
virtual bool needsGeometry() const;
642654
virtual QgsExpression::Node* clone() const;
643655
};
@@ -658,6 +670,7 @@ class QgsExpression
658670
virtual QString dump() const;
659671

660672
virtual QSet<QString> referencedColumns() const;
673+
virtual QSet<QString> referencedVariables() const;
661674
virtual bool needsGeometry() const;
662675
virtual QgsExpression::Node* clone() const;
663676

@@ -692,6 +705,7 @@ class QgsExpression
692705
virtual QString dump() const;
693706

694707
virtual QSet<QString> referencedColumns() const;
708+
virtual QSet<QString> referencedVariables() const;
695709
virtual bool needsGeometry() const;
696710
virtual QgsExpression::Node* clone() const;
697711
};
@@ -712,6 +726,7 @@ class QgsExpression
712726
virtual QString dump() const;
713727

714728
virtual QSet<QString> referencedColumns() const;
729+
virtual QSet<QString> referencedVariables() const;
715730
virtual bool needsGeometry() const;
716731
virtual QgsExpression::Node* clone() const;
717732

@@ -734,6 +749,7 @@ class QgsExpression
734749
virtual QgsExpression::Node* clone() const;
735750

736751
virtual QSet<QString> referencedColumns() const;
752+
virtual QSet<QString> referencedVariables() const;
737753
virtual bool needsGeometry() const;
738754
};
739755

@@ -751,6 +767,7 @@ class QgsExpression
751767
virtual QString dump() const;
752768

753769
virtual QSet<QString> referencedColumns() const;
770+
virtual QSet<QString> referencedVariables() const;
754771
virtual bool needsGeometry() const;
755772

756773
virtual QgsExpression::Node* clone() const;
@@ -783,6 +800,7 @@ class QgsExpression
783800
virtual QString dump() const;
784801

785802
virtual QSet<QString> referencedColumns() const;
803+
virtual QSet<QString> referencedVariables() const;
786804
virtual bool needsGeometry() const;
787805
virtual QgsExpression::Node* clone() const;
788806
};

‎src/core/qgsexpression.cpp

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ static QVariant fcnMin( const QVariantList& values, const QgsExpressionContext*,
622622
return QVariant( minVal );
623623
}
624624

625-
static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionContext* context, QgsExpression *parent )
625+
static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent )
626626
{
627627
//lazy eval, so we need to evaluate nodes now
628628

@@ -677,15 +677,28 @@ static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionCon
677677
parameters.delimiter = value.toString();
678678
}
679679

680-
QString cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter );
681-
if ( context && context->hasCachedValue( cacheKey ) )
682-
return context->cachedValue( cacheKey );
683-
684680
QVariant result;
685681
if ( context )
686682
{
683+
QString cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter );
684+
685+
QgsExpression subExp( subExpression );
686+
if ( subExp.referencedVariables().contains( "parent" ) || subExp.referencedVariables().contains( QString() ) )
687+
{
688+
cacheKey += ':' + qHash( context->feature() );
689+
}
690+
691+
if ( context && context->hasCachedValue( cacheKey ) )
692+
return context->cachedValue( cacheKey );
693+
687694
QgsExpressionContext subContext( *context );
695+
QgsExpressionContextScope* subScope = new QgsExpressionContextScope();
696+
subScope->setVariable( "parent", context->feature() );
697+
subContext.appendScope( subScope );
688698
result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
699+
700+
if ( ok )
701+
context->setCachedValue( cacheKey, result );
689702
}
690703
else
691704
{
@@ -697,9 +710,6 @@ static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionCon
697710
return QVariant();
698711
}
699712

700-
// cache value
701-
if ( context )
702-
context->setCachedValue( cacheKey, result );
703713
return result;
704714
}
705715

@@ -3991,6 +4001,14 @@ QSet<QString> QgsExpression::referencedColumns() const
39914001
return d->mRootNode->referencedColumns();
39924002
}
39934003

4004+
QSet<QString> QgsExpression::referencedVariables() const
4005+
{
4006+
if ( !d->mRootNode )
4007+
return QSet<QString>();
4008+
4009+
return d->mRootNode->referencedVariables();
4010+
}
4011+
39944012
bool QgsExpression::NodeInOperator::needsGeometry() const
39954013
{
39964014
bool needs = false;
@@ -4303,6 +4321,16 @@ QString QgsExpression::NodeUnaryOperator::dump() const
43034321
return QStringLiteral( "%1 %2" ).arg( UnaryOperatorText[mOp], mOperand->dump() );
43044322
}
43054323

4324+
QSet<QString> QgsExpression::NodeUnaryOperator::referencedColumns() const
4325+
{
4326+
return mOperand->referencedColumns();
4327+
}
4328+
4329+
QSet<QString> QgsExpression::NodeUnaryOperator::referencedVariables() const
4330+
{
4331+
return mOperand->referencedVariables();
4332+
}
4333+
43064334
QgsExpression::Node*QgsExpression::NodeUnaryOperator::clone() const
43074335
{
43084336
return new NodeUnaryOperator( mOp, mOperand->clone() );
@@ -4790,6 +4818,11 @@ QSet<QString> QgsExpression::NodeBinaryOperator::referencedColumns() const
47904818
return mOpLeft->referencedColumns() + mOpRight->referencedColumns();
47914819
}
47924820

4821+
QSet<QString> QgsExpression::NodeBinaryOperator::referencedVariables() const
4822+
{
4823+
return mOpLeft->referencedVariables() + mOpRight->referencedVariables();
4824+
}
4825+
47934826
bool QgsExpression::NodeBinaryOperator::needsGeometry() const
47944827
{
47954828
return mOpLeft->needsGeometry() || mOpRight->needsGeometry();
@@ -4993,6 +5026,23 @@ QSet<QString> QgsExpression::NodeFunction::referencedColumns() const
49935026
return functionColumns;
49945027
}
49955028

5029+
QSet<QString> QgsExpression::NodeFunction::referencedVariables() const
5030+
{
5031+
Function* fd = Functions()[mFnIndex];
5032+
if ( fd->name() == "var" )
5033+
{
5034+
if ( !mArgs->list().isEmpty() )
5035+
{
5036+
QgsExpression::NodeLiteral* var = dynamic_cast<QgsExpression::NodeLiteral*>( mArgs->list().first() );
5037+
if ( var )
5038+
return QSet<QString>() << var->value().toString();
5039+
}
5040+
return QSet<QString>() << QString();
5041+
}
5042+
else
5043+
return QSet<QString>();
5044+
}
5045+
49965046
bool QgsExpression::NodeFunction::needsGeometry() const
49975047
{
49985048
bool needs = Functions()[mFnIndex]->usesGeometry();
@@ -5119,6 +5169,16 @@ QString QgsExpression::NodeLiteral::dump() const
51195169
}
51205170
}
51215171

5172+
QSet<QString> QgsExpression::NodeLiteral::referencedColumns() const
5173+
{
5174+
return QSet<QString>();
5175+
}
5176+
5177+
QSet<QString> QgsExpression::NodeLiteral::referencedVariables() const
5178+
{
5179+
return QSet<QString>();
5180+
}
5181+
51225182
QgsExpression::Node*QgsExpression::NodeLiteral::clone() const
51235183
{
51245184
return new NodeLiteral( mValue );
@@ -5177,6 +5237,16 @@ QString QgsExpression::NodeColumnRef::dump() const
51775237
return QRegExp( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" ).exactMatch( mName ) ? mName : quotedColumnRef( mName );
51785238
}
51795239

5240+
QSet<QString> QgsExpression::NodeColumnRef::referencedColumns() const
5241+
{
5242+
return QSet<QString>() << mName;
5243+
}
5244+
5245+
QSet<QString> QgsExpression::NodeColumnRef::referencedVariables() const
5246+
{
5247+
return QSet<QString>();
5248+
}
5249+
51805250
QgsExpression::Node*QgsExpression::NodeColumnRef::clone() const
51815251
{
51825252
return new NodeColumnRef( mName );
@@ -5253,6 +5323,20 @@ QSet<QString> QgsExpression::NodeCondition::referencedColumns() const
52535323
return lst;
52545324
}
52555325

5326+
QSet<QString> QgsExpression::NodeCondition::referencedVariables() const
5327+
{
5328+
QSet<QString> lst;
5329+
Q_FOREACH ( WhenThen* cond, mConditions )
5330+
{
5331+
lst += cond->mWhenExp->referencedVariables() + cond->mThenExp->referencedVariables();
5332+
}
5333+
5334+
if ( mElseExp )
5335+
lst += mElseExp->referencedVariables();
5336+
5337+
return lst;
5338+
}
5339+
52565340
bool QgsExpression::NodeCondition::needsGeometry() const
52575341
{
52585342
Q_FOREACH ( WhenThen* cond, mConditions )
@@ -5619,6 +5703,14 @@ QSet<QString> QgsExpression::NodeInOperator::referencedColumns() const
56195703
return lst;
56205704
}
56215705

5706+
QSet<QString> QgsExpression::NodeInOperator::referencedVariables() const
5707+
{
5708+
QSet<QString> lst( mNode->referencedVariables() );
5709+
Q_FOREACH ( const Node* n, mList->list() )
5710+
lst.unite( n->referencedVariables() );
5711+
return lst;
5712+
}
5713+
56225714
bool QgsExpression::Function::operator==( const QgsExpression::Function& other ) const
56235715
{
56245716
if ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 )

‎src/core/qgsexpression.h

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,18 @@ class CORE_EXPORT QgsExpression
180180
* QgsFeatureRequest::setSubsetOfAttributes automatically handles this case.
181181
*
182182
* @see referencedAttributeIndexes()
183-
*
184-
* TODO QGIS3: Return QSet<QString>
185183
*/
186184
QSet<QString> referencedColumns() const;
187185

186+
/**
187+
* Return a list of all variables which are used in this expression.
188+
* If the list contains a NULL QString, there is a variable name used
189+
* which is determined at runtime.
190+
*
191+
* @note Added in QGIS 3.0
192+
*/
193+
QSet<QString> referencedVariables() const;
194+
188195
/**
189196
* Return a list of field name indexes obtained from the provided fields.
190197
*
@@ -857,6 +864,11 @@ class CORE_EXPORT QgsExpression
857864
*/
858865
virtual QSet<QString> referencedColumns() const = 0;
859866

867+
/**
868+
* Return a list of all variables which are used in this expression.
869+
*/
870+
virtual QSet<QString> referencedVariables() const = 0;
871+
860872
/**
861873
* Abstract virtual method which returns if the geometry is required to evaluate
862874
* this expression.
@@ -953,7 +965,8 @@ class CORE_EXPORT QgsExpression
953965
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
954966
virtual QString dump() const override;
955967

956-
virtual QSet<QString> referencedColumns() const override { return mOperand->referencedColumns(); }
968+
virtual QSet<QString> referencedColumns() const override;
969+
virtual QSet<QString> referencedVariables() const override;
957970
virtual bool needsGeometry() const override { return mOperand->needsGeometry(); }
958971
virtual Node* clone() const override;
959972

@@ -984,6 +997,7 @@ class CORE_EXPORT QgsExpression
984997
virtual QString dump() const override;
985998

986999
virtual QSet<QString> referencedColumns() const override;
1000+
virtual QSet<QString> referencedVariables() const override;
9871001
virtual bool needsGeometry() const override;
9881002
virtual Node* clone() const override;
9891003

@@ -1028,6 +1042,7 @@ class CORE_EXPORT QgsExpression
10281042
virtual QString dump() const override;
10291043

10301044
virtual QSet<QString> referencedColumns() const override;
1045+
virtual QSet<QString> referencedVariables() const override;
10311046
virtual bool needsGeometry() const override;
10321047
virtual Node* clone() const override;
10331048

@@ -1055,6 +1070,7 @@ class CORE_EXPORT QgsExpression
10551070
virtual QString dump() const override;
10561071

10571072
virtual QSet<QString> referencedColumns() const override;
1073+
virtual QSet<QString> referencedVariables() const override;
10581074
virtual bool needsGeometry() const override;
10591075
virtual Node* clone() const override;
10601076

@@ -1084,7 +1100,8 @@ class CORE_EXPORT QgsExpression
10841100
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
10851101
virtual QString dump() const override;
10861102

1087-
virtual QSet<QString> referencedColumns() const override { return QSet<QString>(); }
1103+
virtual QSet<QString> referencedColumns() const override;
1104+
virtual QSet<QString> referencedVariables() const override;
10881105
virtual bool needsGeometry() const override { return false; }
10891106
virtual Node* clone() const override;
10901107

@@ -1110,7 +1127,8 @@ class CORE_EXPORT QgsExpression
11101127
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
11111128
virtual QString dump() const override;
11121129

1113-
virtual QSet<QString> referencedColumns() const override { return QSet<QString>() << mName; }
1130+
virtual QSet<QString> referencedColumns() const override;
1131+
virtual QSet<QString> referencedVariables() const override;
11141132
virtual bool needsGeometry() const override { return false; }
11151133

11161134
virtual Node* clone() const override;
@@ -1162,6 +1180,7 @@ class CORE_EXPORT QgsExpression
11621180
virtual QString dump() const override;
11631181

11641182
virtual QSet<QString> referencedColumns() const override;
1183+
virtual QSet<QString> referencedVariables() const override;
11651184
virtual bool needsGeometry() const override;
11661185
virtual Node* clone() const override;
11671186

‎src/core/qgsfeature.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,16 @@ QDataStream& operator>>( QDataStream& in, QgsFeature& feature )
313313
feature.setValid( valid );
314314
return in;
315315
}
316+
317+
uint qHash( const QgsFeature& key, uint seed )
318+
{
319+
uint hash = seed;
320+
Q_FOREACH ( const QVariant& attr, key.attributes() )
321+
{
322+
hash ^= qHash( attr.toString() );
323+
}
324+
325+
hash ^= qHash( key.geometry().exportToWkt() );
326+
327+
return hash;
328+
}

‎src/core/qgsfeature.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ typedef QMap<int, QString> QgsFieldNameMap;
349349

350350
typedef QList<QgsFeature> QgsFeatureList;
351351

352+
uint qHash( const QgsFeature& key, uint seed = 0 );
353+
352354
Q_DECLARE_METATYPE( QgsFeature )
353355
Q_DECLARE_METATYPE( QgsFeatureList )
354356

1 commit comments

Comments
 (1)

andreasneumann commented on Oct 28, 2016

@andreasneumann
Member

Nice! The expressions are more and more powerful! Thanks for the improvement.

Please sign in to comment.