Skip to content

Commit

Permalink
Move join intelligence in QgsVectorlayerJoinBuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
pblottiere committed Aug 28, 2017
1 parent 500348e commit b6e42c7
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 120 deletions.
76 changes: 76 additions & 0 deletions python/core/qgsvectorlayerjoinbuffer.sip
Expand Up @@ -132,6 +132,82 @@ Quick way to test if there is any join at all
:rtype: QgsVectorLayerJoinBuffer :rtype: QgsVectorLayerJoinBuffer
%End %End


bool addFeature( const QgsFeature &feature ) const;
%Docstring
Adds a feature in joined layers. The feature given in parameter is the
one added in target layer. If a corresponding joined feature yet exists
in a joined layer, then this feature is just updated. Note that if a
corresponding joined feature has only empty fields, then it's not
created nor added.

\param feature The feature added in the target layer.

:return: false if an error happened, true otherwise

.. versionadded:: 3.0
:rtype: bool
%End

bool addFeatures( const QgsFeatureList &features ) const;
%Docstring
Adds a list of features in joined layers. Features given in parameter
are those added in target layer. If a corresponding joined feature yet
exists in a joined layer, then this feature is just updated. Note that
if a corresponding joined feature has only empty fields, then it's not
created nor added.

\param features The list of features added in the target layer

:return: false if an error happened, true otherwise

.. versionadded:: 3.0
:rtype: bool
%End

bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant() ) const;
%Docstring
Changes attribute value in joined layers. The feature id given in
parameter is the one added in target layer. If the corresponding joined
feature does not exist in a joined layer, then it's automatically
created if its fields are not empty.

\param fid The feature id
\param field The field to update
\param newValue The new value of the attribute
\param oldValue The old value of the attribute

:return: false if an error happened, true otherwise

.. versionadded:: 3.0
:rtype: bool
%End

bool deleteFeature( QgsFeatureId fid ) const;
%Docstring
Deletes a feature from joined layers. The feature id given in
parameter is the one coming from the target layer.

\param fid The feature id from the target layer to delete

:return: false if an error happened, true otherwise

.. versionadded:: 3.0
:rtype: bool
%End

bool deleteFeatures( const QgsFeatureIds &fids ) const;
%Docstring
Deletes a list of features from joined layers. Feature ids given
in aprameter are those coming from the target layer.

\param fids Feature ids from the target layer to delete

:return: false if an error happened, true otherwise

.. versionadded:: 3.0
:rtype: bool
%End

signals: signals:
void joinedFieldsChanged(); void joinedFieldsChanged();
%Docstring %Docstring
Expand Down
122 changes: 5 additions & 117 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -951,7 +951,7 @@ bool QgsVectorLayer::addFeature( QgsFeature &feature, Flags )
updateExtents(); updateExtents();


if ( mJoinBuffer->containsJoins() ) if ( mJoinBuffer->containsJoins() )
success = addFeaturesToJoinedLayers( QgsFeatureList() << feature ); success = mJoinBuffer->addFeature( feature );
} }


return success; return success;
Expand Down Expand Up @@ -2265,27 +2265,7 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV
{ {
if ( fields().fieldOrigin( field ) == QgsFields::OriginJoin ) if ( fields().fieldOrigin( field ) == QgsFields::OriginJoin )
{ {
int srcFieldIndex; return mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue );
const QgsVectorLayerJoinInfo *info = mJoinBuffer->joinForFieldIndex( field, fields(), srcFieldIndex );
if ( info && info->joinLayer() && info->isEditable() )
{
QgsFeature feature = getFeature( fid );

if ( !feature.isValid() )
return false;

const QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( info, feature );

if ( joinFeature.isValid() )
return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue );
else
{
feature.setAttribute( field, newValue );
return addFeaturesToJoinedLayers( QgsFeatureList() << feature );
}
}
else
return false;
} }
else else
{ {
Expand Down Expand Up @@ -2442,7 +2422,7 @@ bool QgsVectorLayer::deleteFeature( QgsFeatureId fid )
return false; return false;


if ( mJoinBuffer->containsJoins() ) if ( mJoinBuffer->containsJoins() )
deleteFeaturesFromJoinedLayers( QgsFeatureIds() << fid ); mJoinBuffer->deleteFeature( fid );


bool res = mEditBuffer->deleteFeature( fid ); bool res = mEditBuffer->deleteFeature( fid );
if ( res ) if ( res )
Expand All @@ -2463,7 +2443,7 @@ bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds &fids )
} }


