move parsing of search ext sections to the coordinating node

This commit is contained in:
javanna 2016-09-09 15:28:12 +02:00 committed by Luca Cavanna
parent 65c7f61ad9
commit 90ab460fcc
28 changed files with 309 additions and 216 deletions

View File

@ -989,7 +989,6 @@
<suppress files="modules[/\\]percolator[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]percolator[/\\]MultiPercolateRequestBuilder.java" checks="LineLength" />
<suppress files="modules[/\\]percolator[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]percolator[/\\]PercolateShardResponse.java" checks="LineLength" />
<suppress files="modules[/\\]percolator[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]percolator[/\\]TransportMultiPercolateAction.java" checks="LineLength" />
<suppress files="modules[/\\]percolator[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]percolator[/\\]TransportPercolateAction.java" checks="LineLength" />
<suppress files="modules[/\\]percolator[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]percolator[/\\]TransportShardMultiPercolateAction.java" checks="LineLength" />
<suppress files="modules[/\\]percolator[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]percolator[/\\]MultiPercolatorIT.java" checks="LineLength" />
<suppress files="modules[/\\]percolator[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]percolator[/\\]PercolatorIT.java" checks="LineLength" />

View File

@ -25,10 +25,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.elasticsearch.plugins.Plugin;
/**
* A registry for {@link org.elasticsearch.common.io.stream.Writeable.Reader} readers of {@link NamedWriteable}.
@ -47,7 +43,7 @@ public class NamedWriteableRegistry {
/** A name for the writeable which is unique to the {@link #categoryClass}. */
public final String name;
/** A reader captability of reading*/
/** A reader capability of reading*/
public final Writeable.Reader<?> reader;
/** Creates a new entry which can be stored by the registry. */

View File

@ -871,6 +871,16 @@ public abstract class StreamOutput extends OutputStream {
}
}
/**
* Writes a list of strings
*/
public void writeStringList(List<String> list) throws IOException {
writeVInt(list.size());
for (String string: list) {
this.writeString(string);
}
}
/**
* Writes a list of {@link NamedWriteable} objects.
*/

View File

