[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) ) )
This commit is contained in:
Matthias Kuhn 2016-10-25 18:53:36 +02:00
parent fb4578131f
commit f80a33be65
5 changed files with 159 additions and 15 deletions

View File

@ -45,14 +45,20 @@ class QgsExpression
/**
* Get list of columns referenced by the expression.
* @note if the returned list contains the QgsFeatureRequest::AllAttributes constant then
*
* @note If the returned list contains the QgsFeatureRequest::AllAttributes constant then
* all attributes from the layer are required for evaluation of the expression.
* QgsFeatureRequest::setSubsetOfAttributes automatically handles this case.
*
* TODO QGIS3: Return QSet<QString>
* @see referencedAttributeIndexes()
*/
QSet<QString> referencedColumns() const;
/**
* Return a list of all variables which are used in this expression.
*/
QSet<QString> referencedVariables() const;
/**
* Return a list of field name indexes obtained from the provided fields.
*
@ -560,6 +566,11 @@ class QgsExpression
*/
virtual QSet<QString> referencedColumns() const = 0;
/**
* Return a list of all variables which are used in this expression.
*/
virtual QSet<QString> referencedVariables() const = 0;
/**
* Abstract virtual method which returns if the geometry is required to evaluate
* this expression.
@ -638,6 +649,7 @@ class QgsExpression
virtual QString dump() const;
virtual QSet<QString> referencedColumns() const;
virtual QSet<QString> referencedVariables() const;
virtual bool needsGeometry() const;
virtual QgsExpression::Node* clone() const;
};
@ -658,6 +670,7 @@ class QgsExpression
virtual QString dump() const;
virtual QSet<QString> referencedColumns() const;
virtual QSet<QString> referencedVariables() const;
virtual bool needsGeometry() const;
virtual QgsExpression::Node* clone() const;
@ -692,6 +705,7 @@ class QgsExpression
virtual QString dump() const;
virtual QSet<QString> referencedColumns() const;
virtual QSet<QString> referencedVariables() const;
virtual bool needsGeometry() const;
virtual QgsExpression::Node* clone() const;
};
@ -712,6 +726,7 @@ class QgsExpression
virtual QString dump() const;
virtual QSet<QString> referencedColumns() const;
virtual QSet<QString> referencedVariables() const;
virtual bool needsGeometry() const;
virtual QgsExpression::Node* clone() const;
@ -734,6 +749,7 @@ class QgsExpression
virtual QgsExpression::Node* clone() const;
virtual QSet<QString> referencedColumns() const;
virtual QSet<QString> referencedVariables() const;
virtual bool needsGeometry() const;
};
@ -751,6 +767,7 @@ class QgsExpression
virtual QString dump() const;
virtual QSet<QString> referencedColumns() const;
virtual QSet<QString> referencedVariables() const;
virtual bool needsGeometry() const;
virtual QgsExpression::Node* clone() const;
@ -783,6 +800,7 @@ class QgsExpression
virtual QString dump() const;
virtual QSet<QString> referencedColumns() const;
virtual QSet<QString> referencedVariables() const;
virtual bool needsGeometry() const;
virtual QgsExpression::Node* clone() const;
};

View File

@ -622,7 +622,7 @@ static QVariant fcnMin( const QVariantList& values, const QgsExpressionContext*,
return QVariant( minVal );
}
static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionContext* context, QgsExpression *parent )
static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent )
{
//lazy eval, so we need to evaluate nodes now
@ -677,15 +677,28 @@ static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionCon
parameters.delimiter = value.toString();
}
QString cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4" ).arg( vl->id(), QString::number( static_cast< int >( aggregate ) ), subExpression, parameters.filter );
if ( context && context->hasCachedValue( cacheKey ) )
return context->cachedValue( cacheKey );
QVariant result;
if ( context )
{
QString cacheKey = QStringLiteral( "aggfcn:%1:%2:%3:%4" ).arg( vl->id(), QString::number( aggregate ), subExpression, parameters.filter );
QgsExpression subExp( subExpression );
if ( subExp.referencedVariables().contains( "parent" ) || subExp.referencedVariables().contains( QString() ) )
{
cacheKey += ':' + qHash( context->feature() );
}
if ( context && context->hasCachedValue( cacheKey ) )
return context->cachedValue( cacheKey );
QgsExpressionContext subContext( *context );
QgsExpressionContextScope* subScope = new QgsExpressionContextScope();
subScope->setVariable( "parent", context->feature() );
subContext.appendScope( subScope );
result = vl->aggregate( aggregate, subExpression, parameters, &subContext, &ok );
if ( ok )
context->setCachedValue( cacheKey, result );
}
else
{
@ -697,9 +710,6 @@ static QVariant fcnAggregate( const QVariantList& values, const QgsExpressionCon
return QVariant();
}
// cache value
if ( context )
context->setCachedValue( cacheKey, result );
return result;
}
@ -3991,6 +4001,14 @@ QSet<QString> QgsExpression::referencedColumns() const
return d->mRootNode->referencedColumns();
}
QSet<QString> QgsExpression::referencedVariables() const
{
if ( !d->mRootNode )
return QSet<QString>();
return d->mRootNode->referencedVariables();
}
bool QgsExpression::NodeInOperator::needsGeometry() const
{
bool needs = false;
@ -4303,6 +4321,16 @@ QString QgsExpression::NodeUnaryOperator::dump() const
return QStringLiteral( "%1 %2" ).arg( UnaryOperatorText[mOp], mOperand->dump() );
}
QSet<QString> QgsExpression::NodeUnaryOperator::referencedColumns() const
{
return mOperand->referencedColumns();
}
QSet<QString> QgsExpression::NodeUnaryOperator::referencedVariables() const
{
return mOperand->referencedVariables();
}
QgsExpression::Node*QgsExpression::NodeUnaryOperator::clone() const
{
return new NodeUnaryOperator( mOp, mOperand->clone() );
@ -4790,6 +4818,11 @@ QSet<QString> QgsExpression::NodeBinaryOperator::referencedColumns() const
return mOpLeft->referencedColumns() + mOpRight->referencedColumns();
}
QSet<QString> QgsExpression::NodeBinaryOperator::referencedVariables() const
{
return mOpLeft->referencedVariables() + mOpRight->referencedVariables();
}
bool QgsExpression::NodeBinaryOperator::needsGeometry() const
{
return mOpLeft->needsGeometry() || mOpRight->needsGeometry();
@ -4993,6 +5026,23 @@ QSet<QString> QgsExpression::NodeFunction::referencedColumns() const
return functionColumns;
}
QSet<QString> QgsExpression::NodeFunction::referencedVariables() const
{
Function* fd = Functions()[mFnIndex];
if ( fd->name() == "var" )
{
if ( !mArgs->list().isEmpty() )
{
QgsExpression::NodeLiteral* var = dynamic_cast<QgsExpression::NodeLiteral*>( mArgs->list().first() );
if ( var )
return QSet<QString>() << var->value().toString();
}
return QSet<QString>() << QString();
}
else
return QSet<QString>();
}
bool QgsExpression::NodeFunction::needsGeometry() const
{
bool needs = Functions()[mFnIndex]->usesGeometry();
@ -5119,6 +5169,16 @@ QString QgsExpression::NodeLiteral::dump() const
}
}
QSet<QString> QgsExpression::NodeLiteral::referencedColumns() const
{
return QSet<QString>();
}
QSet<QString> QgsExpression::NodeLiteral::referencedVariables() const
{
return QSet<QString>();
}
QgsExpression::Node*QgsExpression::NodeLiteral::clone() const
{
return new NodeLiteral( mValue );
@ -5177,6 +5237,16 @@ QString QgsExpression::NodeColumnRef::dump() const
return QRegExp( "^[A-Za-z_\x80-\xff][A-Za-z0-9_\x80-\xff]*$" ).exactMatch( mName ) ? mName : quotedColumnRef( mName );
}
QSet<QString> QgsExpression::NodeColumnRef::referencedColumns() const
{
return QSet<QString>() << mName;
}
QSet<QString> QgsExpression::NodeColumnRef::referencedVariables() const
{
return QSet<QString>();
}
QgsExpression::Node*QgsExpression::NodeColumnRef::clone() const
{
return new NodeColumnRef( mName );
@ -5253,6 +5323,20 @@ QSet<QString> QgsExpression::NodeCondition::referencedColumns() const
return lst;
}
QSet<QString> QgsExpression::NodeCondition::referencedVariables() const
{
QSet<QString> lst;
Q_FOREACH ( WhenThen* cond, mConditions )
{
lst += cond->mWhenExp->referencedVariables() + cond->mThenExp->referencedVariables();
}
if ( mElseExp )
lst += mElseExp->referencedVariables();
return lst;
}
bool QgsExpression::NodeCondition::needsGeometry() const
{
Q_FOREACH ( WhenThen* cond, mConditions )
@ -5619,6 +5703,14 @@ QSet<QString> QgsExpression::NodeInOperator::referencedColumns() const
return lst;
}
QSet<QString> QgsExpression::NodeInOperator::referencedVariables() const
{
QSet<QString> lst( mNode->referencedVariables() );
Q_FOREACH ( const Node* n, mList->list() )
lst.unite( n->referencedVariables() );
return lst;
}
bool QgsExpression::Function::operator==( const QgsExpression::Function& other ) const
{
if ( QString::compare( mName, other.mName, Qt::CaseInsensitive ) == 0 )

View File

@ -180,11 +180,18 @@ class CORE_EXPORT QgsExpression
* QgsFeatureRequest::setSubsetOfAttributes automatically handles this case.
*
* @see referencedAttributeIndexes()
*
* TODO QGIS3: Return QSet<QString>
*/
QSet<QString> referencedColumns() const;
/**
* Return a list of all variables which are used in this expression.
* If the list contains a NULL QString, there is a variable name used
* which is determined at runtime.
*
* @note Added in QGIS 3.0
*/
QSet<QString> referencedVariables() const;
/**
* Return a list of field name indexes obtained from the provided fields.
*
@ -857,6 +864,11 @@ class CORE_EXPORT QgsExpression
*/
virtual QSet<QString> referencedColumns() const = 0;
/**
* Return a list of all variables which are used in this expression.
*/
virtual QSet<QString> referencedVariables() const = 0;
/**
* Abstract virtual method which returns if the geometry is required to evaluate
* this expression.
@ -953,7 +965,8 @@ class CORE_EXPORT QgsExpression
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
virtual QString dump() const override;
virtual QSet<QString> referencedColumns() const override { return mOperand->referencedColumns(); }
virtual QSet<QString> referencedColumns() const override;
virtual QSet<QString> referencedVariables() const override;
virtual bool needsGeometry() const override { return mOperand->needsGeometry(); }
virtual Node* clone() const override;
@ -984,6 +997,7 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const override;
virtual QSet<QString> referencedColumns() const override;
virtual QSet<QString> referencedVariables() const override;
virtual bool needsGeometry() const override;
virtual Node* clone() const override;
@ -1028,6 +1042,7 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const override;
virtual QSet<QString> referencedColumns() const override;
virtual QSet<QString> referencedVariables() const override;
virtual bool needsGeometry() const override;
virtual Node* clone() const override;
@ -1055,6 +1070,7 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const override;
virtual QSet<QString> referencedColumns() const override;
virtual QSet<QString> referencedVariables() const override;
virtual bool needsGeometry() const override;
virtual Node* clone() const override;
@ -1084,7 +1100,8 @@ class CORE_EXPORT QgsExpression
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
virtual QString dump() const override;
virtual QSet<QString> referencedColumns() const override { return QSet<QString>(); }
virtual QSet<QString> referencedColumns() const override;
virtual QSet<QString> referencedVariables() const override;
virtual bool needsGeometry() const override { return false; }
virtual Node* clone() const override;
@ -1110,7 +1127,8 @@ class CORE_EXPORT QgsExpression
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
virtual QString dump() const override;
virtual QSet<QString> referencedColumns() const override { return QSet<QString>() << mName; }
virtual QSet<QString> referencedColumns() const override;
virtual QSet<QString> referencedVariables() const override;
virtual bool needsGeometry() const override { return false; }
virtual Node* clone() const override;
@ -1162,6 +1180,7 @@ class CORE_EXPORT QgsExpression
virtual QString dump() const override;
virtual QSet<QString> referencedColumns() const override;
virtual QSet<QString> referencedVariables() const override;
virtual bool needsGeometry() const override;
virtual Node* clone() const override;

View File

@ -313,3 +313,16 @@ QDataStream& operator>>( QDataStream& in, QgsFeature& feature )
feature.setValid( valid );
return in;
}
uint qHash( const QgsFeature& key, uint seed )
{
uint hash = seed;
Q_FOREACH ( const QVariant& attr, key.attributes() )
{
hash ^= qHash( attr.toString() );
}
hash ^= qHash( key.geometry().exportToWkt() );
return hash;
}

View File

@ -349,6 +349,8 @@ typedef QMap<int, QString> QgsFieldNameMap;
typedef QList<QgsFeature> QgsFeatureList;
uint qHash( const QgsFeature& key, uint seed = 0 );
Q_DECLARE_METATYPE( QgsFeature )
Q_DECLARE_METATYPE( QgsFeatureList )