/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.search.internal; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.lucene.search.Collector; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.apache.lucene.util.Counter; import org.elassandra.search.SearchProcessor; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.cache.recycler.PageCacheRecycler; import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.common.DelegatingHasContextAndHeaders; import org.elasticsearch.common.HasContextAndHeaders; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.concurrent.RefCounted; import org.elasticsearch.index.analysis.AnalysisService; import org.elasticsearch.index.cache.bitset.BitsetFilterCache; import org.elasticsearch.index.fielddata.IndexFieldDataService; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.object.ObjectMapper; import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.query.ParsedQuery; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.aggregations.SearchContextAggregations; import org.elasticsearch.search.dfs.DfsSearchResult; import org.elasticsearch.search.fetch.FetchSearchResult; import org.elasticsearch.search.fetch.FetchSubPhase; import org.elasticsearch.search.fetch.FetchSubPhaseContext; import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; import org.elasticsearch.search.fetch.script.ScriptFieldsContext; import org.elasticsearch.search.fetch.source.FetchSourceContext; import org.elasticsearch.search.highlight.SearchContextHighlight; import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.profile.Profilers; import org.elasticsearch.search.query.QuerySearchResult; import org.elasticsearch.search.rescore.RescoreSearchContext; import org.elasticsearch.search.scan.ScanContext; import org.elasticsearch.search.suggest.SuggestionSearchContext; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; /** * This class encapsulates the state needed to execute a search. It holds a reference to the * shards point in time snapshot (IndexReader / ContextIndexSearcher) and allows passing on * state from one query / fetch phase to another. * * This class also implements {@link RefCounted} since in some situations like in {@link org.elasticsearch.search.SearchService} * a SearchContext can be closed concurrently due to independent events ie. when an index gets removed. To prevent accessing closed * IndexReader / IndexSearcher instances the SearchContext can be guarded by a reference count and fail if it's been closed by * an external event. */ // For reference why we use RefCounted here see #20095 public abstract class SearchContext extends DelegatingHasContextAndHeaders implements Releasable, RefCounted { private static ThreadLocal<SearchContext> current = new ThreadLocal<>(); public final static int DEFAULT_TERMINATE_AFTER = 0; public static void setCurrent(SearchContext value) { current.set(value); QueryParseContext.setTypes(value.types()); } public static void removeCurrent() { current.remove(); QueryParseContext.removeTypes(); } public static SearchContext current() { return current.get(); } private Multimap<Lifetime, Releasable> clearables = null; private final AtomicBoolean closed = new AtomicBoolean(false); private InnerHitsContext innerHitsContext; protected final ParseFieldMatcher parseFieldMatcher; protected SearchProcessor processor = null; protected SearchContext(ParseFieldMatcher parseFieldMatcher, HasContextAndHeaders contextHeaders) { super(contextHeaders); this.parseFieldMatcher = parseFieldMatcher; } public ParseFieldMatcher parseFieldMatcher() { return parseFieldMatcher; } @Override public final void close() { if (closed.compareAndSet(false, true)) { // prevent double closing decRef(); } } private boolean nowInMillisUsed; protected abstract void doClose(); /** * Should be called before executing the main query and after all other parameters have been set. */ public abstract void preProcess(); public void processors(SearchProcessor processors) { this.processor = processors; } public SearchProcessor processor() { return processor; } public abstract Query searchFilter(String[] types); public abstract SearchProcessor searchProcessor(); public abstract long id(); public abstract String source(); public abstract ShardSearchRequest request(); public abstract SearchType searchType(); public abstract SearchContext searchType(SearchType searchType); public abstract SearchShardTarget shardTarget(); public abstract int numberOfShards(); public abstract boolean hasTypes(); public abstract String[] types(); public abstract float queryBoost(); public abstract SearchContext queryBoost(float queryBoost); public abstract long getOriginNanoTime(); public final long nowInMillis() { nowInMillisUsed = true; return nowInMillisImpl(); } public final boolean nowInMillisUsed() { return nowInMillisUsed; } protected abstract long nowInMillisImpl(); public abstract ScrollContext scrollContext(); public abstract SearchContext scrollContext(ScrollContext scroll); public abstract SearchContextAggregations aggregations(); public abstract SearchContext aggregations(SearchContextAggregations aggregations); public abstract <SubPhaseContext extends FetchSubPhaseContext> SubPhaseContext getFetchSubPhaseContext(FetchSubPhase.ContextFactory<SubPhaseContext> contextFactory); public abstract SearchContextHighlight highlight(); public abstract void highlight(SearchContextHighlight highlight); public InnerHitsContext innerHits() { if (innerHitsContext == null) { innerHitsContext = new InnerHitsContext(); } return innerHitsContext; } public abstract SuggestionSearchContext suggest(); public abstract void suggest(SuggestionSearchContext suggest); /** * @return list of all rescore contexts. empty if there aren't any. */ public abstract List<RescoreSearchContext> rescore(); public abstract void addRescore(RescoreSearchContext rescore); public abstract boolean hasScriptFields(); public abstract ScriptFieldsContext scriptFields(); /** * A shortcut function to see whether there is a fetchSourceContext and it says the source is requested. */ public abstract boolean sourceRequested(); public abstract boolean hasFetchSourceContext(); public abstract FetchSourceContext fetchSourceContext(); public abstract SearchContext fetchSourceContext(FetchSourceContext fetchSourceContext); public abstract ContextIndexSearcher searcher(); public abstract IndexShard indexShard(); public abstract MapperService mapperService(); public abstract AnalysisService analysisService(); public abstract ClusterService clusterService(); public abstract IndexQueryParserService queryParserService(); public abstract SimilarityService similarityService(); public abstract ScriptService scriptService(); public abstract PageCacheRecycler pageCacheRecycler(); public abstract BigArrays bigArrays(); public abstract BitsetFilterCache bitsetFilterCache(); public abstract IndexFieldDataService fieldData(); public abstract long timeoutInMillis(); public abstract void timeoutInMillis(long timeoutInMillis); public abstract int terminateAfter(); public abstract void terminateAfter(int terminateAfter); public abstract SearchContext minimumScore(float minimumScore); public abstract Float minimumScore(); public abstract SearchContext sort(Sort sort); public abstract Sort sort(); public abstract SearchContext trackScores(boolean trackScores); public abstract boolean trackScores(); public abstract SearchContext parsedPostFilter(ParsedQuery postFilter); public abstract ParsedQuery parsedPostFilter(); public abstract Query aliasFilter(); public abstract SearchContext parsedQuery(ParsedQuery query); public abstract ParsedQuery parsedQuery(); /** * The query to execute, might be rewritten. */ public abstract Query query(); public abstract int from(); public abstract SearchContext from(int from); public abstract int size(); public abstract SearchContext size(int size); public abstract boolean hasFieldNames(); public abstract List<String> fieldNames(); public abstract void emptyFieldNames(); public abstract boolean explain(); public abstract void explain(boolean explain); @Nullable public abstract List<String> groupStats(); public abstract void groupStats(List<String> groupStats); public abstract boolean version(); public abstract void version(boolean version); public abstract int[] docIdsToLoad(); public abstract int docIdsToLoadFrom(); public abstract int docIdsToLoadSize(); public abstract SearchContext docIdsToLoad(int[] docIdsToLoad, int docsIdsToLoadFrom, int docsIdsToLoadSize); public abstract void accessed(long accessTime); public abstract long lastAccessTime(); public abstract long keepAlive(); public abstract void keepAlive(long keepAlive); public abstract SearchLookup lookup(); public abstract DfsSearchResult dfsResult(); public abstract QuerySearchResult queryResult(); public abstract FetchSearchResult fetchResult(); /** * Return a handle over the profilers for the current search request, or {@code null} if profiling is not enabled. */ public abstract Profilers getProfilers(); public abstract boolean includeNode(); public abstract void includeNode(boolean includeNode); public abstract ClusterState getClusterState(); /** * Schedule the release of a resource. The time when {@link Releasable#close()} will be called on this object * is function of the provided {@link Lifetime}. */ public void addReleasable(Releasable releasable, Lifetime lifetime) { if (clearables == null) { clearables = MultimapBuilder.enumKeys(Lifetime.class).arrayListValues().build(); } clearables.put(lifetime, releasable); } public void clearReleasables(Lifetime lifetime) { if (clearables != null) { List<Collection<Releasable>> releasables = new ArrayList<>(); for (Lifetime lc : Lifetime.values()) { if (lc.compareTo(lifetime) > 0) { break; } releasables.add(clearables.removeAll(lc)); } Releasables.close(Iterables.concat(releasables)); } } public abstract ScanContext scanContext(); public abstract MappedFieldType smartNameFieldType(String name); /** * Looks up the given field, but does not restrict to fields in the types set on this context. */ public abstract MappedFieldType smartNameFieldTypeFromAnyType(String name); public abstract ObjectMapper getObjectMapper(String name); public abstract Counter timeEstimateCounter(); /** Return a view of the additional query collectors that should be run for this context. */ public abstract Map<Class<?>, Collector> queryCollectors(); /** * The life time of an object that is used during search execution. */ public enum Lifetime { /** * This life time is for objects that only live during collection time. */ COLLECTION, /** * This life time is for objects that need to live until the end of the current search phase. */ PHASE, /** * This life time is for objects that need to live until the search context they are attached to is destroyed. */ CONTEXT } // copied from AbstractRefCounted since this class subclasses already DelegatingHasContextAndHeaders // 5.x doesn't have this problem private final AtomicInteger refCount = new AtomicInteger(1); @Override public final void incRef() { if (tryIncRef() == false) { alreadyClosed(); } } @Override public final boolean tryIncRef() { do { int i = refCount.get(); if (i > 0) { if (refCount.compareAndSet(i, i + 1)) { return true; } } else { return false; } } while (true); } @Override public final void decRef() { int i = refCount.decrementAndGet(); assert i >= 0; if (i == 0) { try { clearReleasables(Lifetime.CONTEXT); } finally { doClose(); } } } protected void alreadyClosed() { throw new IllegalStateException("search context is already closed can't increment refCount current count [" + refCount() + "]"); } /** * Returns the current reference count. */ public int refCount() { return this.refCount.get(); } // end copy from AbstractRefCounted }