-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 
Author Message
 Post subject: [Hibernate Search] CachingWrapperFilter does not cache
PostPosted: Fri Nov 09, 2007 11:26 am 
Newbie

Joined: Mon Oct 22, 2007 7:04 am
Posts: 19
I am having problems with the CachingWrapperFilter. As I understand it HibernateSearch should cache them automatically. A CachingWrapperFilter reapplied on a new query should not have to consult the bits method of the Filter it wraps... right?

Here is my small test showing that either my assumption above is wrong or the functionality broken:

Test program:
Code:
/**
* SearchTester is a playground for exploring Hibernate Search and Lucene.
*/
public class SearchTester {

    private void start(FullTextEntityManager fullTextEntityManager) throws ParseException {
        System.out.println("QUERY 1:");
        performQuery(fullTextEntityManager);
        System.out.println("QUERY 2:");
        performQuery(fullTextEntityManager);
    }

    private void performQuery(FullTextEntityManager fullTextEntityManager) {
        TermQuery p = new TermQuery(new Term("name", "foo"));
        FullTextQuery query = fullTextEntityManager.createFullTextQuery(p, Company.class);

        query.enableFullTextFilter("geoFilter")
                .setParameter("swLat", 51.0345)
                .setParameter("swLng", 12.3093)
                .setParameter("neLat", 85.2345)
                .setParameter("neLng", 29.3093);

        query.getResultList();
    }

    private static FullTextEntityManager fullTextEntityManager() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("standalone");
        return Search.createFullTextEntityManager(emf.createEntityManager());
    }

    public static void main(String[] args) throws ParseException {
        new SearchTester().start(fullTextEntityManager());
    }
}



Actual Filter:

Code:
public class GeoFilter {

    private final static PaddedDoubleBridge PADDED_DOUBLE_BRIDGE = new PaddedDoubleBridge();

    private double swLat;
    private double swLng;
    private double neLat;
    private double neLng;

    public void setSwLat(double swLat) {
        this.swLat = swLat;
    }

    public void setSwLng(double swLng) {
        this.swLng = swLng;
    }

    public void setNeLat(double neLat) {
        this.neLat = neLat;
    }

    public void setNeLng(double neLng) {
        this.neLng = neLng;
    }

    @Key
    public FilterKey getKey() {
        StandardFilterKey key = new StandardFilterKey();
        key.addParameter(swLat);
        key.addParameter(swLng);
        key.addParameter(neLat);
        key.addParameter(neLng);
        return key;
    }

    @Factory
    public Filter getFilter() {
       
        // Add some printouts before the bits method is called
        ChainedFilter filter = new ChainedFilter() {
            public BitSet bits(IndexReader reader) throws IOException {
                System.out.println("ChainedFilter.bits   (reader=" + reader + ")");
                return super.bits(reader);
            }
        };

        filter.addFilter(new RangeFilter("address.position.lat", pad(swLat), pad(neLat), false, false));
        filter.addFilter(new RangeFilter("address.position.lng", pad(swLng), pad(neLng), false, false));

        // Add some printouts before the bits method is called
        return new CachingWrapperFilter(filter) {
            public BitSet bits(IndexReader reader) throws IOException {
                System.out.println("CachingWrapperFilter.bits   (reader=" + reader + ")");
                return super.bits(reader);
            }
        };
    }

    private String pad(double d) {
        return PADDED_DOUBLE_BRIDGE.objectToString(d);
    }

}


Printout:
Code:
QUERY 1:
CachingWrapperFilter.bits   (reader=org.apache.lucene.index.MultiReader@8a52b6)
ChainedFilter.bits   (reader=org.apache.lucene.index.MultiReader@8a52b6)
QUERY 2:
CachingWrapperFilter.bits   (reader=org.apache.lucene.index.MultiReader@17b2b2)
ChainedFilter.bits   (reader=org.apache.lucene.index.MultiReader@17b2b2)


Expected printout:
Code:
QUERY 1:
CachingWrapperFilter.bits   (reader=org.apache.lucene.index.MultiReader@8a52b6)
ChainedFilter.bits   (reader=org.apache.lucene.index.MultiReader@8a52b6)
QUERY 2:
CachingWrapperFilter.bits   (reader=org.apache.lucene.index.MultiReader@17b2b2)



I suspect that the fact that I am getting different readers could be part of the problem since the reader might be part of the key to make a lookup in the cache. Why do I get that? Or is the error elsewhere?

Thankful for any feedback on this!

Tobias Hill


Top
 Profile  
 
 Post subject:
PostPosted: Fri Nov 09, 2007 7:13 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
yes readers are used in the cache algorithm to prevent returning stale data.

I believe you've hit a bug, it seems I wrap (properly reused and cached) readers into a MultiReader which by itself is new each time and thus defeat the cache purpose.

Can you by change run the debugger to confirm that? The readers you see should be multiReaders who contains the same underlying reader instances.

Open a JIRA issue

The easy solution for now is to rewrite the CachingWrapperfilter so that it uses the underlying (private) Reader[] of the MultiReader rather than the MultiReader instance itself as a cache key.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 13, 2007 6:46 am 
Newbie

Joined: Mon Oct 22, 2007 7:04 am
Posts: 19
Hi Emmanuel,

Yes indeed, it seems that are reusing the wrapped instance while the wrapper is new. I will post a JIRA-issue about this.

Thanks for you feedback, saved my day!
Tobias

Here is a temp fix which can be used instead of the ordinary CachingWrapperFilter for now. Works for us (confirming the observation above as well).

Code:

/**
* Wraps another filter's result and caches it.  The purpose is to allow
* filters to simply filter, and then wrap with this class to add caching.
*
* See http://forum.hibernate.org/viewtopic.php?p=2368933#2368933 for reason why
* this is needed. Should be removed and replaced by CachingWrapperFilter as soon
* as it is fixed.
*/
public class TempFixCachingWrapperFilter extends Filter {

    public final static Log log = LogFactory.getLog(TempFixCachingWrapperFilter.class);

    protected Filter filter;

    /**
     * A transient Filter cache.
     */
    protected final transient Map<Object, BitSet> cache = new WeakHashMap<Object, BitSet>();

    /**
     * @param filter Filter to cache results of
     */
    public TempFixCachingWrapperFilter(Filter filter) {
        this.filter = filter;
    }

    public BitSet bits(IndexReader reader) throws IOException {
       
        Object cacheKey = getCacheKey(reader);

        synchronized (cache) {  // check cache
            BitSet cached = cache.get(cacheKey);
            if (cached != null) {
                return cached;
            }
        }

        final BitSet bits = filter.bits(reader);

        synchronized (cache) {  // update cache
            cache.put(cacheKey, bits);
        }

        return bits;
    }

    // Just extract the private "subReaders" field if the reader is of type MultiReader
    private Object getCacheKey(IndexReader reader) {
        if (reader instanceof MultiReader) {
            MultiReader multiReader = (MultiReader) reader;
            try {
                Field field = multiReader.getClass().getDeclaredField("subReaders");
                field.setAccessible(true);
                IndexReader[] subReaders = (IndexReader[]) field.get(multiReader);
                return subReaders[0];
            } catch (Exception e) {
                // ignore
            }
        }

        return reader;
    }

    public String toString() {
        return "TempFixCachingWrapperFilter(" + filter + ")";
    }

    public boolean equals(Object o) {
        return o instanceof TempFixCachingWrapperFilter && filter.equals(((TempFixCachingWrapperFilter) o).filter);
    }

    public int hashCode() {
        return filter.hashCode() ^ 0x1117BF25;
    }
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.