if ( mJoinBuffer->containsJoins() ) if ( mJoinBuffer->containsJoins() )
deleteFeaturesFromJoinedLayers( fids ); mJoinBuffer->deleteFeatures( fids );


bool res = mEditBuffer->deleteFeatures( fids ); bool res = mEditBuffer->deleteFeatures( fids );


Expand All @@ -2476,26 +2456,6 @@ bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds &fids )
return res; return res;
} }


bool QgsVectorLayer::deleteFeaturesFromJoinedLayers( QgsFeatureIds fids )
{
bool rc = false;

Q_FOREACH ( const QgsFeatureId &fid, fids )
{
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() )
{
if ( info.isEditable() && info.hasCascadedDelete() )
{
const QgsFeature joinFeature = mJoinBuffer->joinedFeatureOf( &info, getFeature( fid ) );
if ( joinFeature.isValid() )
info.joinLayer()->deleteFeature( joinFeature.id() );
}
}
}

return rc;
}

QgsAttributeList QgsVectorLayer::pkAttributeList() const QgsAttributeList QgsVectorLayer::pkAttributeList() const
{ {
QgsAttributeList pkAttributesList; QgsAttributeList pkAttributesList;
Expand Down Expand Up @@ -2668,83 +2628,11 @@ bool QgsVectorLayer::addFeatures( QgsFeatureList &features, Flags )
updateExtents(); updateExtents();


if ( res && mJoinBuffer->containsJoins() ) if ( res && mJoinBuffer->containsJoins() )
res = addFeaturesToJoinedLayers( features ); res = mJoinBuffer->addFeatures( features );


return res; return res;
} }


bool QgsVectorLayer::addFeaturesToJoinedLayers( QgsFeatureList &features, Flags )
{
// try to add/update a feature in each joined layer
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() )
{
QgsVectorLayer *joinLayer = info.joinLayer();

if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
{
QgsFeatureList joinFeatures;

Q_FOREACH ( const QgsFeature &feature, features )
{
const QgsFeature joinFeature = info.extractJoinedFeature( feature );

// we don't want to add a new feature in joined layer when the id
// column value yet exist, we just want to update the existing one
const QVariant idFieldValue = feature.attribute( info.targetFieldName() );
const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() );

QgsFeatureRequest request;
request.setFilterExpression( filter );
request.setLimit( 1 );

QgsFeatureIterator it = info.joinLayer()->getFeatures( request );
QgsFeature existingFeature;
it.nextFeature( existingFeature );

if ( existingFeature.isValid() )
{
const QStringList *subsetFields = info.joinFieldNamesSubset();
if ( subsetFields )
{
Q_FOREACH ( const QString &field, *subsetFields )
existingFeature.setAttribute( field, joinFeature.attribute( field ) );
}
else
{
Q_FOREACH ( const QgsField &field, joinFeature.fields() )
existingFeature.setAttribute( field.name(), joinFeature.attribute( field.name() ) );
}

joinLayer->updateFeature( existingFeature );
}
else
{
// joined feature is added only if one of its field is not null
bool notNullFields = false;
Q_FOREACH ( const QgsField &field, joinFeature.fields() )
{
if ( field.name() == info.joinFieldName() )
continue;

if ( !joinFeature.attribute( field.name() ).isNull() )
{
notNullFields = true;
break;
}
}

if ( notNullFields )
joinFeatures << joinFeature;
}
}

joinLayer->addFeatures( joinFeatures );
}
}

return true;
}