@ -30,6 +30,7 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryParser;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.SearchExtParser;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
@ -86,7 +87,7 @@ public interface SearchPlugin {
/**
* The new {@link SearchExtParser}s defined by this plugin.
*/
default List<SearchExtParser> getSearchExtParsers() {
default List<SearchExtSpec<?>> getSearchExts() {
return emptyList();
}
/**
@ -167,7 +168,7 @@ public interface SearchPlugin {
/**
* Specification for an {@link Aggregation}.
*/
public static class AggregationSpec extends SearchExtensionSpec<AggregationBuilder, Aggregator.Parser> {
class AggregationSpec extends SearchExtensionSpec<AggregationBuilder, Aggregator.Parser> {
private final Map<String, Writeable.Reader<? extends InternalAggregation>> resultReaders = new TreeMap<>();
/**
@ -224,7 +225,7 @@ public interface SearchPlugin {
/**
* Specification for a {@link PipelineAggregator}.
*/
public static class PipelineAggregationSpec extends SearchExtensionSpec<PipelineAggregationBuilder, PipelineAggregator.Parser> {
class PipelineAggregationSpec extends SearchExtensionSpec<PipelineAggregationBuilder, PipelineAggregator.Parser> {
private final Map<String, Writeable.Reader<? extends InternalAggregation>> resultReaders = new TreeMap<>();
private final Writeable.Reader<? extends PipelineAggregator> aggregatorReader;
@ -297,6 +298,19 @@ public interface SearchPlugin {
}
}
/**
* Specification for a {@link SearchExtBuilder} which represents an additional section that can be
* parsed in a search request (within the ext element).
*/
class SearchExtSpec<T extends SearchExtBuilder> extends SearchExtensionSpec<T, SearchExtParser<T>> {
public SearchExtSpec(ParseField name, Writeable.Reader<? extends T> reader, SearchExtParser<T> parser) {
super(name, reader, parser);
}
public SearchExtSpec(String name, Writeable.Reader<? extends T> reader, SearchExtParser<T> parser) {
super(name, reader, parser);
}
}
/**
* Specification of search time behavior extension like a custom {@link MovAvgModel} or {@link ScoreFunction}.

View File

@ -19,10 +19,6 @@
package org.elasticsearch.rest.action.search;
import java.io.IOException;
import java.util.Map;
import java.util.function.BiConsumer;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.MultiSearchRequest;
import org.elasticsearch.action.search.SearchRequest;
@ -46,6 +42,10 @@ import org.elasticsearch.rest.action.RestToXContentListener;
import org.elasticsearch.search.SearchRequestParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import java.io.IOException;
import java.util.Map;
import java.util.function.BiConsumer;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.lenientNodeBooleanValue;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringArrayValue;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringValue;
@ -97,7 +97,7 @@ public class RestMultiSearchAction extends BaseRestHandler {
final QueryParseContext queryParseContext = new QueryParseContext(searchRequestParsers.queryParsers,
requestParser, parseFieldMatcher);
searchRequest.source(SearchSourceBuilder.fromXContent(queryParseContext,
searchRequestParsers.aggParsers, searchRequestParsers.suggesters));
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers));
multiRequest.add(searchRequest);
} catch (IOException e) {
throw new ElasticsearchParseException("Exception when parsing search request", e);

View File

@ -102,7 +102,8 @@ public class RestSearchAction extends BaseRestHandler {
if (restContent != null) {
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
QueryParseContext context = new QueryParseContext(searchRequestParsers.queryParsers, parser, parseFieldMatcher);
searchRequest.source().parseXContent(context, searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequest.source().parseXContent(context, searchRequestParsers.aggParsers, searchRequestParsers.suggesters,
searchRequestParsers.searchExtParsers);
}
}

View File

@ -0,0 +1,48 @@
/*
* 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;
import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.plugins.SearchPlugin;
/**
* Intermediate serializable representation of a search ext section. To be subclassed by plugins that support
* a custom section as part of a search request, which will be provided within the ext element.
* Any state needs to be serialized as part of the {@link org.elasticsearch.common.io.stream.Writeable#writeTo(StreamOutput)} method and
* read from the incoming stream, usually done adding a constructor that takes {@link org.elasticsearch.common.io.stream.StreamInput} as
* an argument.
*
* Registration happens through {@link SearchPlugin#getSearchExts()}, which also needs a {@link SearchExtParser} that's able to parse
* the incoming request from the REST layer into the proper {@link SearchExtBuilder} subclass.
*
* {@link #getWriteableName()} must return the same name as the one used for the registration
* of the {@link org.elasticsearch.plugins.SearchPlugin.SearchExtSpec}.
*
* @see SearchExtParser
* @see org.elasticsearch.plugins.SearchPlugin.SearchExtSpec
*/
public abstract class SearchExtBuilder implements NamedWriteable, ToXContent {
public abstract int hashCode();
public abstract boolean equals(Object obj);
}

View File

@ -21,18 +21,23 @@ package org.elasticsearch.search;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
/**
* Parser for the ext section of a search request, which can hold custom fetch sub phases config
* Defines a parser that is able to parse {@link org.elasticsearch.search.SearchExtBuilder}s
* from {@link org.elasticsearch.common.xcontent.XContent}.
*
* Registration happens through {@link org.elasticsearch.plugins.SearchPlugin#getSearchExts()}, which also needs a {@link SearchExtBuilder}
* implementation which is the object that this parser returns when reading an incoming request form the REST layer.
*
* @see SearchExtBuilder
* @see org.elasticsearch.plugins.SearchPlugin.SearchExtSpec
*/
public interface SearchExtParser {
@FunctionalInterface
public interface SearchExtParser<T extends SearchExtBuilder> {
/**
* Returns the name of the element that this parser is able to parse
* Parses the supported element placed within the ext section of a search request
*/
String getName();
/**
* Parses the element whose name is returned by {@link #getName()}
*/
Object parse(XContentParser parser) throws Exception;
T fromXContent(XContentParser parser) throws IOException;
}

View File

@ -24,9 +24,9 @@ import org.elasticsearch.common.xcontent.ParseFieldRegistry;
/**
* Extensions to ParseFieldRegistry to make Guice happy.
*/
public class SearchExtParserRegistry extends ParseFieldRegistry<SearchExtParser> {
public class SearchExtRegistry extends ParseFieldRegistry<SearchExtParser> {
SearchExtParserRegistry() {
public SearchExtRegistry() {
super("ext");
}
}

View File

@ -93,6 +93,7 @@ import org.elasticsearch.plugins.SearchPlugin.FetchPhaseConstructionContext;
import org.elasticsearch.plugins.SearchPlugin.PipelineAggregationSpec;
import org.elasticsearch.plugins.SearchPlugin.QuerySpec;
import org.elasticsearch.plugins.SearchPlugin.ScoreFunctionSpec;
import org.elasticsearch.plugins.SearchPlugin.SearchExtSpec;
import org.elasticsearch.plugins.SearchPlugin.SearchExtensionSpec;
import org.elasticsearch.search.action.SearchTransportService;
import org.elasticsearch.search.aggregations.AggregationBuilder;
@ -306,7 +307,7 @@ public class SearchModule extends AbstractModule {
"moving_avg_model");
private final List<FetchSubPhase> fetchSubPhases = new ArrayList<>();
private final SearchExtParserRegistry searchExtParserRegistry = new SearchExtParserRegistry();
private final SearchExtRegistry searchExtParserRegistry = new SearchExtRegistry();
private final Settings settings;
private final List<Entry> namedWriteables = new ArrayList<>();
@ -327,9 +328,9 @@ public class SearchModule extends AbstractModule {
registerAggregations(plugins);
registerPipelineAggregations(plugins);
registerFetchSubPhases(plugins);
registerSearchExtParsers(plugins);
registerSearchExts(plugins);
registerShapes();
searchRequestParsers = new SearchRequestParsers(queryParserRegistry, aggregatorParsers, getSuggesters());
searchRequestParsers = new SearchRequestParsers(queryParserRegistry, aggregatorParsers, getSuggesters(), searchExtParserRegistry);
}
public List<Entry> getNamedWriteables() {
@ -382,7 +383,7 @@ public class SearchModule extends AbstractModule {
if (false == transportClient) {
bind(IndicesQueriesRegistry.class).toInstance(queryParserRegistry);
bind(SearchRequestParsers.class).toInstance(searchRequestParsers);
bind(SearchExtParserRegistry.class).toInstance(searchExtParserRegistry);
bind(SearchExtRegistry.class).toInstance(searchExtParserRegistry);
configureSearch();
}
}
@ -728,12 +729,13 @@ public class SearchModule extends AbstractModule {
registerFromPlugin(plugins, p -> p.getFetchSubPhases(context), this::registerFetchSubPhase);
}
private void registerSearchExtParsers(List<SearchPlugin> plugins) {
registerFromPlugin(plugins, SearchPlugin::getSearchExtParsers, this::registerSearchExtParser);
private void registerSearchExts(List<SearchPlugin> plugins) {
registerFromPlugin(plugins, SearchPlugin::getSearchExts, this::registerSearchExt);
}
private void registerSearchExtParser(SearchExtParser searchExtParser) {
searchExtParserRegistry.register(searchExtParser, searchExtParser.getName());
private void registerSearchExt(SearchExtSpec<?> spec) {
searchExtParserRegistry.register(spec.getParser(), spec.getName());
namedWriteables.add(new Entry(SearchExtBuilder.class, spec.getName().getPreferredName(), spec.getReader()));
}
private void registerFetchSubPhase(FetchSubPhase subPhase) {

View File

@ -37,7 +37,8 @@ public class SearchRequestParsers {
/**
* Query parsers that may be used in search requests.
* @see org.elasticsearch.index.query.QueryParseContext
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers, Suggesters)
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers,
* Suggesters, SearchExtRegistry)
*/
public final IndicesQueriesRegistry queryParsers;
@ -45,20 +46,29 @@ public class SearchRequestParsers {
// and pipeline agg parsers should be here
/**
* Agg and pipeline agg parsers that may be used in search requests.
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers, Suggesters)
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers,
* Suggesters, SearchExtRegistry)
*/
public final AggregatorParsers aggParsers;
// TODO: Suggesters should be removed and the underlying map moved here
/**
* Suggesters that may be used in search requests.
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers, Suggesters)
* @see org.elasticsearch.search.builder.SearchSourceBuilder#fromXContent(QueryParseContext, AggregatorParsers,
* Suggesters, SearchExtRegistry)
*/
public final Suggesters suggesters;
public SearchRequestParsers(IndicesQueriesRegistry queryParsers, AggregatorParsers aggParsers, Suggesters suggesters) {
/**
* Pluggable section that can be parsed out of a search section, within the ext element
*/
public final SearchExtRegistry searchExtParsers;
public SearchRequestParsers(IndicesQueriesRegistry queryParsers, AggregatorParsers aggParsers, Suggesters suggesters,
SearchExtRegistry searchExtParsers) {
this.queryParsers = queryParsers;
this.aggParsers = aggParsers;
this.suggesters = suggesters;
this.searchExtParsers = searchExtParsers;
}
}

View File

@ -39,9 +39,6 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.common.util.concurrent.ConcurrentMapLong;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentLocation;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.engine.Engine;
@ -142,14 +139,11 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
private final ConcurrentMapLong<SearchContext> activeContexts = ConcurrentCollections.newConcurrentMapLongWithAggressiveConcurrency();
private final SearchExtParserRegistry searchExtParserRegistry;
private final ParseFieldMatcher parseFieldMatcher;
@Inject
public SearchService(Settings settings, ClusterSettings clusterSettings, ClusterService clusterService, IndicesService indicesService,
ThreadPool threadPool, ScriptService scriptService, BigArrays bigArrays, FetchPhase fetchPhase,
SearchExtParserRegistry searchExtParserRegistry) {
ThreadPool threadPool, ScriptService scriptService, BigArrays bigArrays, FetchPhase fetchPhase) {
super(settings);
this.parseFieldMatcher = new ParseFieldMatcher(settings);
this.threadPool = threadPool;
@ -165,8 +159,6 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
this.keepAliveReaper = threadPool.scheduleWithFixedDelay(new Reaper(), keepAliveInterval, Names.SAME);
this.searchExtParserRegistry = searchExtParserRegistry;
defaultSearchTimeout = DEFAULT_SEARCH_TIMEOUT_SETTING.get(settings);
clusterSettings.addSettingsUpdateConsumer(DEFAULT_SEARCH_TIMEOUT_SETTING, this::setDefaultSearchTimeout);
}
@ -749,39 +741,8 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
}
}
if (source.ext() != null) {
XContentParser extParser = null;
try {
extParser = XContentFactory.xContent(source.ext()).createParser(source.ext());
if (extParser.nextToken() != XContentParser.Token.START_OBJECT) {
throw new SearchParseException(context, "expected start object, found [" + extParser.currentToken() + "] instead",
extParser.getTokenLocation());
}
XContentParser.Token token;
String currentFieldName = null;
while ((token = extParser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = extParser.currentName();
} else {
SearchExtParser searchExtParser = this.searchExtParserRegistry.lookup(currentFieldName, parseFieldMatcher,
extParser.getTokenLocation());
Object searchExtBuilder = searchExtParser.parse(extParser);
context.putSearchExtBuilder(currentFieldName, searchExtBuilder);
}
}
} catch (Exception e) {
String sSource = "_na_";
try {
sSource = source.toString();
} catch (Exception inner) {
e.addSuppressed(inner);
// ignore
}
XContentLocation location = extParser != null ? extParser.getTokenLocation() : null;
throw new SearchParseException(context, "failed to parse ext source [" + sSource + "]", location, e);
} finally {
if (extParser != null) {
extParser.close();
}
for (SearchExtBuilder searchExtBuilder : source.ext()) {
context.addSearchExt(searchExtBuilder);
}
}
if (source.version() != null) {

View File

@ -25,7 +25,6 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
@ -33,13 +32,14 @@ import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.SearchExtParser;
import org.elasticsearch.search.SearchExtRegistry;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.AggregatorParsers;
@ -108,9 +108,9 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
public static final ParseField SLICE = new ParseField("slice");
public static SearchSourceBuilder fromXContent(QueryParseContext context, AggregatorParsers aggParsers,
Suggesters suggesters) throws IOException {
Suggesters suggesters, SearchExtRegistry searchExtRegistry) throws IOException {
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.parseXContent(context, aggParsers, suggesters);
builder.parseXContent(context, aggParsers, suggesters, searchExtRegistry);
return builder;
}
@ -164,13 +164,13 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
private SuggestBuilder suggestBuilder;
private List<RescoreBuilder<?>> rescoreBuilders;
private List<RescoreBuilder> rescoreBuilders;
private ObjectFloatHashMap<String> indexBoost = null;
private List<String> stats;
private BytesReference ext = null;
private List<SearchExtBuilder> extBuilders;
private boolean profile = false;
@ -203,18 +203,10 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
postQueryBuilder = in.readOptionalNamedWriteable(QueryBuilder.class);
queryBuilder = in.readOptionalNamedWriteable(QueryBuilder.class);
if (in.readBoolean()) {
int size = in.readVInt();
rescoreBuilders = new ArrayList<>();
for (int i = 0; i < size; i++) {
rescoreBuilders.add(in.readNamedWriteable(RescoreBuilder.class));
}
rescoreBuilders = in.readNamedWriteableList(RescoreBuilder.class);
}
if (in.readBoolean()) {
int size = in.readVInt();
scriptFields = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
scriptFields.add(new ScriptField(in));
}
scriptFields = in.readList(ScriptField::new);
}
size = in.readVInt();
if (in.readBoolean()) {
@ -225,18 +217,16 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
}
}
if (in.readBoolean()) {
int size = in.readVInt();
stats = new ArrayList<>();
for (int i = 0; i < size; i++) {
stats.add(in.readString());
}
stats = in.readList(StreamInput::readString);
}
suggestBuilder = in.readOptionalWriteable(SuggestBuilder::new);
terminateAfter = in.readVInt();
timeout = in.readOptionalWriteable(TimeValue::new);
trackScores = in.readBoolean();
version = in.readOptionalBoolean();
ext = in.readOptionalBytesReference();
if (in.readBoolean()) {
extBuilders = in.readNamedWriteableList(SearchExtBuilder.class);
}
profile = in.readBoolean();
searchAfterBuilder = in.readOptionalWriteable(SearchAfterBuilder::new);
sliceBuilder = in.readOptionalWriteable(SliceBuilder::new);
@ -262,18 +252,12 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
boolean hasRescoreBuilders = rescoreBuilders != null;
out.writeBoolean(hasRescoreBuilders);
if (hasRescoreBuilders) {
out.writeVInt(rescoreBuilders.size());
for (RescoreBuilder<?> rescoreBuilder : rescoreBuilders) {
out.writeNamedWriteable(rescoreBuilder);
}
out.writeNamedWriteableList(rescoreBuilders);
}
boolean hasScriptFields = scriptFields != null;
out.writeBoolean(hasScriptFields);
if (hasScriptFields) {
out.writeVInt(scriptFields.size());
for (ScriptField scriptField : scriptFields) {
scriptField.writeTo(out);
}
out.writeList(scriptFields);
}
out.writeVInt(size);
boolean hasSorts = sorts != null;
@ -287,17 +271,19 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
boolean hasStats = stats != null;
out.writeBoolean(hasStats);
if (hasStats) {
out.writeVInt(stats.size());
for (String stat : stats) {
out.writeString(stat);
}
out.writeStringList(stats);
}
out.writeOptionalWriteable(suggestBuilder);
out.writeVInt(terminateAfter);
out.writeOptionalWriteable(timeout);
out.writeBoolean(trackScores);
out.writeOptionalBoolean(version);
out.writeOptionalBytesReference(ext);
if (extBuilders == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeNamedWriteableList(extBuilders);
}
out.writeBoolean(profile);
out.writeOptionalWriteable(searchAfterBuilder);
out.writeOptionalWriteable(sliceBuilder);
@ -649,7 +635,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
/**
* Gets the bytes representing the rescore builders for this request.
*/
public List<RescoreBuilder<?>> rescores() {
public List<RescoreBuilder> rescores() {
return rescoreBuilders;
}
@ -875,13 +861,13 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
return stats;
}
public SearchSourceBuilder ext(XContentBuilder ext) {
this.ext = ext.bytes();
public SearchSourceBuilder ext(List<SearchExtBuilder> searchExtBuilders) {
this.extBuilders = searchExtBuilders;
return this;
}
public BytesReference ext() {
return ext;
public List<SearchExtBuilder> ext() {
return extBuilders;
}
/**
@ -919,7 +905,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
SearchSourceBuilder rewrittenBuilder = new SearchSourceBuilder();
rewrittenBuilder.aggregations = aggregations;
rewrittenBuilder.explain = explain;
rewrittenBuilder.ext = ext;
rewrittenBuilder.extBuilders = extBuilders;
rewrittenBuilder.fetchSourceContext = fetchSourceContext;
rewrittenBuilder.docValueFields = docValueFields;
rewrittenBuilder.storedFieldsContext = storedFieldsContext;
@ -948,9 +934,10 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
/**
* Parse some xContent into this SearchSourceBuilder, overwriting any values specified in the xContent. Use this if you need to set up
* different defaults than a regular SearchSourceBuilder would have and use
* {@link #fromXContent(QueryParseContext, AggregatorParsers, Suggesters)} if you have normal defaults.
* {@link #fromXContent(QueryParseContext, AggregatorParsers, Suggesters, SearchExtRegistry)} if you have normal defaults.
*/
public void parseXContent(QueryParseContext context, AggregatorParsers aggParsers, Suggesters suggesters)
public void parseXContent(QueryParseContext context, AggregatorParsers aggParsers,
Suggesters suggesters, SearchExtRegistry searchExtRegistry)
throws IOException {
XContentParser parser = context.parser();
@ -1034,8 +1021,23 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
rescoreBuilders = new ArrayList<>();
rescoreBuilders.add(RescoreBuilder.parseFromXContent(context));
} else if (context.getParseFieldMatcher().match(currentFieldName, EXT_FIELD)) {
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().copyCurrentStructure(parser);
ext = xContentBuilder.bytes();
extBuilders = new ArrayList<>();
String extSectionName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
extSectionName = parser.currentName();
} else {
SearchExtParser searchExtParser = searchExtRegistry.lookup(extSectionName,
context.getParseFieldMatcher(), parser.getTokenLocation());
SearchExtBuilder searchExtBuilder = searchExtParser.fromXContent(parser);
if (searchExtBuilder.getWriteableName().equals(extSectionName) == false) {
throw new IllegalStateException("The parsed [" + searchExtBuilder.getClass().getName() + "] object has a "
+ "different writeable name compared to the name of the section that it was parsed from: found ["
+ searchExtBuilder.getWriteableName() + "] expected [" + extSectionName + "]");
}
extBuilders.add(searchExtBuilder);
}
}
} else if (context.getParseFieldMatcher().match(currentFieldName, SLICE)) {
sliceBuilder = SliceBuilder.fromXContent(context);
} else {
@ -1221,12 +1223,12 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
builder.field(STATS_FIELD.getPreferredName(), stats);
}
if (ext != null) {
builder.field(EXT_FIELD.getPreferredName());
try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(ext)) {
parser.nextToken();
builder.copyCurrentStructure(parser);
if (extBuilders != null) {
builder.startObject(EXT_FIELD.getPreferredName());
for (SearchExtBuilder extBuilder : extBuilders) {
extBuilder.toXContent(builder, params);
}
builder.endObject();
}
}
@ -1344,9 +1346,9 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
@Override
public int hashCode() {
return Objects.hash(aggregations, explain, fetchSourceContext, docValueFields, storedFieldsContext, from,
highlightBuilder, indexBoost, minScore, postQueryBuilder, queryBuilder, rescoreBuilders, scriptFields, size, sorts,
searchAfterBuilder, sliceBuilder, stats, suggestBuilder, terminateAfter, timeout, trackScores, version, profile);
return Objects.hash(aggregations, explain, fetchSourceContext, docValueFields, storedFieldsContext, from, highlightBuilder,
indexBoost, minScore, postQueryBuilder, queryBuilder, rescoreBuilders, scriptFields, size, sorts, searchAfterBuilder,
sliceBuilder, stats, suggestBuilder, terminateAfter, timeout, trackScores, version, profile, extBuilders);
}
@Override
@ -1381,7 +1383,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
&& Objects.equals(timeout, other.timeout)
&& Objects.equals(trackScores, other.trackScores)
&& Objects.equals(version, other.version)
&& Objects.equals(profile, other.profile);
&& Objects.equals(profile, other.profile)
&& Objects.equals(extBuilders, other.extBuilders);
}
}

View File

@ -53,6 +53,7 @@ import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.aggregations.SearchContextAggregations;
import org.elasticsearch.search.dfs.DfsSearchResult;
@ -145,7 +146,7 @@ public class DefaultSearchContext extends SearchContext {
private volatile long lastAccessTime = -1;
private Profilers profilers;
private final Map<String, Object> searchExtBuilders = new HashMap<>();
private final Map<String, SearchExtBuilder> searchExtBuilders = new HashMap<>();
private final Map<Class<?>, Collector> queryCollectors = new HashMap<>();
private final QueryShardContext queryShardContext;
private FetchPhase fetchPhase;
@ -385,13 +386,15 @@ public class DefaultSearchContext extends SearchContext {
}
@Override
public Object getSearchExtBuilder(String name) {
return searchExtBuilders.get(name);
public void addSearchExt(SearchExtBuilder searchExtBuilder) {
//it's ok to use the writeable name here given that we enforce it to be the same as the name of the element that gets
//parsed by the corresponding parser. There is one single name and one single way to retrieve the parsed object from the context.
searchExtBuilders.put(searchExtBuilder.getWriteableName(), searchExtBuilder);
}
@Override
public void putSearchExtBuilder(String name, Object searchExtBuilder) {
searchExtBuilders.put(name, searchExtBuilder);
public SearchExtBuilder getSearchExt(String name) {
return searchExtBuilders.get(name);
}
@Override

View File

@ -38,6 +38,7 @@ import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.aggregations.SearchContextAggregations;
import org.elasticsearch.search.dfs.DfsSearchResult;
@ -510,13 +511,13 @@ public abstract class FilteredSearchContext extends SearchContext {
}
@Override
public void putSearchExtBuilder(String name, Object fetchSubPhaseBuilder) {
in.putSearchExtBuilder(name, fetchSubPhaseBuilder);
public void addSearchExt(SearchExtBuilder searchExtBuilder) {
in.addSearchExt(searchExtBuilder);
}
@Override
public Object getSearchExtBuilder(String name) {
return in.getSearchExtBuilder(name);
public SearchExtBuilder getSearchExt(String name) {
return in.getSearchExt(name);
}
@Override

View File

@ -44,6 +44,7 @@ import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.aggregations.SearchContextAggregations;
import org.elasticsearch.search.dfs.DfsSearchResult;
@ -184,9 +185,9 @@ public abstract class SearchContext extends AbstractRefCounted implements Releas
public abstract SearchContext aggregations(SearchContextAggregations aggregations);
public abstract void putSearchExtBuilder(String name, Object fetchSubPhaseBuilder);
public abstract void addSearchExt(SearchExtBuilder searchExtBuilder);
public abstract Object getSearchExtBuilder(String name);
public abstract SearchExtBuilder getSearchExt(String name);
public abstract SearchContextHighlight highlight();

View File

@ -175,6 +175,6 @@ public class MultiSearchRequestTests extends ESTestCase {
IndicesQueriesRegistry registry = new IndicesQueriesRegistry();
QueryParser<MatchAllQueryBuilder> parser = MatchAllQueryBuilder::fromXContent;
registry.register(parser, MatchAllQueryBuilder.NAME);
return new SearchRequestParsers(registry, null, null);
return new SearchRequestParsers(registry, null, null, null);
}
}

View File

@ -29,6 +29,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.indices.IndicesModule;
import org.elasticsearch.search.fetch.FetchSubPhasePluginIT;
import org.elasticsearch.test.ESTestCase;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -36,6 +37,7 @@ import org.junit.BeforeClass;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.emptyList;
@ -53,7 +55,8 @@ public class SearchRequestTests extends ESTestCase {
bindMapperExtension();
}
};
SearchModule searchModule = new SearchModule(Settings.EMPTY, false, emptyList()) {
SearchModule searchModule = new SearchModule(Settings.EMPTY, false,
Collections.singletonList(new FetchSubPhasePluginIT.FetchTermVectorsPlugin())) {
@Override
protected void configureSearch() {
// Skip me

View File

@ -59,6 +59,7 @@ import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.search.SearchRequestParsers;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.fetch.FetchSubPhasePluginIT;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilderTests;
import org.elasticsearch.search.rescore.QueryRescoreBuilderTests;
@ -85,7 +86,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.emptyList;
import static org.elasticsearch.test.ClusterServiceUtils.createClusterService;
import static org.elasticsearch.test.ClusterServiceUtils.setState;
import static org.hamcrest.CoreMatchers.containsString;
@ -132,7 +132,8 @@ public class SearchSourceBuilderTests extends ESTestCase {
bindMapperExtension();
}
};
SearchModule searchModule = new SearchModule(settings, false, emptyList()) {
SearchModule searchModule = new SearchModule(settings, false,
Collections.singletonList(new FetchSubPhasePluginIT.FetchTermVectorsPlugin())) {
@Override
protected void configureSearch() {
// Skip me
@ -389,11 +390,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
builder.aggregation(AggregationBuilders.avg(randomAsciiOfLengthBetween(5, 20)));
}
if (randomBoolean()) {
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder();
xContentBuilder.startObject();
xContentBuilder.field("term_vectors_fetch", randomAsciiOfLengthBetween(5, 20));
xContentBuilder.endObject();
builder.ext(xContentBuilder);
builder.ext(Collections.singletonList(new FetchSubPhasePluginIT.TermVectorsFetchBuilder("test")));
}
if (randomBoolean()) {
String field = randomBoolean() ? null : randomAsciiOfLengthBetween(5, 20);
@ -431,15 +428,14 @@ public class SearchSourceBuilderTests extends ESTestCase {
// test the embedded case
}
SearchSourceBuilder newBuilder = SearchSourceBuilder.fromXContent(parseContext, searchRequestParsers.aggParsers,
searchRequestParsers.suggesters);
searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertNull(parser.nextToken());
assertEquals(testBuilder, newBuilder);
assertEquals(testBuilder.hashCode(), newBuilder.hashCode());
}
private static QueryParseContext createParseContext(XContentParser parser) {
QueryParseContext context = new QueryParseContext(searchRequestParsers.queryParsers, parser, parseFieldMatcher);
return context;
return new QueryParseContext(searchRequestParsers.queryParsers, parser, parseFieldMatcher);
}
public void testSerialization() throws IOException {
@ -497,7 +493,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
String restContent = " { \"_source\": { \"includes\": \"include\", \"excludes\": \"*.field2\"}}";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertArrayEquals(new String[]{"*.field2"}, searchSourceBuilder.fetchSource().excludes());
assertArrayEquals(new String[]{"include"}, searchSourceBuilder.fetchSource().includes());
}
@ -506,7 +502,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
String restContent = " { \"_source\": false}";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertArrayEquals(new String[]{}, searchSourceBuilder.fetchSource().excludes());
assertArrayEquals(new String[]{}, searchSourceBuilder.fetchSource().includes());
assertFalse(searchSourceBuilder.fetchSource().fetchSource());
@ -519,7 +515,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
String restContent = " { \"sort\": \"foo\"}";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertEquals(1, searchSourceBuilder.sorts().size());
assertEquals(new FieldSortBuilder("foo"), searchSourceBuilder.sorts().get(0));
}
@ -535,7 +531,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
" ]}";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertEquals(5, searchSourceBuilder.sorts().size());
assertEquals(new FieldSortBuilder("post_date"), searchSourceBuilder.sorts().get(0));
assertEquals(new FieldSortBuilder("user"), searchSourceBuilder.sorts().get(1));
@ -559,7 +555,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
"}\n";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertEquals(1, searchSourceBuilder.aggregations().count());
}
}
@ -575,7 +571,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
"}\n";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertEquals(1, searchSourceBuilder.aggregations().count());
}
}
@ -601,7 +597,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
"}\n";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertEquals(1, searchSourceBuilder.rescores().size());
assertEquals(new QueryRescorerBuilder(QueryBuilders.matchQuery("content", "baz")).windowSize(50),
searchSourceBuilder.rescores().get(0));
@ -624,7 +620,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
"}\n";
try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) {
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertEquals(1, searchSourceBuilder.rescores().size());
assertEquals(new QueryRescorerBuilder(QueryBuilders.matchQuery("content", "baz")).windowSize(50),
searchSourceBuilder.rescores().get(0));
@ -637,7 +633,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
final String query = "{ \"query\": { \"match_all\": {}}, \"timeout\": \"" + timeout + "\"}";
try (XContentParser parser = XContentFactory.xContent(query).createParser(query)) {
final SearchSourceBuilder builder = SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
assertThat(builder.timeout(), equalTo(TimeValue.parseTimeValue(timeout, null, "timeout")));
}
}
@ -650,7 +646,7 @@ public class SearchSourceBuilderTests extends ESTestCase {
expectThrows(
ElasticsearchParseException.class,
() -> SearchSourceBuilder.fromXContent(createParseContext(parser),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters));
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers));
assertThat(e, hasToString(containsString("unit is missing or unrecognized")));
}
}

View File

@ -27,12 +27,15 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.termvectors.TermVectorsRequest;
import org.elasticsearch.action.termvectors.TermVectorsResponse;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.termvectors.TermVectorsService;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.plugins.SearchPlugin;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.SearchExtParser;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -49,6 +52,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static java.util.Collections.singletonList;
import static org.elasticsearch.client.Requests.indexRequest;
@ -56,13 +60,18 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
import static org.hamcrest.CoreMatchers.equalTo;
@ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 1)
@ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 2)
public class FetchSubPhasePluginIT extends ESIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singletonList(FetchTermVectorsPlugin.class);
}
@Override
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
return nodePlugins();
}
@SuppressWarnings("unchecked")
public void testPlugin() throws Exception {
client().admin()
@ -85,10 +94,8 @@ public class FetchSubPhasePluginIT extends ESIntegTestCase {
client().admin().indices().prepareRefresh().execute().actionGet();
XContentBuilder extSource = jsonBuilder().startObject()
.field("term_vectors_fetch", "test")
.endObject();
SearchResponse response = client().prepareSearch().setSource(new SearchSourceBuilder().ext(extSource)).get();
SearchResponse response = client().prepareSearch().setSource(new SearchSourceBuilder()
.ext(Collections.singletonList(new TermVectorsFetchBuilder("test")))).get();
assertSearchResponse(response);
assertThat(((Map<String, Integer>) response.getHits().getAt(0).field("term_vectors_fetch").getValues().get(0)).get("i"),
equalTo(2));
@ -105,8 +112,9 @@ public class FetchSubPhasePluginIT extends ESIntegTestCase {
}
@Override
public List<SearchExtParser> getSearchExtParsers() {
return Collections.singletonList(TermVectorsFetchParser.INSTANCE);
public List<SearchExtSpec<?>> getSearchExts() {
return Collections.singletonList(new SearchExtSpec<>(TermVectorsFetchSubPhase.NAME,
TermVectorsFetchBuilder::new, TermVectorsFetchParser.INSTANCE));
}
}
@ -115,7 +123,7 @@ public class FetchSubPhasePluginIT extends ESIntegTestCase {
@Override
public void hitExecute(SearchContext context, HitContext hitContext) {
TermVectorsFetchBuilder fetchSubPhaseBuilder = (TermVectorsFetchBuilder)context.getSearchExtBuilder(NAME);
TermVectorsFetchBuilder fetchSubPhaseBuilder = (TermVectorsFetchBuilder)context.getSearchExt(NAME);
if (fetchSubPhaseBuilder == null) {
return;
}
@ -145,7 +153,7 @@ public class FetchSubPhasePluginIT extends ESIntegTestCase {
}
}
public static final class TermVectorsFetchParser implements SearchExtParser {
public static final class TermVectorsFetchParser implements SearchExtParser<TermVectorsFetchBuilder> {
private static final TermVectorsFetchParser INSTANCE = new TermVectorsFetchParser();
@ -153,12 +161,7 @@ public class FetchSubPhasePluginIT extends ESIntegTestCase {
}
@Override
public String getName() {
return TermVectorsFetchSubPhase.NAME;
}
@Override
public TermVectorsFetchBuilder parse(XContentParser parser) throws Exception {
public TermVectorsFetchBuilder fromXContent(XContentParser parser) throws IOException {
String field;
XContentParser.Token token = parser.currentToken();
if (token == XContentParser.Token.VALUE_STRING) {
@ -173,15 +176,51 @@ public class FetchSubPhasePluginIT extends ESIntegTestCase {
}
}
public static final class TermVectorsFetchBuilder {
public static final class TermVectorsFetchBuilder extends SearchExtBuilder {
private final String field;
private TermVectorsFetchBuilder(String field) {
public TermVectorsFetchBuilder(String field) {
this.field = field;
}
public TermVectorsFetchBuilder(StreamInput in) throws IOException {
this.field = in.readString();
}
public String getField() {
return field;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TermVectorsFetchBuilder that = (TermVectorsFetchBuilder) o;
return Objects.equals(field, that.field);
}
@Override
public int hashCode() {
return Objects.hash(field);
}
@Override
public String getWriteableName() {
return TermVectorsFetchSubPhase.NAME;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(field);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.field(TermVectorsFetchSubPhase.NAME, field);
}
}
}

View File

@ -34,12 +34,14 @@ import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.indices.IndicesModule;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.search.SearchRequestTests;
import org.elasticsearch.search.fetch.FetchSubPhasePluginIT;
import org.elasticsearch.test.ESTestCase;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.emptyList;
@ -56,7 +58,8 @@ public class ShardSearchTransportRequestTests extends ESTestCase {
bindMapperExtension();
}
};
SearchModule searchModule = new SearchModule(Settings.EMPTY, false, emptyList()) {
SearchModule searchModule = new SearchModule(Settings.EMPTY, false,
Collections.singletonList(new FetchSubPhasePluginIT.FetchTermVectorsPlugin())) {
@Override
protected void configureSearch() {
// Skip me

View File

@ -32,14 +32,11 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchRequestParsers;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -86,7 +83,7 @@ public class TransportSearchTemplateAction extends HandledTransportAction<Search
try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
SearchSourceBuilder builder = SearchSourceBuilder.searchSource();
builder.parseXContent(new QueryParseContext(searchRequestParsers.queryParsers, parser, parseFieldMatcher),
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
searchRequestParsers.aggParsers, searchRequestParsers.suggesters, searchRequestParsers.searchExtParsers);
searchRequest.source(builder);
searchAction.execute(searchRequest, new ActionListener<SearchResponse>() {

View File

@ -37,9 +37,7 @@ import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.search.SearchRequestParsers;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -160,8 +158,7 @@ public class TransportMultiPercolateAction extends HandledTransportAction<MultiP
try {
SearchRequest searchRequest = TransportPercolateAction.createSearchRequest(
percolateRequest, docSource, searchRequestParsers.queryParsers,
searchRequestParsers.aggParsers, parseFieldMatcher
);
searchRequestParsers.aggParsers, searchRequestParsers.searchExtParsers, parseFieldMatcher);
multiSearchRequest.add(searchRequest);
} catch (Exception e) {
preFailures.put(i, new MultiPercolateResponse.Item(e));

View File

@ -45,6 +45,7 @@ import org.elasticsearch.index.query.ConstantScoreQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.search.SearchExtRegistry;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.SearchRequestParsers;
@ -69,7 +70,8 @@ public class TransportPercolateAction extends HandledTransportAction<PercolateRe
public TransportPercolateAction(Settings settings, ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
Client client, SearchRequestParsers searchRequestParsers) {
super(settings, PercolateAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, PercolateRequest::new);
super(settings, PercolateAction.NAME, threadPool, transportService, actionFilters,
indexNameExpressionResolver, PercolateRequest::new);
this.client = client;
this.searchRequestParsers = searchRequestParsers;
this.parseFieldMatcher = new ParseFieldMatcher(settings);
@ -84,7 +86,8 @@ public class TransportPercolateAction extends HandledTransportAction<PercolateRe
if (getResponse.isExists()) {
innerDoExecute(request, getResponse.getSourceAsBytesRef(), listener);
} else {
onFailure(new ResourceNotFoundException("percolate document [{}/{}/{}] doesn't exist", request.getRequest().index(), request.getRequest().type(), request.getRequest().id()));
onFailure(new ResourceNotFoundException("percolate document [{}/{}/{}] doesn't exist",
request.getRequest().index(), request.getRequest().type(), request.getRequest().id()));
}
}
@ -102,7 +105,7 @@ public class TransportPercolateAction extends HandledTransportAction<PercolateRe
SearchRequest searchRequest;
try {
searchRequest = createSearchRequest(request, docSource, searchRequestParsers.queryParsers,
searchRequestParsers.aggParsers, parseFieldMatcher);
searchRequestParsers.aggParsers, searchRequestParsers.searchExtParsers, parseFieldMatcher);
} catch (IOException e) {
listener.onFailure(e);
return;
@ -124,7 +127,10 @@ public class TransportPercolateAction extends HandledTransportAction<PercolateRe
});
}
public static SearchRequest createSearchRequest(PercolateRequest percolateRequest, BytesReference documentSource, IndicesQueriesRegistry queryRegistry, AggregatorParsers aggParsers, ParseFieldMatcher parseFieldMatcher) throws IOException {
public static SearchRequest createSearchRequest(PercolateRequest percolateRequest, BytesReference documentSource,
IndicesQueriesRegistry queryRegistry, AggregatorParsers aggParsers,
SearchExtRegistry searchExtRegistry, ParseFieldMatcher parseFieldMatcher)
throws IOException {
SearchRequest searchRequest = new SearchRequest();
if (percolateRequest.indices() != null) {
searchRequest.indices(percolateRequest.indices());
@ -220,7 +226,7 @@ public class TransportPercolateAction extends HandledTransportAction<PercolateRe
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(source)) {
QueryParseContext context = new QueryParseContext(queryRegistry, parser, parseFieldMatcher);
searchSourceBuilder.parseXContent(context, aggParsers, null);
searchSourceBuilder.parseXContent(context, aggParsers, null, searchExtRegistry);
searchRequest.source(searchSourceBuilder);
return searchRequest;
}
@ -235,7 +241,8 @@ public class TransportPercolateAction extends HandledTransportAction<PercolateRe
matches = new PercolateResponse.Match[hits.getHits().length];
for (int i = 0; i < hits.getHits().length; i++) {
SearchHit hit = hits.getHits()[i];
matches[i] = new PercolateResponse.Match(new Text(hit.getIndex()), new Text(hit.getId()), hit.getScore(), hit.getHighlightFields());
matches[i] = new PercolateResponse.Match(new Text(hit.getIndex()),
new Text(hit.getId()), hit.getScore(), hit.getHighlightFields());
}
}
@ -246,8 +253,8 @@ public class TransportPercolateAction extends HandledTransportAction<PercolateRe
}
return new PercolateResponse(
searchResponse.getTotalShards(), searchResponse.getSuccessfulShards(), searchResponse.getFailedShards(),
shardFailures, matches, hits.getTotalHits(), searchResponse.getTookInMillis(), (InternalAggregations) searchResponse.getAggregations()
searchResponse.getTotalShards(), searchResponse.getSuccessfulShards(), searchResponse.getFailedShards(), shardFailures,
matches, hits.getTotalHits(), searchResponse.getTookInMillis(), (InternalAggregations) searchResponse.getAggregations()
);
}

View File

@ -42,14 +42,11 @@ import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.reindex.remote.RemoteInfo;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchRequestParsers;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import java.io.IOException;
import java.util.List;
@ -87,7 +84,8 @@ public class RestReindexAction extends AbstractBaseReindexRestHandler<ReindexReq
builder.map(source);
try (XContentParser innerParser = parser.contentType().xContent().createParser(builder.bytes())) {
request.getSearchRequest().source().parseXContent(context.queryParseContext(innerParser),
context.searchRequestParsers.aggParsers, context.searchRequestParsers.suggesters);
context.searchRequestParsers.aggParsers, context.searchRequestParsers.suggesters,
context.searchRequestParsers.searchExtParsers);
}
};

View File

@ -110,7 +110,7 @@ public class RestReindexActionTests extends ESTestCase {
}
try (XContentParser p = JsonXContent.jsonXContent.createParser(request)) {
ReindexRequest r = new ReindexRequest(new SearchRequest(), new IndexRequest());
SearchRequestParsers searchParsers = new SearchRequestParsers(new IndicesQueriesRegistry(), null, null);
SearchRequestParsers searchParsers = new SearchRequestParsers(new IndicesQueriesRegistry(), null, null, null);
RestReindexAction.PARSER.parse(p, r, new ReindexParseContext(searchParsers, ParseFieldMatcher.STRICT));
assertEquals("localhost", r.getRemoteInfo().getHost());
assertArrayEquals(new String[] {"source"}, r.getSearchRequest().indices());

View File

@ -72,9 +72,8 @@ public class MockSearchService extends SearchService {
@Inject
public MockSearchService(Settings settings, ClusterSettings clusterSettings, ClusterService clusterService,
IndicesService indicesService, ThreadPool threadPool, ScriptService scriptService,
BigArrays bigArrays, FetchPhase fetchPhase, SearchExtParserRegistry searchExtParserRegistry) {
super(settings, clusterSettings, clusterService, indicesService, threadPool, scriptService,
bigArrays, fetchPhase, searchExtParserRegistry);
BigArrays bigArrays, FetchPhase fetchPhase) {
super(settings, clusterSettings, clusterService, indicesService, threadPool, scriptService, bigArrays, fetchPhase);
}
@Override

View File

@ -39,6 +39,7 @@ import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchExtBuilder;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.aggregations.SearchContextAggregations;
import org.elasticsearch.search.dfs.DfsSearchResult;
@ -89,7 +90,7 @@ public class TestSearchContext extends SearchContext {
private SearchContextAggregations aggregations;
private final long originNanoTime = System.nanoTime();
private final Map<String, Object> searchExtBuilders = new HashMap<>();
private final Map<String, SearchExtBuilder> searchExtBuilders = new HashMap<>();
public TestSearchContext(ThreadPool threadPool, BigArrays bigArrays, ScriptService scriptService, IndexService indexService) {
super(ParseFieldMatcher.STRICT);
@ -196,12 +197,12 @@ public class TestSearchContext extends SearchContext {
}
@Override
public void putSearchExtBuilder(String name, Object searchExtBuilders) {
this.searchExtBuilders.put(name, searchExtBuilders);
public void addSearchExt(SearchExtBuilder searchExtBuilder) {
searchExtBuilders.put(searchExtBuilder.getWriteableName(), searchExtBuilder);
}
@Override
public Object getSearchExtBuilder(String name) {
public SearchExtBuilder getSearchExt(String name) {
return searchExtBuilders.get(name);
}