From 0e418d18a4cd4ff2e380826c94be09883ee21b65 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Wed, 15 Jan 2014 22:33:45 +0100 Subject: [PATCH] Added no-cache infrastucture the the filter cache. During query parsing if a filter is encountered that extends from NoCacheFilter then the filter will not be given to the filter cache (also not wrapped in FilterCacheFilterWrapper). Also if a filter directly or indirectly wraps a NoCacheFilter then that filter will also not be cached. Relates to #4757 --- .../common/lucene/search/NoCacheFilter.java | 50 +++++++++++++++++++ .../index/query/QueryParseContext.java | 23 ++++++--- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/elasticsearch/common/lucene/search/NoCacheFilter.java b/src/main/java/org/elasticsearch/common/lucene/search/NoCacheFilter.java index 49d36854c76..6ec76d98ac9 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/NoCacheFilter.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/NoCacheFilter.java @@ -19,11 +19,61 @@ package org.elasticsearch.common.lucene.search; +import org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.Filter; +import org.apache.lucene.util.Bits; + +import java.io.IOException; /** * A marker interface for {@link org.apache.lucene.search.Filter} denoting the filter * as one that should not be cached, ever. */ public abstract class NoCacheFilter extends Filter { + + private static final class NoCacheFilterWrapper extends NoCacheFilter { + private final Filter delegate; + private NoCacheFilterWrapper(Filter delegate) { + this.delegate = delegate; + } + + @Override + public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException { + return delegate.getDocIdSet(context, acceptDocs); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof NoCacheFilterWrapper) { + return delegate.equals(((NoCacheFilterWrapper)obj).delegate); + } + return false; + } + + @Override + public String toString() { + + return "no_cache(" + delegate + ")"; + } + + } + + /** + * Wraps a filter in a NoCacheFilter or returns it if it already is a NoCacheFilter. + */ + public static Filter wrap(Filter filter) { + if (filter instanceof NoCacheFilter) { + return filter; + } + return new NoCacheFilterWrapper(filter); + } } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/index/query/QueryParseContext.java b/src/main/java/org/elasticsearch/index/query/QueryParseContext.java index 43eb4d86c2d..0a0579be658 100644 --- a/src/main/java/org/elasticsearch/index/query/QueryParseContext.java +++ b/src/main/java/org/elasticsearch/index/query/QueryParseContext.java @@ -30,6 +30,7 @@ import org.apache.lucene.search.similarities.Similarity; import org.elasticsearch.cache.recycler.CacheRecycler; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.lucene.search.NoCacheFilter; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.Index; import org.elasticsearch.index.analysis.AnalysisService; @@ -75,6 +76,8 @@ public class QueryParseContext { private final Index index; + private boolean propagateNoCache = false; + IndexQueryParserService indexQueryParser; private final Map namedFilters = Maps.newHashMap(); @@ -168,6 +171,9 @@ public class QueryParseContext { if (filter == null) { return null; } + if (this.propagateNoCache || filter instanceof NoCacheFilter) { + return filter; + } if (cacheKey != null) { filter = new CacheKeyFilter.Wrapper(filter, cacheKey); } @@ -251,7 +257,7 @@ public class QueryParseContext { if (filterParser == null) { throw new QueryParsingException(index, "No filter registered for [" + filterName + "]"); } - Filter result = filterParser.parse(this); + Filter result = executeFilterParser(filterParser); if (parser.currentToken() == XContentParser.Token.END_OBJECT || parser.currentToken() == XContentParser.Token.END_ARRAY) { // if we are at END_OBJECT, move to the next one... parser.nextToken(); @@ -264,12 +270,17 @@ public class QueryParseContext { if (filterParser == null) { throw new QueryParsingException(index, "No filter registered for [" + filterName + "]"); } + return executeFilterParser(filterParser); + } + + private Filter executeFilterParser(FilterParser filterParser) throws IOException { + final boolean propagateNoCache = this.propagateNoCache; // first safe the state that we need to restore + this.propagateNoCache = false; // parse the subfilter with caching, that's fine Filter result = filterParser.parse(this); - // don't move to the nextToken in this case... -// if (parser.currentToken() == XContentParser.Token.END_OBJECT || parser.currentToken() == XContentParser.Token.END_ARRAY) { -// // if we are at END_OBJECT, move to the next one... -// parser.nextToken(); -// } + // now make sure we set propagateNoCache to true if it is true already or if the result is + // an instance of NoCacheFilter or if we used to be true! all filters above will + // be not cached ie. wrappers of this filter! + this.propagateNoCache |= (result instanceof NoCacheFilter) || propagateNoCache; return result; }