Skip to content

Commit 531d63f

Browse files
committedNov 23, 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 (cherry-picked from 5e3d8fe)
1 parent e82f8a4 commit 531d63f

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
@@ -300,5 +300,16 @@ class CORE_EXPORT QgsVectorLayerCache : public QObject
300300
friend class QgsCachedFeatureIterator;
301301
friend class QgsCachedFeatureWriterIterator;
302302
friend class QgsCachedFeature;
303+
304+
/** Returns true if the cache contains all the features required for a specified request.
305+
* @param featureRequest feature request
306+
* @param it will be set to iterator for matching features
307+
* @returns true if cache can satisfy request
308+
* @note this method only checks for available features, not whether the cache
309+
* contains required attributes or geometry. For that, use checkInformationCovered()
310+
*/
311+
bool canUseCacheForRequest( const QgsFeatureRequest& featureRequest, QgsFeatureIterator& it );
312+
313+
friend class TestVectorLayerCache;
303314
};
304315
#endif // QgsVectorLayerCache_H

‎tests/src/core/testqgsvectorlayercache.cpp

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

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

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

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

0 commit comments

Comments
 (0)
Please sign in to comment.