net.conquiris.lucene.GuavaCachingFilter.java Source code

Java tutorial

Introduction

Here is the source code for net.conquiris.lucene.GuavaCachingFilter.java

Source

/*
 * Copyright (C) the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.conquiris.lucene;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.CachingWrapperFilter.DeletesMode;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Filter;
import org.apache.lucene.util.FixedBitSet;

import com.google.common.base.Preconditions;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheStats;
import com.google.common.util.concurrent.UncheckedExecutionException;

/**
 * A wrapping filter based on Guava {@link Cache}. Uses a caching mode equivalent to Lucene's
 * {@link DeletesMode.RECACHE}.
 * @author Andres Rodriguez
 */
public final class GuavaCachingFilter extends Filter {
    /** Serial UID. */
    private static final long serialVersionUID = -3469818482930960618L;

    /** Wrapped filter. */
    private final Filter filter;
    /** Cache. */
    private final Cache<Object, DocIdSet> cache;

    /**
     * Constructor.
     * @param filter Filter to cache results of.
     * @param size the maximum size of the cache.
     * @param duration the length of time after an entry is last accessed that it should be
     *          automatically removed.
     * @param unit the unit that {@code duration} is expressed in
     * @throws IllegalArgumentException if {@code size} is negative
     * @throws IllegalArgumentException if {@code duration} is negative
     */
    public GuavaCachingFilter(Filter filter, int size, long duration, TimeUnit unit) {
        this.filter = Preconditions.checkNotNull(filter, "The filter to cache must be provided");
        this.cache = CacheBuilder.newBuilder().softValues().maximumSize(size).expireAfterAccess(duration, unit)
                .build();
    }

    /**
     * Constructor with a maximum size of 100 elements and a expiration time of 10 minutes.
     * @param filter Filter to cache results of
     */
    public GuavaCachingFilter(Filter filter) {
        this(filter, 100, 10, TimeUnit.MINUTES);
    }

    @Override
    public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
        final Object key = reader.hasDeletions() ? reader.getDeletesCacheKey() : reader.getCoreCacheKey();
        try {
            return cache.get(key, new Loader(reader));
        } catch (Throwable t) {
            Throwable cause = t.getCause();
            if (cause instanceof IOException) {
                throw (IOException) cause;
            }
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            }
            if (cause instanceof Error) {
                throw (Error) cause;
            }
            if (t instanceof UncheckedExecutionException) {
                throw (UncheckedExecutionException) t;
            }
            throw new UncheckedExecutionException(cause);
        }
    }

    /** Returns a current snapshot of this cache's cumulative statistics. */
    public CacheStats stats() {
        return cache.stats();
    }

    @Override
    public String toString() {
        return "GuavaCachingFilter(" + filter + ")";
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof GuavaCachingFilter))
            return false;
        return this.filter.equals(((GuavaCachingFilter) o).filter);
    }

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

    /** Cache loader. */
    private final class Loader implements Callable<DocIdSet> {
        private final IndexReader reader;

        Loader(IndexReader reader) {
            this.reader = checkNotNull(reader, "The index reader must be provided");
        }

        public DocIdSet call() throws Exception {
            return docIdSetToCache(filter.getDocIdSet(reader));
        }

        /**
         * Provide the DocIdSet to be cached, using the DocIdSet provided by the wrapped Filter.
         * <p>
         * This implementation returns the given {@link DocIdSet}, if {@link DocIdSet#isCacheable}
         * returns <code>true</code>, else it copies the {@link DocIdSetIterator} into an
         * {@link FixedBitSet}.
         */
        private DocIdSet docIdSetToCache(DocIdSet docIdSet) throws IOException {
            if (docIdSet == null) {
                // this is better than returning null, as the nonnull result can be cached
                return DocIdSet.EMPTY_DOCIDSET;
            } else if (docIdSet.isCacheable()) {
                return docIdSet;
            } else {
                final DocIdSetIterator it = docIdSet.iterator();
                // null is allowed to be returned by iterator(),
                // in this case we wrap with the empty set,
                // which is cacheable.
                if (it == null) {
                    return DocIdSet.EMPTY_DOCIDSET;
                } else {
                    final FixedBitSet bits = new FixedBitSet(reader.maxDoc());
                    bits.or(it);
                    return bits;
                }
            }
        }
    }
}