void QgsVectorLayer::setCoordinateSystem() void QgsVectorLayer::setCoordinateSystem()
{ {
QgsDebugMsg( "----- Computing Coordinate System" ); QgsDebugMsg( "----- Computing Coordinate System" );
Expand Down
3 changes: 0 additions & 3 deletions src/core/qgsvectorlayer.h
Expand Up @@ -1932,9 +1932,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
//! Read simple labeling from layer's custom properties (QGIS 2.x projects) //! Read simple labeling from layer's custom properties (QGIS 2.x projects)
QgsAbstractVectorLayerLabeling *readLabelingFromCustomProperties(); QgsAbstractVectorLayerLabeling *readLabelingFromCustomProperties();


bool addFeaturesToJoinedLayers( QgsFeatureList &features, Flags flags = 0 );
bool deleteFeaturesFromJoinedLayers( QgsFeatureIds fids );

#ifdef SIP_RUN #ifdef SIP_RUN
QgsVectorLayer( const QgsVectorLayer &rhs ); QgsVectorLayer( const QgsVectorLayer &rhs );
#endif #endif
Expand Down
134 changes: 134 additions & 0 deletions src/core/qgsvectorlayerjoinbuffer.cpp
Expand Up @@ -517,3 +517,137 @@ void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl )
connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection ); connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection );
connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection ); connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection );
} }

bool QgsVectorLayerJoinBuffer::addFeature( const QgsFeature &feature ) const
{
return addFeatures( QgsFeatureList() << feature );
}

bool QgsVectorLayerJoinBuffer::addFeatures( const QgsFeatureList &features ) const
{
if ( !containsJoins() )
return false;

// try to add/update a feature in each joined layer
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() )
{
QgsVectorLayer *joinLayer = info.joinLayer();

if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
{
QgsFeatureList joinFeatures;

Q_FOREACH ( const QgsFeature &feature, features )
{
const QgsFeature joinFeature = info.extractJoinedFeature( feature );

// we don't want to add a new feature in joined layer when the id
// column value yet exist, we just want to update the existing one
const QVariant idFieldValue = feature.attribute( info.targetFieldName() );
const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() );

QgsFeatureRequest request;
request.setFilterExpression( filter );
request.setLimit( 1 );

QgsFeatureIterator it = info.joinLayer()->getFeatures( request );
QgsFeature existingFeature;
it.nextFeature( existingFeature );

if ( existingFeature.isValid() )
{
const QStringList *subsetFields = info.joinFieldNamesSubset();
if ( subsetFields )
{
Q_FOREACH ( const QString &field, *subsetFields )
existingFeature.setAttribute( field, joinFeature.attribute( field ) );
}
else
{
Q_FOREACH ( const QgsField &field, joinFeature.fields() )
existingFeature.setAttribute( field.name(), joinFeature.attribute( field.name() ) );
}

joinLayer->updateFeature( existingFeature );
}
else
{
// joined feature is added only if one of its field is not null
bool notNullFields = false;
Q_FOREACH ( const QgsField &field, joinFeature.fields() )
{
if ( field.name() == info.joinFieldName() )
continue;

if ( !joinFeature.attribute( field.name() ).isNull() )
{
notNullFields = true;
break;
}
}

if ( notNullFields )
joinFeatures << joinFeature;
}
}

joinLayer->addFeatures( joinFeatures );
}
}

return true;
}

bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) const
{
if ( mLayer->fields().fieldOrigin( field ) != QgsFields::OriginJoin )
return false;

int srcFieldIndex;
const QgsVectorLayerJoinInfo *info = joinForFieldIndex( field, mLayer->fields(), srcFieldIndex );
if ( info && info->joinLayer() && info->isEditable() )
{
QgsFeature feature = mLayer->getFeature( fid );

if ( !feature.isValid() )
return false;

const QgsFeature joinFeature = joinedFeatureOf( info, feature );

if ( joinFeature.isValid() )
return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue );
else
{
feature.setAttribute( field, newValue );
return addFeatures( QgsFeatureList() << feature );
}
}
else
return false;
}

bool QgsVectorLayerJoinBuffer::deleteFeature( QgsFeatureId fid ) const
{
return deleteFeatures( QgsFeatureIds() << fid );
}

bool QgsVectorLayerJoinBuffer::deleteFeatures( const QgsFeatureIds &fids ) const
{
if ( !containsJoins() )
return false;

Q_FOREACH ( const QgsFeatureId &fid, fids )
{
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vectorJoins() )
{
if ( info.isEditable() && info.hasCascadedDelete() )
{
const QgsFeature joinFeature = joinedFeatureOf( &info, mLayer->getFeature( fid ) );
if ( joinFeature.isValid() )
info.joinLayer()->deleteFeature( joinFeature.id() );
}
}
}

return true;
}

0 comments on commit b6e42c7

Please sign in to comment.