Skip to content
Permalink
Browse files

Move join intelligence in QgsVectorlayerJoinBuffer

  • Loading branch information
pblottiere committed Aug 25, 2017
1 parent 500348e commit b6e42c7d2b4b8625c5853ee4f73f82016884f6e6
@@ -132,6 +132,82 @@ Quick way to test if there is any join at all
:rtype: QgsVectorLayerJoinBuffer
%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:
void joinedFieldsChanged();
%Docstring
@@ -951,7 +951,7 @@ bool QgsVectorLayer::addFeature( QgsFeature &feature, Flags )
updateExtents();

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

return success;
@@ -2265,27 +2265,7 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV
{
if ( fields().fieldOrigin( field ) == QgsFields::OriginJoin )
{
int srcFieldIndex;
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;
return mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue );
}
else
{
@@ -2442,7 +2422,7 @@ bool QgsVectorLayer::deleteFeature( QgsFeatureId fid )
return false;

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

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

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

bool res = mEditBuffer->deleteFeatures( fids );

@@ -2476,26 +2456,6 @@ bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds &fids )
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 pkAttributesList;
@@ -2668,83 +2628,11 @@ bool QgsVectorLayer::addFeatures( QgsFeatureList &features, Flags )
updateExtents();

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

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()
{
QgsDebugMsg( "----- Computing Coordinate System" );
@@ -1932,9 +1932,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
//! Read simple labeling from layer's custom properties (QGIS 2.x projects)
QgsAbstractVectorLayerLabeling *readLabelingFromCustomProperties();

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

#ifdef SIP_RUN
QgsVectorLayer( const QgsVectorLayer &rhs );
#endif
@@ -517,3 +517,137 @@ void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl )
connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, 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.
You can’t perform that action at this time.