diff --git a/core/src/main/java/org/elasticsearch/search/builder/OldSearchSourceBuilder.java b/core/src/main/java/org/elasticsearch/search/builder/OldSearchSourceBuilder.java deleted file mode 100644 index 949628bd040..00000000000 --- a/core/src/main/java/org/elasticsearch/search/builder/OldSearchSourceBuilder.java +++ /dev/null @@ -1,840 +0,0 @@ -/* - * 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.builder; - -import com.carrotsearch.hppc.ObjectFloatHashMap; - -import org.elasticsearch.ElasticsearchGenerationException; -import org.elasticsearch.action.support.QuerySourceBuilder; -import org.elasticsearch.action.support.ToXContentToBytes; -import org.elasticsearch.client.Requests; -import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.script.Script; -import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; -import org.elasticsearch.search.fetch.innerhits.InnerHitsBuilder; -import org.elasticsearch.search.fetch.source.FetchSourceContext; -import org.elasticsearch.search.highlight.HighlightBuilder; -import org.elasticsearch.search.internal.SearchContext; -import org.elasticsearch.search.rescore.RescoreBuilder; -import org.elasticsearch.search.sort.SortBuilder; -import org.elasticsearch.search.sort.SortBuilders; -import org.elasticsearch.search.sort.SortOrder; -import org.elasticsearch.search.suggest.SuggestBuilder; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * A search source builder allowing to easily build search source. Simple - * construction using - * {@link org.elasticsearch.search.builder.OldSearchSourceBuilder#searchSource()} - */ -public class OldSearchSourceBuilder extends ToXContentToBytes { - - /** - * A static factory method to construct a new search source. - */ - public static OldSearchSourceBuilder searchSource() { - return new OldSearchSourceBuilder(); - } - - /** - * A static factory method to construct new search highlights. - */ - public static HighlightBuilder highlight() { - return new HighlightBuilder(); - } - - private QuerySourceBuilder querySourceBuilder; - - private QueryBuilder postQueryBuilder; - - private BytesReference filterBinary; - - private int from = -1; - - private int size = -1; - - private Boolean explain; - - private Boolean version; - - private List sorts; - - private boolean trackScores = false; - - private Float minScore; - - private long timeoutInMillis = -1; - private int terminateAfter = SearchContext.DEFAULT_TERMINATE_AFTER; - - private List fieldNames; - private List fieldDataFields; - private List scriptFields; - private FetchSourceContext fetchSourceContext; - - private List aggregations; - private BytesReference aggregationsBinary; - - private HighlightBuilder highlightBuilder; - - private SuggestBuilder suggestBuilder; - - private InnerHitsBuilder innerHitsBuilder; - - private List rescoreBuilders; - private Integer defaultRescoreWindowSize; - - private ObjectFloatHashMap indexBoost = null; - - private String[] stats; - - /** - * Constructs a new search source builder. - */ - public OldSearchSourceBuilder() { - } - - /** - * Sets the query provided as a {@link QuerySourceBuilder} - */ - public OldSearchSourceBuilder query(QuerySourceBuilder querySourceBuilder) { - this.querySourceBuilder = querySourceBuilder; - return this; - } - - /** - * Constructs a new search source builder with a search query. - * - * @see org.elasticsearch.index.query.QueryBuilders - */ - public OldSearchSourceBuilder query(QueryBuilder query) { - if (this.querySourceBuilder == null) { - this.querySourceBuilder = new QuerySourceBuilder(); - } - this.querySourceBuilder.setQuery(query); - return this; - } - - /** - * Constructs a new search source builder with a raw search query. - */ - public OldSearchSourceBuilder query(byte[] queryBinary) { - return query(queryBinary, 0, queryBinary.length); - } - - /** - * Constructs a new search source builder with a raw search query. - */ - public OldSearchSourceBuilder query(byte[] queryBinary, int queryBinaryOffset, int queryBinaryLength) { - return query(new BytesArray(queryBinary, queryBinaryOffset, queryBinaryLength)); - } - - /** - * Constructs a new search source builder with a raw search query. - */ - public OldSearchSourceBuilder query(BytesReference queryBinary) { - if (this.querySourceBuilder == null) { - this.querySourceBuilder = new QuerySourceBuilder(); - } - this.querySourceBuilder.setQuery(queryBinary); - return this; - } - - /** - * Constructs a new search source builder with a raw search query. - */ - public OldSearchSourceBuilder query(String queryString) { - return query(queryString.getBytes(StandardCharsets.UTF_8)); - } - - /** - * Constructs a new search source builder with a query from a builder. - */ - public OldSearchSourceBuilder query(XContentBuilder query) { - return query(query.bytes()); - } - - /** - * Constructs a new search source builder with a query from a map. - */ - @SuppressWarnings("unchecked") - public OldSearchSourceBuilder query(Map query) { - try { - XContentBuilder builder = XContentFactory.contentBuilder(Requests.CONTENT_TYPE); - builder.map(query); - return query(builder); - } catch (IOException e) { - throw new ElasticsearchGenerationException("Failed to generate [" + query + "]", e); - } - } - - /** - * Sets a filter that will be executed after the query has been executed and - * only has affect on the search hits (not aggregations). This filter is - * always executed as last filtering mechanism. - */ - public OldSearchSourceBuilder postFilter(QueryBuilder postFilter) { - this.postQueryBuilder = postFilter; - return this; - } - - /** - * Sets a filter on the query executed that only applies to the search query - * (and not aggs for example). - */ - public OldSearchSourceBuilder postFilter(String postFilterString) { - return postFilter(postFilterString.getBytes(StandardCharsets.UTF_8)); - } - - /** - * Sets a filter on the query executed that only applies to the search query - * (and not aggs for example). - */ - public OldSearchSourceBuilder postFilter(byte[] postFilter) { - return postFilter(postFilter, 0, postFilter.length); - } - - /** - * Sets a filter on the query executed that only applies to the search query - * (and not aggs for example). - */ - public OldSearchSourceBuilder postFilter(byte[] postFilterBinary, int postFilterBinaryOffset, int postFilterBinaryLength) { - return postFilter(new BytesArray(postFilterBinary, postFilterBinaryOffset, postFilterBinaryLength)); - } - - /** - * Sets a filter on the query executed that only applies to the search query - * (and not aggs for example). - */ - public OldSearchSourceBuilder postFilter(BytesReference postFilterBinary) { - this.filterBinary = postFilterBinary; - return this; - } - - /** - * Constructs a new search source builder with a query from a builder. - */ - public OldSearchSourceBuilder postFilter(XContentBuilder postFilter) { - return postFilter(postFilter.bytes()); - } - - /** - * Constructs a new search source builder with a query from a map. - */ - @SuppressWarnings("unchecked") - public OldSearchSourceBuilder postFilter(Map postFilter) { - try { - XContentBuilder builder = XContentFactory.contentBuilder(Requests.CONTENT_TYPE); - builder.map(postFilter); - return postFilter(builder); - } catch (IOException e) { - throw new ElasticsearchGenerationException("Failed to generate [" + postFilter + "]", e); - } - } - - /** - * From index to start the search from. Defaults to 0. - */ - public OldSearchSourceBuilder from(int from) { - this.from = from; - return this; - } - - /** - * The number of search hits to return. Defaults to 10. - */ - public OldSearchSourceBuilder size(int size) { - this.size = size; - return this; - } - - /** - * Sets the minimum score below which docs will be filtered out. - */ - public OldSearchSourceBuilder minScore(float minScore) { - this.minScore = minScore; - return this; - } - - /** - * Should each {@link org.elasticsearch.search.SearchHit} be returned with - * an explanation of the hit (ranking). - */ - public OldSearchSourceBuilder explain(Boolean explain) { - this.explain = explain; - return this; - } - - /** - * Should each {@link org.elasticsearch.search.SearchHit} be returned with a - * version associated with it. - */ - public OldSearchSourceBuilder version(Boolean version) { - this.version = version; - return this; - } - - /** - * An optional timeout to control how long search is allowed to take. - */ - public OldSearchSourceBuilder timeout(TimeValue timeout) { - this.timeoutInMillis = timeout.millis(); - return this; - } - - /** - * An optional timeout to control how long search is allowed to take. - */ - public OldSearchSourceBuilder timeout(String timeout) { - this.timeoutInMillis = TimeValue.parseTimeValue(timeout, null, getClass().getSimpleName() + ".timeout").millis(); - return this; - } - - /** - * An optional terminate_after to terminate the search after collecting - * terminateAfter documents - */ - public OldSearchSourceBuilder terminateAfter(int terminateAfter) { - if (terminateAfter <= 0) { - throw new IllegalArgumentException("terminateAfter must be > 0"); - } - this.terminateAfter = terminateAfter; - return this; - } - - /** - * Adds a sort against the given field name and the sort ordering. - * - * @param name - * The name of the field - * @param order - * The sort ordering - */ - public OldSearchSourceBuilder sort(String name, SortOrder order) { - return sort(SortBuilders.fieldSort(name).order(order)); - } - - /** - * Add a sort against the given field name. - * - * @param name - * The name of the field to sort by - */ - public OldSearchSourceBuilder sort(String name) { - return sort(SortBuilders.fieldSort(name)); - } - - /** - * Adds a sort builder. - */ - public OldSearchSourceBuilder sort(SortBuilder sort) { - if (sorts == null) { - sorts = new ArrayList<>(); - } - sorts.add(sort); - return this; - } - - /** - * Applies when sorting, and controls if scores will be tracked as well. - * Defaults to false. - */ - public OldSearchSourceBuilder trackScores(boolean trackScores) { - this.trackScores = trackScores; - return this; - } - - /** - * Add an get to perform as part of the search. - */ - public OldSearchSourceBuilder aggregation(AbstractAggregationBuilder aggregation) { - if (aggregations == null) { - aggregations = new ArrayList<>(); - } - aggregations.add(aggregation); - return this; - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - public OldSearchSourceBuilder aggregations(byte[] aggregationsBinary) { - return aggregations(aggregationsBinary, 0, aggregationsBinary.length); - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - public OldSearchSourceBuilder aggregations(byte[] aggregationsBinary, int aggregationsBinaryOffset, int aggregationsBinaryLength) { - return aggregations(new BytesArray(aggregationsBinary, aggregationsBinaryOffset, aggregationsBinaryLength)); - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - public OldSearchSourceBuilder aggregations(BytesReference aggregationsBinary) { - this.aggregationsBinary = aggregationsBinary; - return this; - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - public OldSearchSourceBuilder aggregations(XContentBuilder aggs) { - return aggregations(aggs.bytes()); - } - - /** - * Set the rescore window size for rescores that don't specify their window. - */ - public OldSearchSourceBuilder defaultRescoreWindowSize(int defaultRescoreWindowSize) { - this.defaultRescoreWindowSize = defaultRescoreWindowSize; - return this; - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - @SuppressWarnings("unchecked") - public OldSearchSourceBuilder aggregations(Map aggregations) { - try { - XContentBuilder builder = XContentFactory.contentBuilder(Requests.CONTENT_TYPE); - builder.map(aggregations); - return aggregations(builder); - } catch (IOException e) { - throw new ElasticsearchGenerationException("Failed to generate [" + aggregations + "]", e); - } - } - - public HighlightBuilder highlighter() { - if (highlightBuilder == null) { - highlightBuilder = new HighlightBuilder(); - } - return highlightBuilder; - } - - /** - * Adds highlight to perform as part of the search. - */ - public OldSearchSourceBuilder highlight(HighlightBuilder highlightBuilder) { - this.highlightBuilder = highlightBuilder; - return this; - } - - public InnerHitsBuilder innerHitsBuilder() { - if (innerHitsBuilder == null) { - innerHitsBuilder = new InnerHitsBuilder(); - } - return innerHitsBuilder; - } - - public SuggestBuilder suggest() { - if (suggestBuilder == null) { - suggestBuilder = new SuggestBuilder("suggest"); - } - return suggestBuilder; - } - - public OldSearchSourceBuilder addRescorer(RescoreBuilder rescoreBuilder) { - if (rescoreBuilders == null) { - rescoreBuilders = new ArrayList<>(); - } - rescoreBuilders.add(rescoreBuilder); - return this; - } - - public OldSearchSourceBuilder clearRescorers() { - rescoreBuilders = null; - return this; - } - - /** - * Indicates whether the response should contain the stored _source for - * every hit - */ - public OldSearchSourceBuilder fetchSource(boolean fetch) { - if (this.fetchSourceContext == null) { - this.fetchSourceContext = new FetchSourceContext(fetch); - } else { - this.fetchSourceContext.fetchSource(fetch); - } - return this; - } - - /** - * Indicate that _source should be returned with every hit, with an - * "include" and/or "exclude" set which can include simple wildcard - * elements. - * - * @param include - * An optional include (optionally wildcarded) pattern to filter - * the returned _source - * @param exclude - * An optional exclude (optionally wildcarded) pattern to filter - * the returned _source - */ - public OldSearchSourceBuilder fetchSource(@Nullable String include, @Nullable String exclude) { - return fetchSource(include == null ? Strings.EMPTY_ARRAY : new String[] { include }, exclude == null ? Strings.EMPTY_ARRAY - : new String[] { exclude }); - } - - /** - * Indicate that _source should be returned with every hit, with an - * "include" and/or "exclude" set which can include simple wildcard - * elements. - * - * @param includes - * An optional list of include (optionally wildcarded) pattern to - * filter the returned _source - * @param excludes - * An optional list of exclude (optionally wildcarded) pattern to - * filter the returned _source - */ - public OldSearchSourceBuilder fetchSource(@Nullable String[] includes, @Nullable String[] excludes) { - fetchSourceContext = new FetchSourceContext(includes, excludes); - return this; - } - - /** - * Indicate how the _source should be fetched. - */ - public OldSearchSourceBuilder fetchSource(@Nullable FetchSourceContext fetchSourceContext) { - this.fetchSourceContext = fetchSourceContext; - return this; - } - - /** - * Sets no fields to be loaded, resulting in only id and type to be returned - * per field. - */ - public OldSearchSourceBuilder noFields() { - this.fieldNames = Collections.emptyList(); - return this; - } - - /** - * Sets the fields to load and return as part of the search request. If none - * are specified, the source of the document will be returned. - */ - public OldSearchSourceBuilder fields(List fields) { - this.fieldNames = fields; - return this; - } - - /** - * Adds the fields to load and return as part of the search request. If none - * are specified, the source of the document will be returned. - */ - public OldSearchSourceBuilder fields(String... fields) { - if (fieldNames == null) { - fieldNames = new ArrayList<>(); - } - Collections.addAll(fieldNames, fields); - return this; - } - - /** - * Adds a field to load and return (note, it must be stored) as part of the - * search request. If none are specified, the source of the document will be - * return. - */ - public OldSearchSourceBuilder field(String name) { - if (fieldNames == null) { - fieldNames = new ArrayList<>(); - } - fieldNames.add(name); - return this; - } - - /** - * Adds a field to load from the field data cache and return as part of the - * search request. - */ - public OldSearchSourceBuilder fieldDataField(String name) { - if (fieldDataFields == null) { - fieldDataFields = new ArrayList<>(); - } - fieldDataFields.add(name); - return this; - } - - /** - * Adds a script field under the given name with the provided script. - * - * @param name - * The name of the field - * @param script - * The script - */ - public OldSearchSourceBuilder scriptField(String name, Script script) { - if (scriptFields == null) { - scriptFields = new ArrayList<>(); - } - scriptFields.add(new ScriptField(name, script)); - return this; - } - - /** - * Sets the boost a specific index will receive when the query is executeed - * against it. - * - * @param index - * The index to apply the boost against - * @param indexBoost - * The boost to apply to the index - */ - public OldSearchSourceBuilder indexBoost(String index, float indexBoost) { - if (this.indexBoost == null) { - this.indexBoost = new ObjectFloatHashMap<>(); - } - this.indexBoost.put(index, indexBoost); - return this; - } - - /** - * The stats groups this request will be aggregated under. - */ - public OldSearchSourceBuilder stats(String... statsGroups) { - this.stats = statsGroups; - return this; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - innerToXContent(builder, params); - builder.endObject(); - return builder; - } - - public void innerToXContent(XContentBuilder builder, Params params) throws IOException { - if (from != -1) { - builder.field("from", from); - } - if (size != -1) { - builder.field("size", size); - } - - if (timeoutInMillis != -1) { - builder.field("timeout", timeoutInMillis); - } - - if (terminateAfter != SearchContext.DEFAULT_TERMINATE_AFTER) { - builder.field("terminate_after", terminateAfter); - } - - if (querySourceBuilder != null) { - querySourceBuilder.innerToXContent(builder, params); - } - - if (postQueryBuilder != null) { - builder.field("post_filter"); - postQueryBuilder.toXContent(builder, params); - } - - if (filterBinary != null) { - if (XContentFactory.xContentType(filterBinary) == builder.contentType()) { - builder.rawField("filter", filterBinary); - } else { - builder.field("filter_binary", filterBinary); - } - } - - if (minScore != null) { - builder.field("min_score", minScore); - } - - if (version != null) { - builder.field("version", version); - } - - if (explain != null) { - builder.field("explain", explain); - } - - if (fetchSourceContext != null) { - if (!fetchSourceContext.fetchSource()) { - builder.field("_source", false); - } else { - builder.startObject("_source"); - builder.array("includes", fetchSourceContext.includes()); - builder.array("excludes", fetchSourceContext.excludes()); - builder.endObject(); - } - } - - if (fieldNames != null) { - if (fieldNames.size() == 1) { - builder.field("fields", fieldNames.get(0)); - } else { - builder.startArray("fields"); - for (String fieldName : fieldNames) { - builder.value(fieldName); - } - builder.endArray(); - } - } - - if (fieldDataFields != null) { - builder.startArray("fielddata_fields"); - for (String fieldName : fieldDataFields) { - builder.value(fieldName); - } - builder.endArray(); - } - - if (scriptFields != null) { - builder.startObject("script_fields"); - for (ScriptField scriptField : scriptFields) { - builder.startObject(scriptField.fieldName()); - builder.field("script", scriptField.script()); - builder.endObject(); - } - builder.endObject(); - } - - if (sorts != null) { - builder.startArray("sort"); - for (SortBuilder sort : sorts) { - builder.startObject(); - sort.toXContent(builder, params); - builder.endObject(); - } - builder.endArray(); - } - - if (trackScores) { - builder.field("track_scores", true); - } - - if (indexBoost != null) { - builder.startObject("indices_boost"); - assert !indexBoost.containsKey(null); - final Object[] keys = indexBoost.keys; - final float[] values = indexBoost.values; - for (int i = 0; i < keys.length; i++) { - if (keys[i] != null) { - builder.field((String) keys[i], values[i]); - } - } - builder.endObject(); - } - - if (aggregations != null) { - builder.field("aggregations"); - builder.startObject(); - for (AbstractAggregationBuilder aggregation : aggregations) { - aggregation.toXContent(builder, params); - } - builder.endObject(); - } - - if (aggregationsBinary != null) { - if (XContentFactory.xContentType(aggregationsBinary) == builder.contentType()) { - builder.rawField("aggregations", aggregationsBinary); - } else { - builder.field("aggregations_binary", aggregationsBinary); - } - } - - if (highlightBuilder != null) { - highlightBuilder.toXContent(builder, params); - } - - if (innerHitsBuilder != null) { - innerHitsBuilder.toXContent(builder, params); - } - - if (suggestBuilder != null) { - suggestBuilder.toXContent(builder, params); - } - - if (rescoreBuilders != null) { - // Strip empty rescoreBuilders from the request - Iterator itr = rescoreBuilders.iterator(); - while (itr.hasNext()) { - if (itr.next().isEmpty()) { - itr.remove(); - } - } - - // Now build the request taking care to skip empty lists and only send the object form - // if there is just one builder. - if (rescoreBuilders.size() == 1) { - builder.startObject("rescore"); - rescoreBuilders.get(0).toXContent(builder, params); - if (rescoreBuilders.get(0).windowSize() == null && defaultRescoreWindowSize != null) { - builder.field("window_size", defaultRescoreWindowSize); - } - builder.endObject(); - } else if (!rescoreBuilders.isEmpty()) { - builder.startArray("rescore"); - for (RescoreBuilder rescoreBuilder : rescoreBuilders) { - builder.startObject(); - rescoreBuilder.toXContent(builder, params); - if (rescoreBuilder.windowSize() == null && defaultRescoreWindowSize != null) { - builder.field("window_size", defaultRescoreWindowSize); - } - builder.endObject(); - } - builder.endArray(); - } - } - - if (stats != null) { - builder.startArray("stats"); - for (String stat : stats) { - builder.value(stat); - } - builder.endArray(); - } - } - - private static class ScriptField { - private final String fieldName; - private final Script script; - - private ScriptField(String fieldName, Script script) { - this.fieldName = fieldName; - this.script = script; - } - - public String fieldName() { - return fieldName; - } - - public Script script() { - return script; - } - } -} diff --git a/core/src/test/java/org/elasticsearch/search/builder/NewSearchSourceBuilderTests.java b/core/src/test/java/org/elasticsearch/search/builder/NewSearchSourceBuilderTests.java deleted file mode 100644 index 9f663ef531e..00000000000 --- a/core/src/test/java/org/elasticsearch/search/builder/NewSearchSourceBuilderTests.java +++ /dev/null @@ -1,637 +0,0 @@ -/* - * 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.builder; - -import org.elasticsearch.Version; -import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; -import org.elasticsearch.action.get.GetRequest; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.support.PlainActionFuture; -import org.elasticsearch.action.termvectors.MultiTermVectorsRequest; -import org.elasticsearch.action.termvectors.MultiTermVectorsResponse; -import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.ClusterService; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.common.ParseFieldMatcher; -import org.elasticsearch.common.compress.CompressedXContent; -import org.elasticsearch.common.inject.AbstractModule; -import org.elasticsearch.common.inject.Injector; -import org.elasticsearch.common.inject.ModulesBuilder; -import org.elasticsearch.common.inject.multibindings.Multibinder; -import org.elasticsearch.common.inject.util.Providers; -import org.elasticsearch.common.io.stream.BytesStreamOutput; -import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.settings.SettingsModule; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.EnvironmentModule; -import org.elasticsearch.index.Index; -import org.elasticsearch.index.IndexNameModule; -import org.elasticsearch.index.analysis.AnalysisModule; -import org.elasticsearch.index.cache.IndexCacheModule; -import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.mapper.MapperServiceModule; -import org.elasticsearch.index.query.AbstractQueryTestCase; -import org.elasticsearch.index.query.IndexQueryParserService; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.QueryParseContext; -import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.index.query.functionscore.ScoreFunctionParser; -import org.elasticsearch.index.settings.IndexSettingsModule; -import org.elasticsearch.index.similarity.SimilarityModule; -import org.elasticsearch.indices.IndicesModule; -import org.elasticsearch.indices.analysis.IndicesAnalysisService; -import org.elasticsearch.indices.breaker.CircuitBreakerService; -import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptModule; -import org.elasticsearch.search.aggregations.AggregationBuilders; -import org.elasticsearch.search.fetch.innerhits.InnerHitsBuilder; -import org.elasticsearch.search.fetch.innerhits.InnerHitsBuilder.InnerHit; -import org.elasticsearch.search.fetch.source.FetchSourceContext; -import org.elasticsearch.search.highlight.HighlightBuilder; -import org.elasticsearch.search.internal.SearchContext; -import org.elasticsearch.search.rescore.RescoreBuilder; -import org.elasticsearch.search.sort.SortBuilders; -import org.elasticsearch.search.sort.SortOrder; -import org.elasticsearch.search.suggest.SuggestBuilder; -import org.elasticsearch.search.suggest.SuggestBuilders; -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.TestSearchContext; -import org.elasticsearch.test.VersionUtils; -import org.elasticsearch.test.cluster.TestClusterService; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.threadpool.ThreadPoolModule; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.io.IOException; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.Matchers.equalTo; - -public class NewSearchSourceBuilderTests extends ESTestCase { - - protected static final String STRING_FIELD_NAME = "mapped_string"; - protected static final String STRING_FIELD_NAME_2 = "mapped_string_2"; - protected static final String INT_FIELD_NAME = "mapped_int"; - protected static final String DOUBLE_FIELD_NAME = "mapped_double"; - protected static final String BOOLEAN_FIELD_NAME = "mapped_boolean"; - protected static final String DATE_FIELD_NAME = "mapped_date"; - protected static final String OBJECT_FIELD_NAME = "mapped_object"; - protected static final String GEO_POINT_FIELD_NAME = "mapped_geo_point"; - protected static final String GEO_SHAPE_FIELD_NAME = "mapped_geo_shape"; - protected static final String[] MAPPED_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, - BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, OBJECT_FIELD_NAME, GEO_POINT_FIELD_NAME, GEO_SHAPE_FIELD_NAME }; - protected static final String[] MAPPED_LEAF_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, - BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, GEO_POINT_FIELD_NAME }; - - private static Injector injector; - private static IndexQueryParserService queryParserService; - - protected static IndexQueryParserService queryParserService() { - return queryParserService; - } - - private static Index index; - - protected static Index getIndex() { - return index; - } - - private static String[] currentTypes; - - protected static String[] getCurrentTypes() { - return currentTypes; - } - - private static NamedWriteableRegistry namedWriteableRegistry; - - private static String[] randomTypes; - private static ClientInvocationHandler clientInvocationHandler = new ClientInvocationHandler(); - - /** - * Setup for the whole base test class. - */ - @BeforeClass - public static void init() throws IOException { - // we have to prefer CURRENT since with the range of versions we support it's rather unlikely to get the current actually. - Version version = randomBoolean() ? Version.CURRENT : VersionUtils.randomVersionBetween(random(), Version.V_2_0_0_beta1, Version.CURRENT); - Settings settings = Settings.settingsBuilder() - .put("name", AbstractQueryTestCase.class.toString()) - .put("path.home", createTempDir()) - .build(); - Settings indexSettings = Settings.settingsBuilder() - .put(IndexMetaData.SETTING_VERSION_CREATED, version).build(); - index = new Index(randomAsciiOfLengthBetween(1, 10)); - final TestClusterService clusterService = new TestClusterService(); - clusterService.setState(new ClusterState.Builder(clusterService.state()).metaData(new MetaData.Builder().put( - new IndexMetaData.Builder(index.name()).settings(indexSettings).numberOfShards(1).numberOfReplicas(0)))); - final Client proxy = (Client) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[] { Client.class }, - clientInvocationHandler); - injector = new ModulesBuilder().add( - new EnvironmentModule(new Environment(settings)), - new SettingsModule(settings), - new ThreadPoolModule(new ThreadPool(settings)), - new MapperServiceModule(), - new IndicesModule(settings) { - @Override - public void configure() { - // skip services - bindQueryParsersExtension(); - } - }, - new ScriptModule(settings), - new IndexSettingsModule(index, indexSettings), - new IndexCacheModule(indexSettings), - new AnalysisModule(indexSettings, new IndicesAnalysisService(indexSettings)), - new SimilarityModule(indexSettings), - new IndexNameModule(index), - new AbstractModule() { - @Override - protected void configure() { - bind(Client.class).toInstance(proxy); - Multibinder.newSetBinder(binder(), ScoreFunctionParser.class); - bind(ClusterService.class).toProvider(Providers.of(clusterService)); - bind(CircuitBreakerService.class).to(NoneCircuitBreakerService.class); - bind(NamedWriteableRegistry.class).asEagerSingleton(); - } - } - ).createInjector(); - queryParserService = injector.getInstance(IndexQueryParserService.class); - MapperService mapperService = injector.getInstance(MapperService.class); - //create some random type with some default field, those types will stick around for all of the subclasses - currentTypes = new String[randomIntBetween(0, 5)]; - for (int i = 0; i < currentTypes.length; i++) { - String type = randomAsciiOfLengthBetween(1, 10); - mapperService.merge(type, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(type, - STRING_FIELD_NAME, "type=string", - STRING_FIELD_NAME_2, "type=string", - INT_FIELD_NAME, "type=integer", - DOUBLE_FIELD_NAME, "type=double", - BOOLEAN_FIELD_NAME, "type=boolean", - DATE_FIELD_NAME, "type=date", - OBJECT_FIELD_NAME, "type=object", - GEO_POINT_FIELD_NAME, "type=geo_point,lat_lon=true,geohash=true,geohash_prefix=true", - GEO_SHAPE_FIELD_NAME, "type=geo_shape" - ).string()), false, false); - // also add mappings for two inner field in the object field - mapperService.merge(type, new CompressedXContent("{\"properties\":{\""+OBJECT_FIELD_NAME+"\":{\"type\":\"object\"," - + "\"properties\":{\""+DATE_FIELD_NAME+"\":{\"type\":\"date\"},\""+INT_FIELD_NAME+"\":{\"type\":\"integer\"}}}}}"), false, false); - currentTypes[i] = type; - } - namedWriteableRegistry = injector.getInstance(NamedWriteableRegistry.class); - } - - @AfterClass - public static void afterClass() throws Exception { - terminate(injector.getInstance(ThreadPool.class)); - injector = null; - index = null; - queryParserService = null; - currentTypes = null; - namedWriteableRegistry = null; - randomTypes = null; - } - - @Before - public void beforeTest() { - clientInvocationHandler.delegate = this; - //set some random types to be queried as part the search request, before each test - randomTypes = getRandomTypes(); - } - - protected void setSearchContext(String[] types) { - TestSearchContext testSearchContext = new TestSearchContext(); - testSearchContext.setTypes(types); - SearchContext.setCurrent(testSearchContext); - } - - @After - public void afterTest() { - clientInvocationHandler.delegate = null; - QueryShardContext.removeTypes(); - SearchContext.removeCurrent(); - } - - protected final SearchSourceBuilder createSearchSourceBuilder() throws IOException { - SearchSourceBuilder builder = new SearchSourceBuilder(); - if (randomBoolean()) { - builder.from(randomIntBetween(0, 10000)); - } - if (randomBoolean()) { - builder.size(randomIntBetween(0, 10000)); - } - if (randomBoolean()) { - builder.explain(randomBoolean()); - } - if (randomBoolean()) { - builder.version(randomBoolean()); - } - if (randomBoolean()) { - builder.trackScores(randomBoolean()); - } - if (randomBoolean()) { - builder.minScore(randomFloat() * 1000); - } - if (randomBoolean()) { - builder.timeout(new TimeValue(randomIntBetween(1, 100), randomFrom(TimeUnit.values()))); - } - if (randomBoolean()) { - builder.terminateAfter(randomIntBetween(1, 100000)); - } - // if (randomBoolean()) { - // builder.defaultRescoreWindowSize(randomIntBetween(1, 100)); - // } - if (randomBoolean()) { - int fieldsSize = randomInt(25); - List fields = new ArrayList<>(fieldsSize); - for (int i = 0; i < fieldsSize; i++) { - fields.add(randomAsciiOfLengthBetween(5, 50)); - } - builder.fields(fields); - } - if (randomBoolean()) { - int fieldDataFieldsSize = randomInt(25); - for (int i = 0; i < fieldDataFieldsSize; i++) { - builder.fieldDataField(randomAsciiOfLengthBetween(5, 50)); - } - } - if (randomBoolean()) { - int scriptFieldsSize = randomInt(25); - for (int i = 0; i < scriptFieldsSize; i++) { - if (randomBoolean()) { - builder.scriptField(randomAsciiOfLengthBetween(5, 50), new Script("foo"), randomBoolean()); - } else { - builder.scriptField(randomAsciiOfLengthBetween(5, 50), new Script("foo")); - } - } - } - if (randomBoolean()) { - FetchSourceContext fetchSourceContext; - int branch = randomInt(5); - String[] includes = new String[randomIntBetween(0, 20)]; - for (int i = 0; i < includes.length; i++) { - includes[i] = randomAsciiOfLengthBetween(5, 20); - } - String[] excludes = new String[randomIntBetween(0, 20)]; - for (int i = 0; i < excludes.length; i++) { - excludes[i] = randomAsciiOfLengthBetween(5, 20); - } - switch (branch) { - case 0: - fetchSourceContext = new FetchSourceContext(randomBoolean()); - break; - case 1: - fetchSourceContext = new FetchSourceContext(includes, excludes); - break; - case 2: - fetchSourceContext = new FetchSourceContext(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20)); - break; - case 3: - fetchSourceContext = new FetchSourceContext(true, includes, excludes, randomBoolean()); - break; - case 4: - fetchSourceContext = new FetchSourceContext(includes); - break; - case 5: - fetchSourceContext = new FetchSourceContext(randomAsciiOfLengthBetween(5, 20)); - break; - default: - throw new IllegalStateException(); - } - builder.fetchSource(fetchSourceContext); - } - if (randomBoolean()) { - int size = randomIntBetween(0, 20); - List statsGroups = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - statsGroups.add(randomAsciiOfLengthBetween(5, 20)); - } - builder.stats(statsGroups); - } - if (randomBoolean()) { - int indexBoostSize = randomIntBetween(1, 10); - for (int i = 0; i < indexBoostSize; i++) { - builder.indexBoost(randomAsciiOfLengthBetween(5, 20), randomFloat() * 10); - } - } - if (randomBoolean()) { - // NORELEASE make RandomQueryBuilder work outside of the - // AbstractQueryTestCase - // builder.query(RandomQueryBuilder.createQuery(getRandom())); - builder.query(QueryBuilders.termQuery(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20))); - } - if (randomBoolean()) { - // NORELEASE make RandomQueryBuilder work outside of the - // AbstractQueryTestCase - // builder.postFilter(RandomQueryBuilder.createQuery(getRandom())); - builder.postFilter(QueryBuilders.termQuery(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20))); - } - if (randomBoolean()) { - int numSorts = randomIntBetween(1, 5); - for (int i = 0; i < numSorts; i++) { - int branch = randomInt(5); - switch (branch) { - case 0: - builder.sort(SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values()))); - break; - case 1: - builder.sort(SortBuilders.geoDistanceSort(randomAsciiOfLengthBetween(5, 20)) - .geohashes(AbstractQueryTestCase.randomGeohash(1, 12)).order(randomFrom(SortOrder.values()))); - break; - case 2: - builder.sort(SortBuilders.scoreSort().order(randomFrom(SortOrder.values()))); - break; - case 3: - builder.sort(SortBuilders.scriptSort(new Script("foo"), "number").order(randomFrom(SortOrder.values()))); - break; - case 4: - builder.sort(randomAsciiOfLengthBetween(5, 20)); - break; - case 5: - builder.sort(randomAsciiOfLengthBetween(5, 20), randomFrom(SortOrder.values())); - break; - } - } - } - if (randomBoolean()) { - // NORELEASE need a random highlight builder method - builder.highlighter(new HighlightBuilder().field(randomAsciiOfLengthBetween(5, 20))); - } - if (randomBoolean()) { - // NORELEASE need a random suggest builder method - builder.suggest(new SuggestBuilder().setText(randomAsciiOfLengthBetween(1, 5)).addSuggestion( - SuggestBuilders.termSuggestion(randomAsciiOfLengthBetween(1, 5)))); - } - if (randomBoolean()) { - // NORELEASE need a random inner hits builder method - InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); - InnerHit innerHit = new InnerHit(); - innerHit.field(randomAsciiOfLengthBetween(5, 20)); - innerHitsBuilder.addNestedInnerHits(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20), innerHit); - builder.innerHits(innerHitsBuilder); - } - if (randomBoolean()) { - int numRescores = randomIntBetween(1, 5); - for (int i = 0; i < numRescores; i++) { - // NORELEASE need a random rescore builder method - RescoreBuilder rescoreBuilder = new RescoreBuilder(); - rescoreBuilder.rescorer(RescoreBuilder.queryRescorer(QueryBuilders.termQuery(randomAsciiOfLengthBetween(5, 20), - randomAsciiOfLengthBetween(5, 20)))); - builder.addRescorer(rescoreBuilder); - } - } - if (randomBoolean()) { - // NORELEASE need a random aggregation builder method - builder.aggregation(AggregationBuilders.avg(randomAsciiOfLengthBetween(5, 20))); - } - if (true) { - // NORELEASE need a method to randomly build content for ext - XContentBuilder xContentBuilder = XContentFactory.jsonBuilder(); - xContentBuilder.startObject(); - xContentBuilder.field("term_vectors_fetch", randomAsciiOfLengthBetween(5, 20)); - xContentBuilder.endObject(); - builder.ext(xContentBuilder); - } - return builder; - } - - /** - * Generic test that creates new query from the test query and checks both for equality - * and asserts equality on the two queries. - */ - @Test - public void testFromXContent() throws IOException { - SearchSourceBuilder testBuilder = createSearchSourceBuilder(); - SearchSourceBuilder newBuilder = parseQuery(testBuilder.toString(), ParseFieldMatcher.STRICT); - assertNotSame(testBuilder, newBuilder); - assertEquals(testBuilder, newBuilder); - assertEquals(testBuilder.hashCode(), newBuilder.hashCode()); - } - - protected SearchSourceBuilder parseQuery(String queryAsString, ParseFieldMatcher matcher) throws IOException { - XContentParser parser = XContentFactory.xContent(queryAsString).createParser(queryAsString); - QueryParseContext context = createParseContext(); - context.reset(parser); - context.parseFieldMatcher(matcher); - return SearchSourceBuilder.PROTOTYPE.fromXContent(parser, context); - } - - /** - * Test serialization and deserialization of the test query. - */ - @Test - public void testSerialization() throws IOException { - SearchSourceBuilder testBuilder = createSearchSourceBuilder(); - try (BytesStreamOutput output = new BytesStreamOutput()) { - testBuilder.writeTo(output); - try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) { - SearchSourceBuilder deserializedBuilder = SearchSourceBuilder.PROTOTYPE.readFrom(in); - assertEquals(deserializedBuilder, testBuilder); - assertEquals(deserializedBuilder.hashCode(), testBuilder.hashCode()); - assertNotSame(deserializedBuilder, testBuilder); - } - } - } - - @Test - public void testEqualsAndHashcode() throws IOException { - SearchSourceBuilder firstBuilder = createSearchSourceBuilder(); - assertFalse("query is equal to null", firstBuilder.equals(null)); - assertFalse("query is equal to incompatible type", firstBuilder.equals("")); - assertTrue("query is not equal to self", firstBuilder.equals(firstBuilder)); - assertThat("same query's hashcode returns different values if called multiple times", firstBuilder.hashCode(), - equalTo(firstBuilder.hashCode())); - - SearchSourceBuilder secondBuilder = copyBuilder(firstBuilder); - assertTrue("query is not equal to self", secondBuilder.equals(secondBuilder)); - assertTrue("query is not equal to its copy", firstBuilder.equals(secondBuilder)); - assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder)); - assertThat("query copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(firstBuilder.hashCode())); - - SearchSourceBuilder thirdBuilder = copyBuilder(secondBuilder); - assertTrue("query is not equal to self", thirdBuilder.equals(thirdBuilder)); - assertTrue("query is not equal to its copy", secondBuilder.equals(thirdBuilder)); - assertThat("query copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(thirdBuilder.hashCode())); - assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder)); - assertThat("query copy's hashcode is different from original hashcode", firstBuilder.hashCode(), equalTo(thirdBuilder.hashCode())); - assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder)); - assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder)); - } - - //we use the streaming infra to create a copy of the query provided as argument - protected SearchSourceBuilder copyBuilder(SearchSourceBuilder builder) throws IOException { - try (BytesStreamOutput output = new BytesStreamOutput()) { - builder.writeTo(output); - try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) { - SearchSourceBuilder secondQuery = SearchSourceBuilder.PROTOTYPE.readFrom(in); - return secondQuery; - } - } - } - - /** - * @return a new {@link QueryShardContext} based on the base test index and queryParserService - */ - protected static QueryShardContext createShardContext() { - QueryShardContext queryCreationContext = new QueryShardContext(index, queryParserService); - queryCreationContext.reset(); - queryCreationContext.parseFieldMatcher(ParseFieldMatcher.EMPTY); - return queryCreationContext; - } - - /** - * @return a new {@link QueryParseContext} based on the base test index and queryParserService - */ - protected static QueryParseContext createParseContext() { - return createShardContext().parseContext(); - } - - protected String[] getRandomTypes() { - String[] types; - if (currentTypes.length > 0 && randomBoolean()) { - int numberOfQueryTypes = randomIntBetween(1, currentTypes.length); - types = new String[numberOfQueryTypes]; - for (int i = 0; i < numberOfQueryTypes; i++) { - types[i] = randomFrom(currentTypes); - } - } else { - if (randomBoolean()) { - types = new String[] { MetaData.ALL }; - } else { - types = new String[0]; - } - } - return types; - } - - private static class ClientInvocationHandler implements InvocationHandler { - NewSearchSourceBuilderTests delegate; - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.equals(Client.class.getDeclaredMethod("get", GetRequest.class))) { - return new PlainActionFuture() { - @Override - public GetResponse get() throws InterruptedException, ExecutionException { - return delegate.executeGet((GetRequest) args[0]); - } - }; - } else if (method.equals(Client.class.getDeclaredMethod("multiTermVectors", MultiTermVectorsRequest.class))) { - return new PlainActionFuture() { - @Override - public MultiTermVectorsResponse get() throws InterruptedException, ExecutionException { - return delegate.executeMultiTermVectors((MultiTermVectorsRequest) args[0]); - } - }; - } else if (method.equals(Object.class.getDeclaredMethod("toString"))) { - return "MockClient"; - } - throw new UnsupportedOperationException("this test can't handle calls to: " + method); - } - - } - - /** - * Override this to handle {@link Client#get(GetRequest)} calls from parsers - * / builders - */ - protected GetResponse executeGet(GetRequest getRequest) { - throw new UnsupportedOperationException("this test can't handle GET requests"); - } - - /** - * Override this to handle {@link Client#get(GetRequest)} calls from parsers - * / builders - */ - protected MultiTermVectorsResponse executeMultiTermVectors(MultiTermVectorsRequest mtvRequest) { - throw new UnsupportedOperationException("this test can't handle MultiTermVector requests"); - } - - public void testParseIncludeExclude() throws IOException { - SearchSourceBuilder builder = new SearchSourceBuilder(); - { - String restContent = " { \"_source\": { \"includes\": \"include\", \"excludes\": \"*.field2\"}}"; - try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { - SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); - assertArrayEquals(new String[]{"*.field2" }, searchSourceBuilder.fetchSource().excludes()); - assertArrayEquals(new String[]{"include" }, searchSourceBuilder.fetchSource().includes()); - } - } - { - String restContent = " { \"_source\": false}"; - try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { - SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); - assertArrayEquals(new String[]{}, searchSourceBuilder.fetchSource().excludes()); - assertArrayEquals(new String[]{}, searchSourceBuilder.fetchSource().includes()); - assertFalse(searchSourceBuilder.fetchSource().fetchSource()); - } - } - } - - public void testParseSort() throws IOException { - SearchSourceBuilder builder = new SearchSourceBuilder(); - { - String restContent = " { \"sort\": \"foo\"}"; - try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { - SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); - assertEquals(1, searchSourceBuilder.sorts().size()); - assertEquals("{\"foo\":{}}", searchSourceBuilder.sorts().get(0).toUtf8()); - } - } - - { - String restContent = "{\"sort\" : [\n" + - " { \"post_date\" : {\"order\" : \"asc\"}},\n" + - " \"user\",\n" + - " { \"name\" : \"desc\" },\n" + - " { \"age\" : \"desc\" },\n" + - " \"_score\"\n" + - " ]}"; - try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { - SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); - assertEquals(5, searchSourceBuilder.sorts().size()); - assertEquals("{\"post_date\":{\"order\":\"asc\"}}", searchSourceBuilder.sorts().get(0).toUtf8()); - assertEquals("\"user\"", searchSourceBuilder.sorts().get(1).toUtf8()); - assertEquals("{\"name\":\"desc\"}", searchSourceBuilder.sorts().get(2).toUtf8()); - assertEquals("{\"age\":\"desc\"}", searchSourceBuilder.sorts().get(3).toUtf8()); - assertEquals("\"_score\"", searchSourceBuilder.sorts().get(4).toUtf8()); - } - } - } -} diff --git a/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java b/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java index 80a683c5021..85e4b8e9d58 100644 --- a/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java @@ -16,75 +16,622 @@ * specific language governing permissions and limitations * under the License. */ + package org.elasticsearch.search.builder; +import org.elasticsearch.Version; +import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; +import org.elasticsearch.action.get.GetRequest; +import org.elasticsearch.action.get.GetResponse; +import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.action.termvectors.MultiTermVectorsRequest; +import org.elasticsearch.action.termvectors.MultiTermVectorsResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.ClusterService; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.common.ParseFieldMatcher; +import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.inject.AbstractModule; +import org.elasticsearch.common.inject.Injector; +import org.elasticsearch.common.inject.ModulesBuilder; +import org.elasticsearch.common.inject.multibindings.Multibinder; +import org.elasticsearch.common.inject.util.Providers; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.settings.SettingsModule; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.EnvironmentModule; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.IndexNameModule; +import org.elasticsearch.index.analysis.AnalysisModule; +import org.elasticsearch.index.cache.IndexCacheModule; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.MapperServiceModule; +import org.elasticsearch.index.query.AbstractQueryTestCase; +import org.elasticsearch.index.query.IndexQueryParserService; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.functionscore.ScoreFunctionParser; +import org.elasticsearch.index.settings.IndexSettingsModule; +import org.elasticsearch.index.similarity.SimilarityModule; +import org.elasticsearch.indices.IndicesModule; +import org.elasticsearch.indices.analysis.IndicesAnalysisService; +import org.elasticsearch.indices.breaker.CircuitBreakerService; +import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptModule; +import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.fetch.innerhits.InnerHitsBuilder; +import org.elasticsearch.search.fetch.innerhits.InnerHitsBuilder.InnerHit; +import org.elasticsearch.search.fetch.source.FetchSourceContext; +import org.elasticsearch.search.highlight.HighlightBuilder; +import org.elasticsearch.search.internal.SearchContext; +import org.elasticsearch.search.rescore.RescoreBuilder; +import org.elasticsearch.search.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.search.suggest.SuggestBuilder; +import org.elasticsearch.search.suggest.SuggestBuilders; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.TestSearchContext; +import org.elasticsearch.test.VersionUtils; +import org.elasticsearch.test.cluster.TestClusterService; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.threadpool.ThreadPoolModule; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.equalTo; public class SearchSourceBuilderTests extends ESTestCase { - SearchSourceBuilder builder = new SearchSourceBuilder(); + protected static final String STRING_FIELD_NAME = "mapped_string"; + protected static final String STRING_FIELD_NAME_2 = "mapped_string_2"; + protected static final String INT_FIELD_NAME = "mapped_int"; + protected static final String DOUBLE_FIELD_NAME = "mapped_double"; + protected static final String BOOLEAN_FIELD_NAME = "mapped_boolean"; + protected static final String DATE_FIELD_NAME = "mapped_date"; + protected static final String OBJECT_FIELD_NAME = "mapped_object"; + protected static final String GEO_POINT_FIELD_NAME = "mapped_geo_point"; + protected static final String GEO_SHAPE_FIELD_NAME = "mapped_geo_shape"; + protected static final String[] MAPPED_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, + BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, OBJECT_FIELD_NAME, GEO_POINT_FIELD_NAME, GEO_SHAPE_FIELD_NAME }; + protected static final String[] MAPPED_LEAF_FIELD_NAMES = new String[] { STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, + BOOLEAN_FIELD_NAME, DATE_FIELD_NAME, GEO_POINT_FIELD_NAME }; - @Test // issue #6632 - public void testThatSearchSourceBuilderIncludesExcludesAreAppliedCorrectly() throws Exception { - builder.fetchSource("foo", null); - assertIncludes(builder, "foo"); - assertExcludes(builder); + private static Injector injector; + private static IndexQueryParserService queryParserService; - builder.fetchSource(null, "foo"); - assertIncludes(builder); - assertExcludes(builder, "foo"); - - builder.fetchSource(null, new String[]{"foo"}); - assertIncludes(builder); - assertExcludes(builder, "foo"); - - builder.fetchSource(new String[]{"foo"}, null); - assertIncludes(builder, "foo"); - assertExcludes(builder); - - builder.fetchSource("foo", "bar"); - assertIncludes(builder, "foo"); - assertExcludes(builder, "bar"); - - builder.fetchSource(new String[]{"foo"}, new String[]{"bar", "baz"}); - assertIncludes(builder, "foo"); - assertExcludes(builder, "bar", "baz"); + protected static IndexQueryParserService queryParserService() { + return queryParserService; } - private void assertIncludes(SearchSourceBuilder builder, String... elems) throws IOException { - assertFieldValues(builder, "includes", elems); + private static Index index; + + protected static Index getIndex() { + return index; } - private void assertExcludes(SearchSourceBuilder builder, String... elems) throws IOException { - assertFieldValues(builder, "excludes", elems); + private static String[] currentTypes; + + protected static String[] getCurrentTypes() { + return currentTypes; } - private void assertFieldValues(SearchSourceBuilder builder, String fieldName, String... elems) throws IOException { - Map map = getSourceMap(builder); + private static NamedWriteableRegistry namedWriteableRegistry; - assertThat(map, hasKey(fieldName)); - assertThat(map.get(fieldName), is(instanceOf(List.class))); - List castedList = (List) map.get(fieldName); - assertThat(castedList, hasSize(elems.length)); - assertThat(castedList, hasItems(elems)); - } + private static String[] randomTypes; + private static ClientInvocationHandler clientInvocationHandler = new ClientInvocationHandler(); - private Map getSourceMap(SearchSourceBuilder builder) throws IOException { - Map data; - try (XContentParser parser = JsonXContent.jsonXContent.createParser(builder.toString())) { - data = parser.map(); + /** + * Setup for the whole base test class. + */ + @BeforeClass + public static void init() throws IOException { + // we have to prefer CURRENT since with the range of versions we support it's rather unlikely to get the current actually. + Version version = randomBoolean() ? Version.CURRENT : VersionUtils.randomVersionBetween(random(), Version.V_2_0_0_beta1, Version.CURRENT); + Settings settings = Settings.settingsBuilder() + .put("name", AbstractQueryTestCase.class.toString()) + .put("path.home", createTempDir()) + .build(); + Settings indexSettings = Settings.settingsBuilder() + .put(IndexMetaData.SETTING_VERSION_CREATED, version).build(); + index = new Index(randomAsciiOfLengthBetween(1, 10)); + final TestClusterService clusterService = new TestClusterService(); + clusterService.setState(new ClusterState.Builder(clusterService.state()).metaData(new MetaData.Builder().put( + new IndexMetaData.Builder(index.name()).settings(indexSettings).numberOfShards(1).numberOfReplicas(0)))); + final Client proxy = (Client) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[] { Client.class }, + clientInvocationHandler); + injector = new ModulesBuilder().add( + new EnvironmentModule(new Environment(settings)), + new SettingsModule(settings), + new ThreadPoolModule(new ThreadPool(settings)), + new MapperServiceModule(), + new IndicesModule(settings) { + @Override + public void configure() { + // skip services + bindQueryParsersExtension(); + } + }, + new ScriptModule(settings), + new IndexSettingsModule(index, indexSettings), + new IndexCacheModule(indexSettings), + new AnalysisModule(indexSettings, new IndicesAnalysisService(indexSettings)), + new SimilarityModule(indexSettings), + new IndexNameModule(index), + new AbstractModule() { + @Override + protected void configure() { + bind(Client.class).toInstance(proxy); + Multibinder.newSetBinder(binder(), ScoreFunctionParser.class); + bind(ClusterService.class).toProvider(Providers.of(clusterService)); + bind(CircuitBreakerService.class).to(NoneCircuitBreakerService.class); + bind(NamedWriteableRegistry.class).asEagerSingleton(); + } + } + ).createInjector(); + queryParserService = injector.getInstance(IndexQueryParserService.class); + MapperService mapperService = injector.getInstance(MapperService.class); + //create some random type with some default field, those types will stick around for all of the subclasses + currentTypes = new String[randomIntBetween(0, 5)]; + for (int i = 0; i < currentTypes.length; i++) { + String type = randomAsciiOfLengthBetween(1, 10); + mapperService.merge(type, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(type, + STRING_FIELD_NAME, "type=string", + STRING_FIELD_NAME_2, "type=string", + INT_FIELD_NAME, "type=integer", + DOUBLE_FIELD_NAME, "type=double", + BOOLEAN_FIELD_NAME, "type=boolean", + DATE_FIELD_NAME, "type=date", + OBJECT_FIELD_NAME, "type=object", + GEO_POINT_FIELD_NAME, "type=geo_point,lat_lon=true,geohash=true,geohash_prefix=true", + GEO_SHAPE_FIELD_NAME, "type=geo_shape" + ).string()), false, false); + // also add mappings for two inner field in the object field + mapperService.merge(type, new CompressedXContent("{\"properties\":{\""+OBJECT_FIELD_NAME+"\":{\"type\":\"object\"," + + "\"properties\":{\""+DATE_FIELD_NAME+"\":{\"type\":\"date\"},\""+INT_FIELD_NAME+"\":{\"type\":\"integer\"}}}}}"), false, false); + currentTypes[i] = type; } - assertThat(data, hasKey("_source")); - return (Map) data.get("_source"); + namedWriteableRegistry = injector.getInstance(NamedWriteableRegistry.class); } + @AfterClass + public static void afterClass() throws Exception { + terminate(injector.getInstance(ThreadPool.class)); + injector = null; + index = null; + queryParserService = null; + currentTypes = null; + namedWriteableRegistry = null; + randomTypes = null; + } + + @Before + public void beforeTest() { + clientInvocationHandler.delegate = this; + //set some random types to be queried as part the search request, before each test + randomTypes = getRandomTypes(); + } + + protected void setSearchContext(String[] types) { + TestSearchContext testSearchContext = new TestSearchContext(); + testSearchContext.setTypes(types); + SearchContext.setCurrent(testSearchContext); + } + + @After + public void afterTest() { + clientInvocationHandler.delegate = null; + QueryShardContext.removeTypes(); + SearchContext.removeCurrent(); + } + + protected final SearchSourceBuilder createSearchSourceBuilder() throws IOException { + SearchSourceBuilder builder = new SearchSourceBuilder(); + if (randomBoolean()) { + builder.from(randomIntBetween(0, 10000)); + } + if (randomBoolean()) { + builder.size(randomIntBetween(0, 10000)); + } + if (randomBoolean()) { + builder.explain(randomBoolean()); + } + if (randomBoolean()) { + builder.version(randomBoolean()); + } + if (randomBoolean()) { + builder.trackScores(randomBoolean()); + } + if (randomBoolean()) { + builder.minScore(randomFloat() * 1000); + } + if (randomBoolean()) { + builder.timeout(new TimeValue(randomIntBetween(1, 100), randomFrom(TimeUnit.values()))); + } + if (randomBoolean()) { + builder.terminateAfter(randomIntBetween(1, 100000)); + } + // if (randomBoolean()) { + // builder.defaultRescoreWindowSize(randomIntBetween(1, 100)); + // } + if (randomBoolean()) { + int fieldsSize = randomInt(25); + List fields = new ArrayList<>(fieldsSize); + for (int i = 0; i < fieldsSize; i++) { + fields.add(randomAsciiOfLengthBetween(5, 50)); + } + builder.fields(fields); + } + if (randomBoolean()) { + int fieldDataFieldsSize = randomInt(25); + for (int i = 0; i < fieldDataFieldsSize; i++) { + builder.fieldDataField(randomAsciiOfLengthBetween(5, 50)); + } + } + if (randomBoolean()) { + int scriptFieldsSize = randomInt(25); + for (int i = 0; i < scriptFieldsSize; i++) { + if (randomBoolean()) { + builder.scriptField(randomAsciiOfLengthBetween(5, 50), new Script("foo"), randomBoolean()); + } else { + builder.scriptField(randomAsciiOfLengthBetween(5, 50), new Script("foo")); + } + } + } + if (randomBoolean()) { + FetchSourceContext fetchSourceContext; + int branch = randomInt(5); + String[] includes = new String[randomIntBetween(0, 20)]; + for (int i = 0; i < includes.length; i++) { + includes[i] = randomAsciiOfLengthBetween(5, 20); + } + String[] excludes = new String[randomIntBetween(0, 20)]; + for (int i = 0; i < excludes.length; i++) { + excludes[i] = randomAsciiOfLengthBetween(5, 20); + } + switch (branch) { + case 0: + fetchSourceContext = new FetchSourceContext(randomBoolean()); + break; + case 1: + fetchSourceContext = new FetchSourceContext(includes, excludes); + break; + case 2: + fetchSourceContext = new FetchSourceContext(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20)); + break; + case 3: + fetchSourceContext = new FetchSourceContext(true, includes, excludes, randomBoolean()); + break; + case 4: + fetchSourceContext = new FetchSourceContext(includes); + break; + case 5: + fetchSourceContext = new FetchSourceContext(randomAsciiOfLengthBetween(5, 20)); + break; + default: + throw new IllegalStateException(); + } + builder.fetchSource(fetchSourceContext); + } + if (randomBoolean()) { + int size = randomIntBetween(0, 20); + List statsGroups = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + statsGroups.add(randomAsciiOfLengthBetween(5, 20)); + } + builder.stats(statsGroups); + } + if (randomBoolean()) { + int indexBoostSize = randomIntBetween(1, 10); + for (int i = 0; i < indexBoostSize; i++) { + builder.indexBoost(randomAsciiOfLengthBetween(5, 20), randomFloat() * 10); + } + } + if (randomBoolean()) { + // NORELEASE make RandomQueryBuilder work outside of the + // AbstractQueryTestCase + // builder.query(RandomQueryBuilder.createQuery(getRandom())); + builder.query(QueryBuilders.termQuery(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20))); + } + if (randomBoolean()) { + // NORELEASE make RandomQueryBuilder work outside of the + // AbstractQueryTestCase + // builder.postFilter(RandomQueryBuilder.createQuery(getRandom())); + builder.postFilter(QueryBuilders.termQuery(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20))); + } + if (randomBoolean()) { + int numSorts = randomIntBetween(1, 5); + for (int i = 0; i < numSorts; i++) { + int branch = randomInt(5); + switch (branch) { + case 0: + builder.sort(SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values()))); + break; + case 1: + builder.sort(SortBuilders.geoDistanceSort(randomAsciiOfLengthBetween(5, 20)) + .geohashes(AbstractQueryTestCase.randomGeohash(1, 12)).order(randomFrom(SortOrder.values()))); + break; + case 2: + builder.sort(SortBuilders.scoreSort().order(randomFrom(SortOrder.values()))); + break; + case 3: + builder.sort(SortBuilders.scriptSort(new Script("foo"), "number").order(randomFrom(SortOrder.values()))); + break; + case 4: + builder.sort(randomAsciiOfLengthBetween(5, 20)); + break; + case 5: + builder.sort(randomAsciiOfLengthBetween(5, 20), randomFrom(SortOrder.values())); + break; + } + } + } + if (randomBoolean()) { + // NORELEASE need a random highlight builder method + builder.highlighter(new HighlightBuilder().field(randomAsciiOfLengthBetween(5, 20))); + } + if (randomBoolean()) { + // NORELEASE need a random suggest builder method + builder.suggest(new SuggestBuilder().setText(randomAsciiOfLengthBetween(1, 5)).addSuggestion( + SuggestBuilders.termSuggestion(randomAsciiOfLengthBetween(1, 5)))); + } + if (randomBoolean()) { + // NORELEASE need a random inner hits builder method + InnerHitsBuilder innerHitsBuilder = new InnerHitsBuilder(); + InnerHit innerHit = new InnerHit(); + innerHit.field(randomAsciiOfLengthBetween(5, 20)); + innerHitsBuilder.addNestedInnerHits(randomAsciiOfLengthBetween(5, 20), randomAsciiOfLengthBetween(5, 20), innerHit); + builder.innerHits(innerHitsBuilder); + } + if (randomBoolean()) { + int numRescores = randomIntBetween(1, 5); + for (int i = 0; i < numRescores; i++) { + // NORELEASE need a random rescore builder method + RescoreBuilder rescoreBuilder = new RescoreBuilder(); + rescoreBuilder.rescorer(RescoreBuilder.queryRescorer(QueryBuilders.termQuery(randomAsciiOfLengthBetween(5, 20), + randomAsciiOfLengthBetween(5, 20)))); + builder.addRescorer(rescoreBuilder); + } + } + if (randomBoolean()) { + // NORELEASE need a random aggregation builder method + builder.aggregation(AggregationBuilders.avg(randomAsciiOfLengthBetween(5, 20))); + } + if (true) { + // NORELEASE need a method to randomly build content for ext + XContentBuilder xContentBuilder = XContentFactory.jsonBuilder(); + xContentBuilder.startObject(); + xContentBuilder.field("term_vectors_fetch", randomAsciiOfLengthBetween(5, 20)); + xContentBuilder.endObject(); + builder.ext(xContentBuilder); + } + return builder; + } + + /** + * Generic test that creates new query from the test query and checks both for equality + * and asserts equality on the two queries. + */ + @Test + public void testFromXContent() throws IOException { + SearchSourceBuilder testBuilder = createSearchSourceBuilder(); + SearchSourceBuilder newBuilder = parseQuery(testBuilder.toString(), ParseFieldMatcher.STRICT); + assertNotSame(testBuilder, newBuilder); + assertEquals(testBuilder, newBuilder); + assertEquals(testBuilder.hashCode(), newBuilder.hashCode()); + } + + protected SearchSourceBuilder parseQuery(String queryAsString, ParseFieldMatcher matcher) throws IOException { + XContentParser parser = XContentFactory.xContent(queryAsString).createParser(queryAsString); + QueryParseContext context = createParseContext(); + context.reset(parser); + context.parseFieldMatcher(matcher); + return SearchSourceBuilder.PROTOTYPE.fromXContent(parser, context); + } + + /** + * Test serialization and deserialization of the test query. + */ + @Test + public void testSerialization() throws IOException { + SearchSourceBuilder testBuilder = createSearchSourceBuilder(); + try (BytesStreamOutput output = new BytesStreamOutput()) { + testBuilder.writeTo(output); + try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) { + SearchSourceBuilder deserializedBuilder = SearchSourceBuilder.PROTOTYPE.readFrom(in); + assertEquals(deserializedBuilder, testBuilder); + assertEquals(deserializedBuilder.hashCode(), testBuilder.hashCode()); + assertNotSame(deserializedBuilder, testBuilder); + } + } + } + + @Test + public void testEqualsAndHashcode() throws IOException { + SearchSourceBuilder firstBuilder = createSearchSourceBuilder(); + assertFalse("query is equal to null", firstBuilder.equals(null)); + assertFalse("query is equal to incompatible type", firstBuilder.equals("")); + assertTrue("query is not equal to self", firstBuilder.equals(firstBuilder)); + assertThat("same query's hashcode returns different values if called multiple times", firstBuilder.hashCode(), + equalTo(firstBuilder.hashCode())); + + SearchSourceBuilder secondBuilder = copyBuilder(firstBuilder); + assertTrue("query is not equal to self", secondBuilder.equals(secondBuilder)); + assertTrue("query is not equal to its copy", firstBuilder.equals(secondBuilder)); + assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder)); + assertThat("query copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(firstBuilder.hashCode())); + + SearchSourceBuilder thirdBuilder = copyBuilder(secondBuilder); + assertTrue("query is not equal to self", thirdBuilder.equals(thirdBuilder)); + assertTrue("query is not equal to its copy", secondBuilder.equals(thirdBuilder)); + assertThat("query copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(thirdBuilder.hashCode())); + assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder)); + assertThat("query copy's hashcode is different from original hashcode", firstBuilder.hashCode(), equalTo(thirdBuilder.hashCode())); + assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder)); + assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder)); + } + + //we use the streaming infra to create a copy of the query provided as argument + protected SearchSourceBuilder copyBuilder(SearchSourceBuilder builder) throws IOException { + try (BytesStreamOutput output = new BytesStreamOutput()) { + builder.writeTo(output); + try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) { + SearchSourceBuilder secondQuery = SearchSourceBuilder.PROTOTYPE.readFrom(in); + return secondQuery; + } + } + } + + /** + * @return a new {@link QueryShardContext} based on the base test index and queryParserService + */ + protected static QueryShardContext createShardContext() { + QueryShardContext queryCreationContext = new QueryShardContext(index, queryParserService); + queryCreationContext.reset(); + queryCreationContext.parseFieldMatcher(ParseFieldMatcher.EMPTY); + return queryCreationContext; + } + + /** + * @return a new {@link QueryParseContext} based on the base test index and queryParserService + */ + protected static QueryParseContext createParseContext() { + return createShardContext().parseContext(); + } + + protected String[] getRandomTypes() { + String[] types; + if (currentTypes.length > 0 && randomBoolean()) { + int numberOfQueryTypes = randomIntBetween(1, currentTypes.length); + types = new String[numberOfQueryTypes]; + for (int i = 0; i < numberOfQueryTypes; i++) { + types[i] = randomFrom(currentTypes); + } + } else { + if (randomBoolean()) { + types = new String[] { MetaData.ALL }; + } else { + types = new String[0]; + } + } + return types; + } + + private static class ClientInvocationHandler implements InvocationHandler { + SearchSourceBuilderTests delegate; + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.equals(Client.class.getDeclaredMethod("get", GetRequest.class))) { + return new PlainActionFuture() { + @Override + public GetResponse get() throws InterruptedException, ExecutionException { + return delegate.executeGet((GetRequest) args[0]); + } + }; + } else if (method.equals(Client.class.getDeclaredMethod("multiTermVectors", MultiTermVectorsRequest.class))) { + return new PlainActionFuture() { + @Override + public MultiTermVectorsResponse get() throws InterruptedException, ExecutionException { + return delegate.executeMultiTermVectors((MultiTermVectorsRequest) args[0]); + } + }; + } else if (method.equals(Object.class.getDeclaredMethod("toString"))) { + return "MockClient"; + } + throw new UnsupportedOperationException("this test can't handle calls to: " + method); + } + + } + + /** + * Override this to handle {@link Client#get(GetRequest)} calls from parsers + * / builders + */ + protected GetResponse executeGet(GetRequest getRequest) { + throw new UnsupportedOperationException("this test can't handle GET requests"); + } + + /** + * Override this to handle {@link Client#get(GetRequest)} calls from parsers + * / builders + */ + protected MultiTermVectorsResponse executeMultiTermVectors(MultiTermVectorsRequest mtvRequest) { + throw new UnsupportedOperationException("this test can't handle MultiTermVector requests"); + } + + public void testParseIncludeExclude() throws IOException { + SearchSourceBuilder builder = new SearchSourceBuilder(); + { + String restContent = " { \"_source\": { \"includes\": \"include\", \"excludes\": \"*.field2\"}}"; + try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { + SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); + assertArrayEquals(new String[]{"*.field2" }, searchSourceBuilder.fetchSource().excludes()); + assertArrayEquals(new String[]{"include" }, searchSourceBuilder.fetchSource().includes()); + } + } + { + String restContent = " { \"_source\": false}"; + try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { + SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); + assertArrayEquals(new String[]{}, searchSourceBuilder.fetchSource().excludes()); + assertArrayEquals(new String[]{}, searchSourceBuilder.fetchSource().includes()); + assertFalse(searchSourceBuilder.fetchSource().fetchSource()); + } + } + } + + public void testParseSort() throws IOException { + SearchSourceBuilder builder = new SearchSourceBuilder(); + { + String restContent = " { \"sort\": \"foo\"}"; + try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { + SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); + assertEquals(1, searchSourceBuilder.sorts().size()); + assertEquals("{\"foo\":{}}", searchSourceBuilder.sorts().get(0).toUtf8()); + } + } + + { + String restContent = "{\"sort\" : [\n" + + " { \"post_date\" : {\"order\" : \"asc\"}},\n" + + " \"user\",\n" + + " { \"name\" : \"desc\" },\n" + + " { \"age\" : \"desc\" },\n" + + " \"_score\"\n" + + " ]}"; + try (XContentParser parser = XContentFactory.xContent(restContent).createParser(restContent)) { + SearchSourceBuilder searchSourceBuilder = builder.fromXContent(parser, new QueryParseContext(queryParserService.indicesQueriesRegistry())); + assertEquals(5, searchSourceBuilder.sorts().size()); + assertEquals("{\"post_date\":{\"order\":\"asc\"}}", searchSourceBuilder.sorts().get(0).toUtf8()); + assertEquals("\"user\"", searchSourceBuilder.sorts().get(1).toUtf8()); + assertEquals("{\"name\":\"desc\"}", searchSourceBuilder.sorts().get(2).toUtf8()); + assertEquals("{\"age\":\"desc\"}", searchSourceBuilder.sorts().get(3).toUtf8()); + assertEquals("\"_score\"", searchSourceBuilder.sorts().get(4).toUtf8()); + } + } + } }