Skip to content

Commit

Permalink
HSEARCH-1513 CachingWrapperFilter attempts to cache null docIdSets
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanne committed Mar 4, 2014
1 parent 05ea3d0 commit 27eae46
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,13 @@ private synchronized DocIdSet buildBitSet() throws IOException {
int size = andedDocIdSets.size();
DocIdSetIterator[] iterators = new DocIdSetIterator[size];
for ( int i = 0; i < size; i++ ) {
DocIdSet docIdSet = andedDocIdSets.get( i );
if ( docIdSet == null ) {
// Since Lucene 4 even the docIdSet could be returned at null to signify an empty match
return EMPTY_DOCIDSET;
}
// build all iterators
DocIdSetIterator docIdSetIterator = andedDocIdSets.get( i ).iterator();
DocIdSetIterator docIdSetIterator = docIdSet.iterator();
if ( docIdSetIterator == null ) {
// the Lucene API permits to return null on any iterator for empty matches
return EMPTY_DOCIDSET;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@
* the filter <code>BitSet</code>.
*
* @author Hardy Ferentschik
* @author Sanne Grinovero
* @see org.apache.lucene.search.CachingWrapperFilter
* @see <a href="http://opensource.atlassian.com/projects/hibernate/browse/HSEARCH-174">HSEARCH-174</a>
* @see <a href="https://hibernate.atlassian.net/browse/HSEARCH-174">HSEARCH-174</a>
*/
@SuppressWarnings("serial")
public class CachingWrapperFilter extends Filter {
Expand All @@ -50,6 +51,13 @@ public class CachingWrapperFilter extends Filter {

public static final int DEFAULT_SIZE = 5;

/**
* Any Filter could return null as a value representing an empty match set,
* we need to use NULL_OBJECT as a marker token to be able to cache this
* return value.
*/
private static final Object NULL_OBJECT = new Object();

/**
* The cache using soft references in order to store the filter bit sets.
*/
Expand Down Expand Up @@ -86,18 +94,34 @@ public CachingWrapperFilter(Filter filter, int size) {
@Override
public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
final AtomicReader reader = context.reader();
DocIdSet cached = (DocIdSet) cache.get( reader );
Object cached = cache.get( reader );
if ( cached != null ) {
return cached;
if ( cached == NULL_OBJECT ) {
return null;
}
else {
return (DocIdSet) cached;
}
}
synchronized ( cache ) {
cached = (DocIdSet) cache.get( reader );
cached = cache.get( reader );
if ( cached != null ) {
return cached;
if ( cached == NULL_OBJECT ) {
return null;
}
else {
return (DocIdSet) cached;
}
}
final DocIdSet docIdSet = filter.getDocIdSet( context, acceptDocs );
cache.put( reader, docIdSet );
return docIdSet;
if ( docIdSet == null ) {
cache.put( reader, NULL_OBJECT );
return null;
}
else {
cache.put( reader, docIdSet );
return docIdSet;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@
cache = FilterCacheModeType.INSTANCE_ONLY),
@FullTextFilterDef(name = "empty",
impl = NullReturningEmptyFilter.class,
cache = FilterCacheModeType.INSTANCE_ONLY)
cache = FilterCacheModeType.INSTANCE_ONLY),
@FullTextFilterDef(name = "cached_empty",
impl = NullReturningEmptyFilter.class,
cache = FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS)
})
public class Driver {
@Id
Expand Down
15 changes: 15 additions & 0 deletions orm/src/test/java/org/hibernate/search/test/filter/FilterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.hibernate.search.Search;
import org.hibernate.search.SearchException;
import org.hibernate.search.test.SearchTestCase;
import org.hibernate.search.test.util.TestForIssue;

/**
* @author Emmanuel Bernard
Expand Down Expand Up @@ -127,6 +128,20 @@ public void testStraightFilters() {
assertEquals( "Should not filter anymore", 3, ftQuery.getResultSize() );
}

@TestForIssue(jiraKey = "HSEARCH-1513")
public void testCachedEmptyFilters() {
FullTextQuery ftQuery = fullTextSession.createFullTextQuery( query, Driver.class );
ftQuery.enableFullTextFilter( "bestDriver" );
Filter dateFilter = TermRangeFilter.newStringRange( "delivery", "2001", "2005", true, true );
ftQuery.setFilter( dateFilter );
assertEquals( "Should select only liz", 1, ftQuery.getResultSize() );

ftQuery = fullTextSession.createFullTextQuery( query, Driver.class );
ftQuery.enableFullTextFilter( "bestDriver" );
ftQuery.enableFullTextFilter( "cached_empty" );
assertEquals( "two filters, one is empty, should not match anything", 0, ftQuery.getResultSize() );
}

public void testMultipleFiltersOfSameTypeWithDifferentParameters() {
FullTextQuery ftQuery = fullTextSession.createFullTextQuery( query, Driver.class );
ftQuery.enableFullTextFilter( "fieldConstraintFilter-1" )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,20 @@

import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Filter;
import org.apache.lucene.util.Bits;

/**
* Apparently it's legal for Lucene filters to return null
* on {@link DocIdSet#iterator()} : make sure we can deal with it as well.
* on {@link Filter#getDocIdSet} : make sure we can deal with it as well.
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2011 Red Hat Inc.
*/
public class NullReturningEmptyFilter extends Filter implements Serializable {

public static final DocIdSet EMPTY_DOCIDSET = new DocIdSet() {

@Override
public DocIdSetIterator iterator() {
return null;
}

@Override
public boolean isCacheable() {
return true;
}
};

@Override
public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException {
return EMPTY_DOCIDSET;
return null;
}

}

0 comments on commit 27eae46

Please sign in to comment.