private static Key buildKey(ShardSearchRequest request, SearchContext context) throws Exception { // TODO: for now, this will create different keys for different JSON order // TODO: tricky to get around this, need to parse and order all, which can be expensive return new Key( context.indexShard(), ((DirectoryReader) context.searcher().getIndexReader()).getVersion(), request.cacheKey()); }
/** Can the shard request be cached at all? */ public boolean canCache(ShardSearchRequest request, SearchContext context) { // TODO: for now, template is not supported, though we could use the generated bytes as the key if (hasLength(request.templateSource())) { return false; } // for now, only enable it for requests with no hits if (context.size() != 0) { return false; } // We cannot cache with DFS because results depend not only on the content of the index but also // on the overridden statistics. So if you ran two queries on the same index with different // stats // (because an other shard was updated) you would get wrong results because of the scores // (think about top_hits aggs or scripts using the score) if (!CACHEABLE_SEARCH_TYPES.contains(context.searchType())) { return false; } IndexMetaData index = clusterService.state().getMetaData().index(request.index()); if (index == null) { // in case we didn't yet have the cluster state, or it just got deleted return false; } // if not explicitly set in the request, use the index setting, if not, use the request if (request.requestCache() == null) { if (!isCacheEnabled(index.settings(), Boolean.FALSE)) { return false; } } else if (!request.requestCache()) { return false; } // if the reader is not a directory reader, we can't get the version from it if (!(context.searcher().getIndexReader() instanceof DirectoryReader)) { return false; } // if now in millis is used (or in the future, a more generic "isDeterministic" flag // then we can't cache based on "now" key within the search request, as it is not deterministic if (context.nowInMillisUsed()) { return false; } return true; }
private void parseTemplate(ShardSearchRequest request) { BytesReference processedQuery; if (request.template() != null) { ExecutableScript executable = this.scriptService.executable(request.template(), ScriptContext.Standard.SEARCH); processedQuery = (BytesReference) executable.run(); } else { if (!hasLength(request.templateSource())) { return; } XContentParser parser = null; Template template = null; try { parser = XContentFactory.xContent(request.templateSource()) .createParser(request.templateSource()); template = TemplateQueryParser.parse(parser, "params", "template"); if (template.getType() == ScriptService.ScriptType.INLINE) { // Try to double parse for nested template id/file parser = null; try { ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH); processedQuery = (BytesReference) executable.run(); parser = XContentFactory.xContent(processedQuery).createParser(processedQuery); } catch (ElasticsearchParseException epe) { // This was an non-nested template, the parse failure was due to this, it is safe to // assume this refers to a file // for backwards compatibility and keep going template = new Template( template.getScript(), ScriptService.ScriptType.FILE, MustacheScriptEngineService.NAME, null, template.getParams()); ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH); processedQuery = (BytesReference) executable.run(); } if (parser != null) { try { Template innerTemplate = TemplateQueryParser.parse(parser); if (hasLength(innerTemplate.getScript()) && !innerTemplate.getType().equals(ScriptService.ScriptType.INLINE)) { // An inner template referring to a filename or id template = new Template( innerTemplate.getScript(), innerTemplate.getType(), MustacheScriptEngineService.NAME, null, template.getParams()); ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH); processedQuery = (BytesReference) executable.run(); } } catch (ScriptParseException e) { // No inner template found, use original template from above } } } else { ExecutableScript executable = this.scriptService.executable(template, ScriptContext.Standard.SEARCH); processedQuery = (BytesReference) executable.run(); } } catch (IOException e) { throw new ElasticsearchParseException("Failed to parse template", e); } finally { Releasables.closeWhileHandlingException(parser); } if (!hasLength(template.getScript())) { throw new ElasticsearchParseException("Template must have [template] field configured"); } } request.source(processedQuery); }
final SearchContext createContext( ShardSearchRequest request, @Nullable Engine.Searcher searcher) { IndexService indexService = indicesService.indexServiceSafe(request.index()); IndexShard indexShard = indexService.shardSafe(request.shardId()); SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId()); Engine.Searcher engineSearcher = searcher == null ? indexShard.acquireSearcher("search") : searcher; SearchContext context = new DefaultSearchContext( idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter()); SearchContext.setCurrent(context); try { context.scroll(request.scroll()); parseTemplate(request); parseSource(context, request.source()); parseSource(context, request.extraSource()); // if the from and size are still not set, default them if (context.from() == -1) { context.from(0); } if (context.searchType() == SearchType.COUNT) { // so that the optimizations we apply to size=0 also apply to search_type=COUNT // and that we close contexts when done with the query phase context.searchType(SearchType.QUERY_THEN_FETCH); context.size(0); } else if (context.size() == -1) { context.size(10); } // pre process dfsPhase.preProcess(context); queryPhase.preProcess(context); fetchPhase.preProcess(context); // compute the context keep alive long keepAlive = defaultKeepAlive; if (request.scroll() != null && request.scroll().keepAlive() != null) { keepAlive = request.scroll().keepAlive().millis(); } context.keepAlive(keepAlive); } catch (Throwable e) { context.close(); throw ExceptionsHelper.convertToRuntime(e); } return context; }