Skip to content

Commit 5e3d8fe

Browse files
committedNov 15, 2016
Use cached feature iterator if cache has all needed features
Previously a cached feature iterator would only be returned if cache was either full or used a cache index On behalf of Faunalia, sponsored by ENEL
1 parent afd5d1e commit 5e3d8fe

File tree

3 files changed

+123
-9
lines changed

3 files changed

+123
-9
lines changed
 

‎src/core/qgsvectorlayercache.cpp

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,54 @@ void QgsVectorLayerCache::invalidate()
270270
emit invalidated();
271271
}
272272

273+
bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator& it )
274+
{
275+
// check first for available indices
276+
Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
277+
{
278+
if ( idx->getCacheIterator( it, featureRequest ) )
279+
{
280+
return true;
281+
}
282+
}
283+
284+
// no indexes available, but maybe we have already cached all required features anyway?
285+
switch ( featureRequest.filterType() )
286+
{
287+
case QgsFeatureRequest::FilterFid:
288+
{
289+
if ( mCache.contains( featureRequest.filterFid() ) )
290+
{
291+
it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
292+
return true;
293+
}
294+
break;
295+
}
296+
case QgsFeatureRequest::FilterFids:
297+
{
298+
if ( mCache.keys().toSet().contains( featureRequest.filterFids() ) )
299+
{
300+
it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
301+
return true;
302+
}
303+
break;
304+
}
305+
case QgsFeatureRequest::FilterNone:
306+
case QgsFeatureRequest::FilterRect:
307+
case QgsFeatureRequest::FilterExpression:
308+
{
309+
if ( mFullCache )
310+
{
311+
it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
312+
return true;
313+
}
314+
break;
315+
}
316+
317+
}
318+
return false;
319+
}
320+
273321
QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &featureRequest )
274322
{
275323
QgsFeatureIterator it;
@@ -285,15 +333,8 @@ QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &fe
285333
}
286334
else
287335
{
288-
// Check if an index is able to deliver the requested features
289-
Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
290-
{
291-
if ( idx->getCacheIterator( it, featureRequest ) )
292-
{
293-
requiresWriterIt = false;
294-
break;
295-
}
296-
}
336+
// may still be able to satisfy request using cache
337+
requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
297338
}
298339
}
299340
else

‎src/core/qgsvectorlayercache.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,5 +338,16 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject
338338
friend class QgsCachedFeatureIterator;
339339
friend class QgsCachedFeatureWriterIterator;
340340
friend class QgsCachedFeature;
341+
342+
/** Returns true if the cache contains all the features required for a specified request.
343+
* @param featureRequest feature request
344+
* @param it will be set to iterator for matching features
345+
* @returns true if cache can satisfy request
346+
* @note this method only checks for available features, not whether the cache
347+
* contains required attributes or geometry. For that, use checkInformationCovered()
348+
*/
349+
bool canUseCacheForRequest( const QgsFeatureRequest& featureRequest, QgsFeatureIterator& it );
350+
351+
friend class TestVectorLayerCache;
341352
};
342353
#endif // QgsVectorLayerCache_H

‎tests/src/core/testqgsvectorlayercache.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class TestVectorLayerCache : public QObject
5454
void testSubsetRequest();
5555
void testFullCache();
5656
void testFullCacheThroughRequest();
57+
void testCanUseCacheForRequest();
5758

5859
void onCommittedFeaturesAdded( const QString&, const QgsFeatureList& );
5960

@@ -267,6 +268,67 @@ void TestVectorLayerCache::testFullCacheThroughRequest()
267268
QVERIFY( cache.hasFullCache() );
268269
}
269270

271+
void TestVectorLayerCache::testCanUseCacheForRequest()
272+
{
273+
//first get some feature ids from layer
274+
QgsFeature f;
275+
QgsFeatureIterator it = mPointsLayer->getFeatures();
276+
it.nextFeature( f );
277+
QgsFeatureId id1 = f.id();
278+
it.nextFeature( f );
279+
QgsFeatureId id2 = f.id();
280+
281+
QgsVectorLayerCache cache( mPointsLayer, 10 );
282+
// initially nothing in cache, so can't use it to fulfill the request
283+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id1 ), it ) );
284+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id2 ), it ) );
285+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFids( QgsFeatureIds() << id1 << id2 ), it ) );
286+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterRect( QgsRectangle( 1, 2, 3, 4 ) ), it ) );
287+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterExpression( "$x<5" ), it ) );
288+
289+
// get just the first feature into the cache
290+
it = cache.getFeatures( QgsFeatureRequest().setFilterFid( id1 ) );
291+
while ( it.nextFeature( f ) ) { }
292+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id1 ), it ) );
293+
//verify that the returned iterator was correct
294+
QVERIFY( it.nextFeature( f ) );
295+
QCOMPARE( f.id(), id1 );
296+
QVERIFY( !it.nextFeature( f ) );
297+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id2 ), it ) );
298+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFids( QgsFeatureIds() << id1 << id2 ), it ) );
299+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterRect( QgsRectangle( 1, 2, 3, 4 ) ), it ) );
300+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterExpression( "$x<5" ), it ) );
301+
302+
// get feature 2 into cache
303+
it = cache.getFeatures( QgsFeatureRequest().setFilterFid( id2 ) );
304+
while ( it.nextFeature( f ) ) { }
305+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id1 ), it ) );
306+
QVERIFY( it.nextFeature( f ) );
307+
QCOMPARE( f.id(), id1 );
308+
QVERIFY( !it.nextFeature( f ) );
309+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id2 ), it ) );
310+
QVERIFY( it.nextFeature( f ) );
311+
QCOMPARE( f.id(), id2 );
312+
QVERIFY( !it.nextFeature( f ) );
313+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFids( QgsFeatureIds() << id1 << id2 ), it ) );
314+
QVERIFY( it.nextFeature( f ) );
315+
QgsFeatureIds result;
316+
result << f.id();
317+
QVERIFY( it.nextFeature( f ) );
318+
result << f.id();
319+
QCOMPARE( result, QgsFeatureIds() << id1 << id2 );
320+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterRect( QgsRectangle( 1, 2, 3, 4 ) ), it ) );
321+
QVERIFY( !cache.canUseCacheForRequest( QgsFeatureRequest().setFilterExpression( "$x<5" ), it ) );
322+
323+
// can only use rect/expression requests if cache has everything
324+
cache.setFullCache( true );
325+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id1 ), it ) );
326+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFid( id2 ), it ) );
327+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterFids( QgsFeatureIds() << id1 << id2 ), it ) );
328+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterRect( QgsRectangle( 1, 2, 3, 4 ) ), it ) );
329+
QVERIFY( cache.canUseCacheForRequest( QgsFeatureRequest().setFilterExpression( "$x<5" ), it ) );
330+
}
331+
270332
void TestVectorLayerCache::onCommittedFeaturesAdded( const QString& layerId, const QgsFeatureList& features )
271333
{
272334
Q_UNUSED( layerId )

0 commit comments

Comments
 (0)
Please sign in to comment.