QGIS API Documentation  2.99.0-Master (f867b65)
qgsvectorlayerjoinbuffer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerjoinbuffer.cpp
3  ----------------------------
4  begin : Feb 09, 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 
20 #include "qgsfeatureiterator.h"
21 #include "qgslogger.h"
22 #include "qgsproject.h"
23 #include "qgsvectordataprovider.h"
24 
25 #include <QDomElement>
26 
28  : mLayer( layer )
29 {
30 }
31 
32 static QList<QgsVectorLayer *> _outEdges( QgsVectorLayer *vl )
33 {
34  QList<QgsVectorLayer *> lst;
35  Q_FOREACH ( const QgsVectorLayerJoinInfo &info, vl->vectorJoins() )
36  {
37  if ( QgsVectorLayer *joinVl = info.joinLayer() )
38  lst << joinVl;
39  }
40  return lst;
41 }
42 
43 static bool _hasCycleDFS( QgsVectorLayer *n, QHash<QgsVectorLayer *, int> &mark )
44 {
45  if ( mark.value( n ) == 1 ) // temporary
46  return true;
47  if ( mark.value( n ) == 0 ) // not visited
48  {
49  mark[n] = 1; // temporary
50  Q_FOREACH ( QgsVectorLayer *m, _outEdges( n ) )
51  {
52  if ( _hasCycleDFS( m, mark ) )
53  return true;
54  }
55  mark[n] = 2; // permanent
56  }
57  return false;
58 }
59 
60 
62 {
63  QMutexLocker locker( &mMutex );
64  mVectorJoins.push_back( joinInfo );
65 
66  // run depth-first search to detect cycles in the graph of joins between layers.
67  // any cycle would cause infinite recursion when updating fields
68  QHash<QgsVectorLayer *, int> markDFS;
69  if ( mLayer && _hasCycleDFS( mLayer, markDFS ) )
70  {
71  // we have to reject this one
72  mVectorJoins.pop_back();
73  return false;
74  }
75 
76  //cache joined layer to virtual memory if specified by user
77  if ( joinInfo.isUsingMemoryCache() )
78  {
79  cacheJoinLayer( mVectorJoins.last() );
80  }
81 
82  // Wait for notifications about changed fields in joined layer to propagate them.
83  // During project load the joined layers possibly do not exist yet so the connection will not be created,
84  // but then QgsProject makes sure to call createJoinCaches() which will do the connection.
85  // Unique connection makes sure we do not respond to one layer's update more times (in case of multiple join)
86  if ( QgsVectorLayer *vl = joinInfo.joinLayer() )
87  {
88  connectJoinedLayer( vl );
89  }
90 
91  emit joinedFieldsChanged();
92  return true;
93 }
94 
95 
96 bool QgsVectorLayerJoinBuffer::removeJoin( const QString &joinLayerId )
97 {
98  QMutexLocker locker( &mMutex );
99  bool res = false;
100  for ( int i = 0; i < mVectorJoins.size(); ++i )
101  {
102  if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
103  {
104  if ( QgsVectorLayer *vl = mVectorJoins.at( i ).joinLayer() )
105  {
106  disconnect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields );
107  }
108 
109  mVectorJoins.removeAt( i );
110  res = true;
111  }
112  }
113 
114  emit joinedFieldsChanged();
115  return res;
116 }
117 
118 void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorLayerJoinInfo &joinInfo )
119 {
120  //memory cache not required or already done
121  if ( !joinInfo.isUsingMemoryCache() || !joinInfo.cacheDirty )
122  {
123  return;
124  }
125 
126  QgsVectorLayer *cacheLayer = joinInfo.joinLayer();
127  if ( cacheLayer )
128  {
129  int joinFieldIndex = cacheLayer->fields().indexFromName( joinInfo.joinFieldName() );
130 
131  if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->fields().count() )
132  return;
133 
134  joinInfo.cachedAttributes.clear();
135 
136  QgsFeatureRequest request;
138 
139  // maybe user requested just a subset of layer's attributes
140  // so we do not have to cache everything
141  bool hasSubset = joinInfo.joinFieldNamesSubset();
142  QVector<int> subsetIndices;
143  if ( hasSubset )
144  {
145  subsetIndices = joinSubsetIndices( cacheLayer, *joinInfo.joinFieldNamesSubset() );
146 
147  // we need just subset of attributes - but make sure to include join field name
148  QgsAttributeList cacheLayerAttrs = subsetIndices.toList();
149  if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
150  cacheLayerAttrs.append( joinFieldIndex );
151  request.setSubsetOfAttributes( cacheLayerAttrs );
152  }
153 
154  QgsFeatureIterator fit = cacheLayer->getFeatures( request );
155  QgsFeature f;
156  while ( fit.nextFeature( f ) )
157  {
158  QgsAttributes attrs = f.attributes();
159  QString key = attrs.at( joinFieldIndex ).toString();
160  if ( hasSubset )
161  {
162  QgsAttributes subsetAttrs( subsetIndices.count() );
163  for ( int i = 0; i < subsetIndices.count(); ++i )
164  subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
165  joinInfo.cachedAttributes.insert( key, subsetAttrs );
166  }
167  else
168  {
169  QgsAttributes attrs2 = attrs;
170  attrs2.remove( joinFieldIndex ); // skip the join field to avoid double field names (fields often have the same name)
171  joinInfo.cachedAttributes.insert( key, attrs2 );
172  }
173  }
174  joinInfo.cacheDirty = false;
175  }
176 }
177 
178 
179 QVector<int> QgsVectorLayerJoinBuffer::joinSubsetIndices( QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset )
180 {
181  QVector<int> subsetIndices;
182  const QgsFields &fields = joinLayer->fields();
183  for ( int i = 0; i < joinFieldsSubset.count(); ++i )
184  {
185  QString joinedFieldName = joinFieldsSubset.at( i );
186  int index = fields.lookupField( joinedFieldName );
187  if ( index != -1 )
188  {
189  subsetIndices.append( index );
190  }
191  else
192  {
193  QgsDebugMsg( "Join layer subset field not found: " + joinedFieldName );
194  }
195  }
196 
197  return subsetIndices;
198 }
199 
201 {
202  QString prefix;
203 
204  QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
205  for ( int joinIdx = 0 ; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
206  {
207  QgsVectorLayer *joinLayer = joinIt->joinLayer();
208  if ( !joinLayer )
209  {
210  continue;
211  }
212 
213  const QgsFields &joinFields = joinLayer->fields();
214  QString joinFieldName = joinIt->joinFieldName();
215 
216  QSet<QString> subset;
217  bool hasSubset = false;
218  if ( joinIt->joinFieldNamesSubset() )
219  {
220  hasSubset = true;
221  subset = QSet<QString>::fromList( *joinIt->joinFieldNamesSubset() );
222  }
223 
224  if ( joinIt->prefix().isNull() )
225  {
226  prefix = joinLayer->name() + '_';
227  }
228  else
229  {
230  prefix = joinIt->prefix();
231  }
232 
233  for ( int idx = 0; idx < joinFields.count(); ++idx )
234  {
235  // if using just a subset of fields, filter some of them out
236  if ( hasSubset && !subset.contains( joinFields.at( idx ).name() ) )
237  continue;
238 
239  //skip the join field to avoid double field names (fields often have the same name)
240  // when using subset of field, use all the selected fields
241  if ( hasSubset || joinFields.at( idx ).name() != joinFieldName )
242  {
243  QgsField f = joinFields.at( idx );
244  f.setName( prefix + f.name() );
245  fields.append( f, QgsFields::OriginJoin, idx + ( joinIdx * 1000 ) );
246  }
247  }
248  }
249 }
250 
252 {
253  QMutexLocker locker( &mMutex );
254  QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
255  for ( ; joinIt != mVectorJoins.end(); ++joinIt )
256  {
257  if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
258  cacheJoinLayer( *joinIt );
259  }
260 }
261 
262 
263 void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &document ) const
264 {
265  QDomElement vectorJoinsElem = document.createElement( QStringLiteral( "vectorjoins" ) );
266  layer_node.appendChild( vectorJoinsElem );
267  QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
268  for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
269  {
270  QDomElement joinElem = document.createElement( QStringLiteral( "join" ) );
271 
272  joinElem.setAttribute( QStringLiteral( "targetFieldName" ), joinIt->targetFieldName() );
273 
274  joinElem.setAttribute( QStringLiteral( "joinLayerId" ), joinIt->joinLayerId() );
275  joinElem.setAttribute( QStringLiteral( "joinFieldName" ), joinIt->joinFieldName() );
276 
277  joinElem.setAttribute( QStringLiteral( "memoryCache" ), joinIt->isUsingMemoryCache() );
278  joinElem.setAttribute( QStringLiteral( "dynamicForm" ), joinIt->isDynamicFormEnabled() );
279 
280  if ( joinIt->joinFieldNamesSubset() )
281  {
282  QDomElement subsetElem = document.createElement( QStringLiteral( "joinFieldsSubset" ) );
283  Q_FOREACH ( const QString &fieldName, *joinIt->joinFieldNamesSubset() )
284  {
285  QDomElement fieldElem = document.createElement( QStringLiteral( "field" ) );
286  fieldElem.setAttribute( QStringLiteral( "name" ), fieldName );
287  subsetElem.appendChild( fieldElem );
288  }
289 
290  joinElem.appendChild( subsetElem );
291  }
292 
293  if ( !joinIt->prefix().isNull() )
294  {
295  joinElem.setAttribute( QStringLiteral( "customPrefix" ), joinIt->prefix() );
296  joinElem.setAttribute( QStringLiteral( "hasCustomPrefix" ), 1 );
297  }
298 
299  vectorJoinsElem.appendChild( joinElem );
300  }
301 }
302 
303 void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node )
304 {
305  mVectorJoins.clear();
306  QDomElement vectorJoinsElem = layer_node.firstChildElement( QStringLiteral( "vectorjoins" ) );
307  if ( !vectorJoinsElem.isNull() )
308  {
309  QDomNodeList joinList = vectorJoinsElem.elementsByTagName( QStringLiteral( "join" ) );
310  for ( int i = 0; i < joinList.size(); ++i )
311  {
312  QDomElement infoElem = joinList.at( i ).toElement();
314  info.setJoinFieldName( infoElem.attribute( QStringLiteral( "joinFieldName" ) ) );
315  // read layer ID - to turn it into layer object, caller will need to call resolveReferences() later
316  info.setJoinLayerId( infoElem.attribute( QStringLiteral( "joinLayerId" ) ) );
317  info.setTargetFieldName( infoElem.attribute( QStringLiteral( "targetFieldName" ) ) );
318  info.setUsingMemoryCache( infoElem.attribute( QStringLiteral( "memoryCache" ) ).toInt() );
319  info.setDynamicFormEnabled( infoElem.attribute( QStringLiteral( "dynamicForm" ) ).toInt() );
320 
321  QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( "joinFieldsSubset" ) );
322  if ( !subsetElem.isNull() )
323  {
324  QStringList *fieldNames = new QStringList;
325  QDomNodeList fieldNodes = infoElem.elementsByTagName( QStringLiteral( "field" ) );
326  fieldNames->reserve( fieldNodes.count() );
327  for ( int i = 0; i < fieldNodes.count(); ++i )
328  *fieldNames << fieldNodes.at( i ).toElement().attribute( QStringLiteral( "name" ) );
329  info.setJoinFieldNamesSubset( fieldNames );
330  }
331 
332  if ( infoElem.attribute( QStringLiteral( "hasCustomPrefix" ) ).toInt() )
333  info.setPrefix( infoElem.attribute( QStringLiteral( "customPrefix" ) ) );
334  else
335  info.setPrefix( QString() );
336 
337  addJoin( info );
338  }
339  }
340 }
341 
343 {
344  bool resolved = false;
345  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
346  {
347  if ( it->joinLayer() )
348  continue; // already resolved
349 
350  if ( QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->mapLayer( it->joinLayerId() ) ) )
351  {
352  it->setJoinLayer( joinedLayer );
353  connectJoinedLayer( joinedLayer );
354  resolved = true;
355  }
356  }
357 
358  if ( resolved )
359  emit joinedFieldsChanged();
360 }
361 
363 {
364  if ( !info )
365  return -1;
366 
367  int joinIndex = mVectorJoins.indexOf( *info );
368  if ( joinIndex == -1 )
369  return -1;
370 
371  for ( int i = 0; i < fields.count(); ++i )
372  {
373  if ( fields.fieldOrigin( i ) != QgsFields::OriginJoin )
374  continue;
375 
376  if ( fields.fieldOriginIndex( i ) / 1000 == joinIndex )
377  return i;
378  }
379  return -1;
380 }
381 
382 const QgsVectorLayerJoinInfo *QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, const QgsFields &fields, int &sourceFieldIndex ) const
383 {
384  if ( fields.fieldOrigin( index ) != QgsFields::OriginJoin )
385  return nullptr;
386 
387  int originIndex = fields.fieldOriginIndex( index );
388  int sourceJoinIndex = originIndex / 1000;
389  sourceFieldIndex = originIndex % 1000;
390 
391  if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
392  return nullptr;
393 
394  return &( mVectorJoins[sourceJoinIndex] );
395 }
396 
397 QList<const QgsVectorLayerJoinInfo *> QgsVectorLayerJoinBuffer::joinsWhereFieldIsId( const QgsField &field ) const
398 {
399  QList<const QgsVectorLayerJoinInfo *> infos;
400 
401  Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mVectorJoins )
402  {
403  if ( infos.contains( &info ) )
404  continue;
405 
406  if ( info.targetFieldName() == field.name() )
407  infos.append( &info );
408  }
409 
410  return infos;
411 }
412 
414 {
415  QgsFeature joinedFeature;
416 
417  if ( info->joinLayer() )
418  {
419  joinedFeature.setFields( info->joinLayer()->fields() );
420 
421  QString joinFieldName = info->joinFieldName();
422  const QVariant targetValue = feature.attribute( info->targetFieldName() );
423  QString filter = QgsExpression::createFieldEqualityExpression( joinFieldName, targetValue );
424 
425  QgsFeatureRequest request;
426  request.setFilterExpression( filter );
427  request.setLimit( 1 );
428 
429  QgsFeatureIterator it = info->joinLayer()->getFeatures( request );
430  it.nextFeature( joinedFeature );
431  }
432 
433  return joinedFeature;
434 }
435 
437 {
438  QgsVectorLayerJoinBuffer *cloned = new QgsVectorLayerJoinBuffer( mLayer );
439  cloned->mVectorJoins = mVectorJoins;
440  return cloned;
441 }
442 
443 void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
444 {
445  // TODO - check - this whole method is probably not needed anymore,
446  // since the cache handling is covered by joinedLayerModified()
447 
448  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
449  Q_ASSERT( joinedLayer );
450 
451  // recache the joined layer
452  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
453  {
454  if ( joinedLayer == it->joinLayer() )
455  {
456  it->cachedAttributes.clear();
457  cacheJoinLayer( *it );
458  }
459  }
460 
461  emit joinedFieldsChanged();
462 }
463 
464 void QgsVectorLayerJoinBuffer::joinedLayerModified()
465 {
466  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
467  Q_ASSERT( joinedLayer );
468 
469  // recache the joined layer
470  for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
471  {
472  if ( joinedLayer == it->joinLayer() )
473  {
474  it->cacheDirty = true;
475  }
476  }
477 }
478 
479 void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
480 {
481  QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
482  Q_ASSERT( joinedLayer );
483 
484  removeJoin( joinedLayer->id() );
485 }
486 
487 void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl )
488 {
489  connect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields, Qt::UniqueConnection );
490  connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection );
491  connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection );
492 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:289
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node.
Wrapper for iterator of features from vector data provider or vector layer.
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:49
FieldOrigin fieldOrigin(int fieldIdx) const
Get field&#39;s origin (value from an enumeration)
Definition: qgsfields.cpp:161
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:155
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
QString name
Definition: qgsfield.h:54
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
void readXml(const QDomNode &layer_node)
Reads joins from project file.
void setJoinFieldName(const QString &fieldName)
Sets name of the field of joined layer that will be used for join.
Container of fields for a vector layer.
Definition: qgsfields.h:41
void setName(const QString &name)
Set the field name.
Definition: qgsfield.cpp:135
void setDynamicFormEnabled(bool enabled)
Sets whether the form has to be dynamically updated with joined fields when a feature is being create...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
QgsVectorLayerJoinBuffer(QgsVectorLayer *layer=nullptr)
int count() const
Return number of items.
Definition: qgsfields.cpp:115
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching) ...
Manages joined fields for a vector layer.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:135
int fieldOriginIndex(int fieldIdx) const
Get field&#39;s origin index (its meaning is specific to each type of origin)
Definition: qgsfields.cpp:169
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
int indexFromName(const QString &fieldName) const
Get the field index from the field name.
Definition: qgsfields.cpp:174
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
QgsFields fields() const override
Returns the list of fields of this layer.
Defines left outer join from our vector layer to some other vector layer.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
bool removeJoin(const QString &joinLayerId)
Removes a vector layer join.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
Reads and writes project states.
Definition: qgsproject.h:79
void setUsingMemoryCache(bool enabled)
Sets whether values from the joined layer should be cached in memory to speed up lookups.
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:46
bool cacheDirty
True if the cached join attributes need to be updated.
void setTargetFieldName(const QString &fieldName)
Sets name of the field of our layer that will be used for join.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
void setPrefix(const QString &prefix)
Sets prefix of fields from the joined layer. If null, joined layer&#39;s name will be used...
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
void updateFields(QgsFields &fields)
Updates field map with joined attributes.
static QVector< int > joinSubsetIndices(QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset)
Return a vector of indices for use in join based on field names from the layer.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
QgsFeatureRequest & setLimit(long limit)
Set the maximum number of features to request.
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
bool isUsingMemoryCache() const
Returns whether values from the joined layer should be cached in memory to speed up lookups...
void joinedFieldsChanged()
Emitted whenever the list of joined fields changes (e.g.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QString name
Definition: qgsmaplayer.h:58
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed.
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
void layerModified()
This signal is emitted when modifications has been done on layer.
void setJoinFieldNamesSubset(QStringList *fieldNamesSubset)
Set subset of fields to be used from joined layer.
QStringList * joinFieldNamesSubset() const
Get subset of fields to be used from joined layer.
QgsAttributes attributes
Definition: qgsfeature.h:71
void resolveReferences(QgsProject *project)
Resolves layer IDs of joined layers using given project&#39;s available layers.
void setJoinLayerId(const QString &layerId)
Sets ID of the joined layer. It will need to be overwritten by setJoinLayer() to a reference to real ...
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Set flags that affect how features will be fetched.