From 9116c1c19f8f9e299d0cdb6f108f798b5fcc41a5 Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Wed, 16 Sep 2015 11:51:58 +0100 Subject: [PATCH] Renamed SearchSourceBuilder to OldSearchSourceBuilder and NewSearchSourceBuilder to SearchSourceBuilder --- .../builder/NewSearchSourceBuilder.java | 1273 ----------------- .../builder/OldSearchSourceBuilder.java | 843 +++++++++++ .../search/builder/SearchSourceBuilder.java | 1123 ++++++++++----- .../builder/NewSearchSourceBuilderTests.java | 28 +- 4 files changed, 1661 insertions(+), 1606 deletions(-) delete mode 100644 core/src/main/java/org/elasticsearch/search/builder/NewSearchSourceBuilder.java create mode 100644 core/src/main/java/org/elasticsearch/search/builder/OldSearchSourceBuilder.java diff --git a/core/src/main/java/org/elasticsearch/search/builder/NewSearchSourceBuilder.java b/core/src/main/java/org/elasticsearch/search/builder/NewSearchSourceBuilder.java deleted file mode 100644 index 3c5bf9f7ba2..00000000000 --- a/core/src/main/java/org/elasticsearch/search/builder/NewSearchSourceBuilder.java +++ /dev/null @@ -1,1273 +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 com.carrotsearch.hppc.cursors.ObjectCursor; - -import org.elasticsearch.action.support.ToXContentToBytes; -import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -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.QueryParsingException; -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.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -/** - * A search source builder allowing to easily build search source. Simple - * construction using - * {@link org.elasticsearch.search.builder.NewSearchSourceBuilder#searchSource()}. - * - * @see org.elasticsearch.action.search.SearchRequest#source(NewSearchSourceBuilder) - */ -/** - * - */ -public final class NewSearchSourceBuilder extends ToXContentToBytes implements Writeable { - - public static final ParseField FROM_FIELD = new ParseField("from"); - public static final ParseField SIZE_FIELD = new ParseField("size"); - public static final ParseField TIMEOUT_FIELD = new ParseField("timeout"); - public static final ParseField TERMINATE_AFTER_FIELD = new ParseField("terminate_after"); - public static final ParseField QUERY_FIELD = new ParseField("query"); - public static final ParseField POST_FILTER_FIELD = new ParseField("post_filter"); - public static final ParseField MIN_SCORE_FIELD = new ParseField("min_score"); - public static final ParseField VERSION_FIELD = new ParseField("version"); - public static final ParseField EXPLAIN_FIELD = new ParseField("explain"); - public static final ParseField _SOURCE_FIELD = new ParseField("_source"); - public static final ParseField FIELDS_FIELD = new ParseField("fields"); - public static final ParseField FIELDDATA_FIELDS_FIELD = new ParseField("fielddata_fields"); - public static final ParseField SCRIPT_FIELDS_FIELD = new ParseField("script_fields"); - public static final ParseField SCRIPT_FIELD = new ParseField("script"); - public static final ParseField SORT_FIELD = new ParseField("sort"); - public static final ParseField TRACK_SCORES_FIELD = new ParseField("track_scores"); - public static final ParseField INDICES_BOOST_FIELD = new ParseField("indices_boost"); - public static final ParseField AGGREGATIONS_FIELD = new ParseField("aggregations"); - public static final ParseField HIGHLIGHT_FIELD = new ParseField("highlight"); - public static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits"); - public static final ParseField SUGGEST_FIELD = new ParseField("suggest"); - public static final ParseField RESCORE_FIELD = new ParseField("rescore"); - public static final ParseField STATS_FIELD = new ParseField("stats"); - - public static final NewSearchSourceBuilder PROTOTYPE = new NewSearchSourceBuilder(); - - /** - * A static factory method to construct a new search source. - */ - public static NewSearchSourceBuilder searchSource() { - return new NewSearchSourceBuilder(); - } - - private QueryBuilder queryBuilder; - - private QueryBuilder postQueryBuilder; - - 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 highlightBuilder; - - private BytesReference suggestBuilder; - - private BytesReference innerHitsBuilder; - - private List rescoreBuilders; - private Integer defaultRescoreWindowSize; - - private ObjectFloatHashMap indexBoost = null; - - private String[] stats; - - /** - * Constructs a new search source builder. - */ - public NewSearchSourceBuilder() { - } - - /** - * Sets the search query for this request. - * - * @see org.elasticsearch.index.query.QueryBuilders - */ - public NewSearchSourceBuilder query(QueryBuilder query) { - this.queryBuilder = query; - return this; - } - - /** - * Gets the query for this request - */ - public QueryBuilder query() { - return queryBuilder; - } - - /** - * 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 NewSearchSourceBuilder postFilter(QueryBuilder postFilter) { - this.postQueryBuilder = postFilter; - return this; - } - - /** - * Gets the post filter for this request - */ - public QueryBuilder postFilter() { - return postQueryBuilder; - } - - /** - * From index to start the search from. Defaults to 0. - */ - public NewSearchSourceBuilder from(int from) { - this.from = from; - return this; - } - - /** - * Gets the from index to start the search from. - **/ - public int from() { - return from; - } - - /** - * The number of search hits to return. Defaults to 10. - */ - public NewSearchSourceBuilder size(int size) { - this.size = size; - return this; - } - - /** - * Gets the number of search hits to return. - */ - public int size() { - return size; - } - - /** - * Sets the minimum score below which docs will be filtered out. - */ - public NewSearchSourceBuilder minScore(float minScore) { - this.minScore = minScore; - return this; - } - - /** - * Gets the minimum score below which docs will be filtered out. - */ - public float minScore() { - return minScore; - } - - /** - * Should each {@link org.elasticsearch.search.SearchHit} be returned with - * an explanation of the hit (ranking). - */ - public NewSearchSourceBuilder explain(Boolean explain) { - this.explain = explain; - return this; - } - - /** - * Indicates whether each search hit will be returned with an explanation of - * the hit (ranking) - */ - public Boolean explain() { - return explain; - } - - /** - * Should each {@link org.elasticsearch.search.SearchHit} be returned with a - * version associated with it. - */ - public NewSearchSourceBuilder version(Boolean version) { - this.version = version; - return this; - } - - /** - * Indicates whether the document's version will be included in the search - * hits. - */ - public Boolean version() { - return version; - } - - /** - * An optional timeout to control how long search is allowed to take. - */ - public NewSearchSourceBuilder timeout(TimeValue timeout) { - this.timeoutInMillis = timeout.millis(); - return this; - } - - /** - * Gets the timeout to control how long search is allowed to take. - */ - public long timeoutInMillis() { - return timeoutInMillis; - } - - /** - * An optional terminate_after to terminate the search after collecting - * terminateAfter documents - */ - public NewSearchSourceBuilder terminateAfter(int terminateAfter) { - if (terminateAfter <= 0) { - throw new IllegalArgumentException("terminateAfter must be > 0"); - } - this.terminateAfter = terminateAfter; - return this; - } - - /** - * Gets the number of documents to terminate after collecting. - */ - public int terminateAfter() { - return terminateAfter; - } - - /** - * Adds a sort against the given field name and the sort ordering. - * - * @param name - * The name of the field - * @param order - * The sort ordering - * @throws IOException - */ - public NewSearchSourceBuilder sort(String name, SortOrder order) throws IOException { - 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 - * @throws IOException - */ - public NewSearchSourceBuilder sort(String name) throws IOException { - return sort(SortBuilders.fieldSort(name)); - } - - /** - * Adds a sort builder. - */ - public NewSearchSourceBuilder sort(SortBuilder sort) throws IOException { - if (sorts == null) { - sorts = new ArrayList<>(); - } - XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); - sort.toXContent(builder, EMPTY_PARAMS); - builder.endObject(); - sorts.add(builder.bytes()); - return this; - } - - /** - * Gets the bytes representing the sort builders for this request. - */ - public List sorts() { - return sorts; - } - - /** - * Applies when sorting, and controls if scores will be tracked as well. - * Defaults to false. - */ - public NewSearchSourceBuilder trackScores(boolean trackScores) { - this.trackScores = trackScores; - return this; - } - - /** - * Indicates whether scores will be tracked for this request. - */ - public boolean trackScores() { - return trackScores; - } - - /** - * Add an aggregation to perform as part of the search. - */ - public NewSearchSourceBuilder aggregation(AbstractAggregationBuilder aggregation) throws IOException { - if (aggregations == null) { - aggregations = new ArrayList<>(); - } - XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); - aggregation.toXContent(builder, EMPTY_PARAMS); - builder.endObject(); - aggregations.add(builder.bytes()); - return this; - } - - /** - * Gets the bytes representing the aggregation builders for this request. - */ - public List aggregations() { - return aggregations; - } - - /** - * Set the rescore window size for rescores that don't specify their window. - */ - public NewSearchSourceBuilder defaultRescoreWindowSize(int defaultRescoreWindowSize) { - this.defaultRescoreWindowSize = defaultRescoreWindowSize; - return this; - } - - /** - * Get the rescore window size for rescores that don't specify their window. - */ - public int defaultRescoreWindowSize() { - return defaultRescoreWindowSize; - } - - /** - * Adds highlight to perform as part of the search. - */ - public NewSearchSourceBuilder highlight(HighlightBuilder highlightBuilder) throws IOException { - XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); - highlightBuilder.innerXContent(builder, EMPTY_PARAMS); - builder.endObject(); - this.highlightBuilder = builder.bytes(); - return this; - } - - /** - * Gets the bytes representing the hightlighter builder for this request. - */ - public BytesReference highlight() { - return highlightBuilder; - } - - public NewSearchSourceBuilder innerHits(InnerHitsBuilder innerHitsBuilder) throws IOException { - XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); - innerHitsBuilder.innerXContent(builder, EMPTY_PARAMS); - builder.endObject(); - this.innerHitsBuilder = builder.bytes(); - return this; - } - - /** - * Gets the bytes representing the inner hits builder for this request. - */ - public BytesReference innerHits() { - return innerHitsBuilder; - } - - public NewSearchSourceBuilder suggest(SuggestBuilder suggestBuilder) throws IOException { - XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); - suggestBuilder.toXContent(builder, EMPTY_PARAMS); - builder.endObject(); - this.suggestBuilder = builder.bytes(); - return this; - } - - /** - * Gets the bytes representing the suggester builder for this request. - */ - public BytesReference suggest() { - return suggestBuilder; - } - - public NewSearchSourceBuilder addRescorer(RescoreBuilder rescoreBuilder) throws IOException { - if (rescoreBuilders == null) { - rescoreBuilders = new ArrayList<>(); - } - XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); - rescoreBuilder.toXContent(builder, EMPTY_PARAMS); - builder.endObject(); - rescoreBuilders.add(builder.bytes()); - return this; - } - - public NewSearchSourceBuilder clearRescorers() { - rescoreBuilders = null; - return this; - } - - /** - * Gets the bytes representing the rescore builders for this request. - */ - public List rescores() { - return rescoreBuilders; - } - - /** - * Indicates whether the response should contain the stored _source for - * every hit - */ - public NewSearchSourceBuilder 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 NewSearchSourceBuilder 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 NewSearchSourceBuilder fetchSource(@Nullable String[] includes, @Nullable String[] excludes) { - fetchSourceContext = new FetchSourceContext(includes, excludes); - return this; - } - - /** - * Indicate how the _source should be fetched. - */ - public NewSearchSourceBuilder fetchSource(@Nullable FetchSourceContext fetchSourceContext) { - this.fetchSourceContext = fetchSourceContext; - return this; - } - - /** - * Gets the {@link FetchSourceContext} which defines how the _source should - * be fetched. - */ - public FetchSourceContext fetchSource() { - return fetchSourceContext; - } - - /** - * 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 NewSearchSourceBuilder fields(List fields) { - this.fieldNames = fields; - return this; - } - - /** - * Gets the fields to load and return as part of the search request. - */ - public List fields() { - return fieldNames; - } - - /** - * Adds a field to load from the field data cache and return as part of the - * search request. - */ - public NewSearchSourceBuilder fieldDataField(String name) { - if (fieldDataFields == null) { - fieldDataFields = new ArrayList<>(); - } - fieldDataFields.add(name); - return this; - } - - /** - * Gets the field-data fields. - */ - public List fieldDataFields() { - return fieldDataFields; - } - - /** - * Adds a script field under the given name with the provided script. - * - * @param name - * The name of the field - * @param script - * The script - */ - public NewSearchSourceBuilder scriptField(String name, Script script) { - if (scriptFields == null) { - scriptFields = new ArrayList<>(); - } - scriptFields.add(new ScriptField(name, script)); - return this; - } - - /** - * Gets the script fields. - */ - public List scriptFields() { - return scriptFields; - } - - /** - * 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 NewSearchSourceBuilder indexBoost(String index, float indexBoost) { - if (this.indexBoost == null) { - this.indexBoost = new ObjectFloatHashMap<>(); - } - this.indexBoost.put(index, indexBoost); - return this; - } - - /** - * Gets the boost a specific indices will receive when the query is - * executeed against them. - */ - public ObjectFloatHashMap indexBoost() { - return indexBoost; - } - - /** - * The stats groups this request will be aggregated under. - */ - public NewSearchSourceBuilder stats(String... statsGroups) { - this.stats = statsGroups; - return this; - } - - /** - * The stats groups this request will be aggregated under. - */ - public String[] stats() { - return stats; - } - - public NewSearchSourceBuilder fromXContent(XContentParser parser, QueryParseContext context) throws IOException { - NewSearchSourceBuilder builder = new NewSearchSourceBuilder(); - XContentParser.Token token; - String currentFieldName = null; - if ((token = parser.nextToken()) != XContentParser.Token.START_OBJECT) { - throw new QueryParsingException(context, "Expected [" + XContentParser.Token.START_OBJECT + "] but found [" + token + "]", - parser.getTokenLocation()); - } - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token.isValue()) { - if (context.parseFieldMatcher().match(currentFieldName, FROM_FIELD)) { - builder.from = parser.intValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, SIZE_FIELD)) { - builder.size = parser.intValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, TIMEOUT_FIELD)) { - builder.timeoutInMillis = parser.longValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, TERMINATE_AFTER_FIELD)) { - builder.terminateAfter = parser.intValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, MIN_SCORE_FIELD)) { - builder.minScore = parser.floatValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, VERSION_FIELD)) { - builder.version = parser.booleanValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, EXPLAIN_FIELD)) { - builder.explain = parser.booleanValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, TRACK_SCORES_FIELD)) { - builder.trackScores = parser.booleanValue(); - } else if (context.parseFieldMatcher().match(currentFieldName, _SOURCE_FIELD)) { - FetchSourceContext fetchSourceContext = FetchSourceContext.parse(parser, context); - builder.fetchSourceContext = fetchSourceContext; - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation()); - } - } else if (token == XContentParser.Token.START_OBJECT) { - if (context.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) { - builder.queryBuilder = context.parseInnerQueryBuilder(); - } else if (context.parseFieldMatcher().match(currentFieldName, POST_FILTER_FIELD)) { - builder.postQueryBuilder = context.parseInnerQueryBuilder(); - } else if (context.parseFieldMatcher().match(currentFieldName, _SOURCE_FIELD)) { - FetchSourceContext fetchSourceContext = FetchSourceContext.parse(parser, context); - builder.fetchSourceContext = fetchSourceContext; - } else if (context.parseFieldMatcher().match(currentFieldName, SCRIPT_FIELDS_FIELD)) { - List scriptFields = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - String scriptFieldName = parser.currentName(); - token = parser.nextToken(); - if (token == XContentParser.Token.START_OBJECT) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token.isValue()) { - if (context.parseFieldMatcher().match(currentFieldName, SCRIPT_FIELD)) { - scriptFields - .add(new ScriptField(scriptFieldName, Script.parse(parser, context.parseFieldMatcher()))); - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName - + "].", parser.getTokenLocation()); - } - } else if (token == XContentParser.Token.START_OBJECT) { - if (context.parseFieldMatcher().match(currentFieldName, SCRIPT_FIELD)) { - scriptFields - .add(new ScriptField(scriptFieldName, Script.parse(parser, context.parseFieldMatcher()))); - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName - + "].", parser.getTokenLocation()); - } - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName - + "].", parser.getTokenLocation()); - } - } - } else { - throw new QueryParsingException(context, "Expected [" + XContentParser.Token.START_OBJECT + "] in [" - + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); - } - } - builder.scriptFields = scriptFields; - } else if (context.parseFieldMatcher().match(currentFieldName, INDICES_BOOST_FIELD)) { - ObjectFloatHashMap indexBoost = new ObjectFloatHashMap(); - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token.isValue()) { - indexBoost.put(currentFieldName, parser.floatValue()); - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation()); - } - } - builder.indexBoost = indexBoost; - } else if (context.parseFieldMatcher().match(currentFieldName, AGGREGATIONS_FIELD)) { - List aggregations = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - currentFieldName = parser.currentName(); - token = parser.nextToken(); - if (token == XContentParser.Token.START_OBJECT) { - XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()); - xContentBuilder.startObject(); - xContentBuilder.field(currentFieldName); - xContentBuilder.copyCurrentStructure(parser); - xContentBuilder.endObject(); - aggregations.add(xContentBuilder.bytes()); - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation()); - } - } - builder.aggregations = aggregations; - } else if (context.parseFieldMatcher().match(currentFieldName, HIGHLIGHT_FIELD)) { - XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); - builder.highlightBuilder = xContentBuilder.bytes(); - } else if (context.parseFieldMatcher().match(currentFieldName, INNER_HITS_FIELD)) { - XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); - builder.innerHitsBuilder = xContentBuilder.bytes(); - } else if (context.parseFieldMatcher().match(currentFieldName, SUGGEST_FIELD)) { - XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); - builder.suggestBuilder = xContentBuilder.bytes(); - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation()); - } - } else if (token == XContentParser.Token.START_ARRAY) { - if (context.parseFieldMatcher().match(currentFieldName, FIELDS_FIELD)) { - List fieldNames = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token == XContentParser.Token.VALUE_STRING) { - fieldNames.add(parser.text()); - } else { - throw new QueryParsingException(context, "Expected [" + XContentParser.Token.VALUE_STRING + "] in [" - + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); - } - } - builder.fieldNames = fieldNames; - } else if (context.parseFieldMatcher().match(currentFieldName, FIELDDATA_FIELDS_FIELD)) { - List fieldDataFields = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token == XContentParser.Token.VALUE_STRING) { - fieldDataFields.add(parser.text()); - } else { - throw new QueryParsingException(context, "Expected [" + XContentParser.Token.VALUE_STRING + "] in [" - + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); - } - } - builder.fieldDataFields = fieldDataFields; - } else if (context.parseFieldMatcher().match(currentFieldName, SORT_FIELD)) { - List sorts = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); - sorts.add(xContentBuilder.bytes()); - } - builder.sorts = sorts; - } else if (context.parseFieldMatcher().match(currentFieldName, RESCORE_FIELD)) { - List rescoreBuilders = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); - rescoreBuilders.add(xContentBuilder.bytes()); - } - builder.rescoreBuilders = rescoreBuilders; - } else if (context.parseFieldMatcher().match(currentFieldName, STATS_FIELD)) { - List stats = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token == XContentParser.Token.VALUE_STRING) { - stats.add(parser.text()); - } else { - throw new QueryParsingException(context, "Expected [" + XContentParser.Token.VALUE_STRING + "] in [" - + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); - } - } - builder.stats = stats.toArray(new String[stats.size()]); - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation()); - } - } else { - throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", - parser.getTokenLocation()); - } - } - return builder; - } - - @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_FIELD.getPreferredName(), from); - } - if (size != -1) { - builder.field(SIZE_FIELD.getPreferredName(), size); - } - - if (timeoutInMillis != -1) { - builder.field(TIMEOUT_FIELD.getPreferredName(), timeoutInMillis); - } - - if (terminateAfter != SearchContext.DEFAULT_TERMINATE_AFTER) { - builder.field(TERMINATE_AFTER_FIELD.getPreferredName(), terminateAfter); - } - - if (queryBuilder != null) { - builder.field(QUERY_FIELD.getPreferredName(), queryBuilder); - } - - if (postQueryBuilder != null) { - builder.field(POST_FILTER_FIELD.getPreferredName(), postQueryBuilder); - } - - if (minScore != null) { - builder.field(MIN_SCORE_FIELD.getPreferredName(), minScore); - } - - if (version != null) { - builder.field(VERSION_FIELD.getPreferredName(), version); - } - - if (explain != null) { - builder.field(EXPLAIN_FIELD.getPreferredName(), explain); - } - - if (fetchSourceContext != null) { - builder.field(_SOURCE_FIELD.getPreferredName(), fetchSourceContext); - } - - if (fieldNames != null) { - if (fieldNames.size() == 1) { - builder.field(FIELDS_FIELD.getPreferredName(), fieldNames.get(0)); - } else { - builder.startArray(FIELDS_FIELD.getPreferredName()); - for (String fieldName : fieldNames) { - builder.value(fieldName); - } - builder.endArray(); - } - } - - if (fieldDataFields != null) { - builder.startArray(FIELDDATA_FIELDS_FIELD.getPreferredName()); - for (String fieldDataField : fieldDataFields) { - builder.value(fieldDataField); - } - builder.endArray(); - } - - if (scriptFields != null) { - builder.startObject(SCRIPT_FIELDS_FIELD.getPreferredName()); - for (ScriptField scriptField : scriptFields) { - builder.startObject(scriptField.fieldName()); - builder.field("script", scriptField.script()); - builder.endObject(); - } - builder.endObject(); - } - - if (sorts != null) { - builder.startArray(SORT_FIELD.getPreferredName()); - for (BytesReference sort : sorts) { - XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(sort); - parser.nextToken(); - builder.copyCurrentStructure(parser); - } - builder.endArray(); - } - - if (trackScores) { - builder.field(TRACK_SCORES_FIELD.getPreferredName(), true); - } - - if (indexBoost != null) { - builder.startObject(INDICES_BOOST_FIELD.getPreferredName()); - 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_FIELD.getPreferredName()); - builder.startObject(); - for (BytesReference aggregation : aggregations) { - XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(aggregation); - parser.nextToken(); - parser.nextToken(); - builder.copyCurrentStructure(parser); - } - builder.endObject(); - } - - if (highlightBuilder != null) { - builder.field(HIGHLIGHT_FIELD.getPreferredName()); - XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(highlightBuilder); - parser.nextToken(); - builder.copyCurrentStructure(parser); - } - - if (innerHitsBuilder != null) { - builder.field(INNER_HITS_FIELD.getPreferredName()); - XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(innerHitsBuilder); - parser.nextToken(); - builder.copyCurrentStructure(parser); - } - - if (suggestBuilder != null) { - builder.field(SUGGEST_FIELD.getPreferredName()); - XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(suggestBuilder); - parser.nextToken(); - builder.copyCurrentStructure(parser); - } - - if (rescoreBuilders != null) { - builder.startArray(RESCORE_FIELD.getPreferredName()); - for (BytesReference rescoreBuilder : rescoreBuilders) { - XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(rescoreBuilder); - parser.nextToken(); - builder.copyCurrentStructure(parser); - } - builder.endArray(); - } - - if (stats != null) { - builder.array(STATS_FIELD.getPreferredName(), stats); - } - } - - private static class ScriptField implements Writeable, ToXContent { - - public static final ScriptField PROTOTYPE = new ScriptField(null, null); - - 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; - } - - @Override - public ScriptField readFrom(StreamInput in) throws IOException { - return new ScriptField(in.readString(), Script.readScript(in)); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeString(fieldName); - script.writeTo(out); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(fieldName); - builder.field(SCRIPT_FIELD.getPreferredName(), script); - builder.endObject(); - return builder; - } - - @Override - public int hashCode() { - return Objects.hash(fieldName, script); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ScriptField other = (ScriptField) obj; - return Objects.equals(fieldName, other.fieldName) && Objects.equals(script, other.script); - } - } - - @Override - public NewSearchSourceBuilder readFrom(StreamInput in) throws IOException { - NewSearchSourceBuilder builder = new NewSearchSourceBuilder(); - if (in.readBoolean()) { - int size = in.readVInt(); - List aggregations = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - aggregations.add(in.readBytesReference()); - } - builder.aggregations = aggregations; - } - if (in.readBoolean()) { - builder.defaultRescoreWindowSize = in.readVInt(); - } - builder.explain = in.readOptionalBoolean(); - builder.fetchSourceContext = FetchSourceContext.optionalReadFromStream(in); - boolean hasFieldDataFields = in.readBoolean(); - if (hasFieldDataFields) { - int size = in.readVInt(); - List fieldDataFields = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - fieldDataFields.add(in.readString()); - } - builder.fieldDataFields = fieldDataFields; - } - boolean hasFieldNames = in.readBoolean(); - if (hasFieldNames) { - int size = in.readVInt(); - List fieldNames = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - fieldNames.add(in.readString()); - } - builder.fieldNames = fieldNames; - } - builder.from = in.readVInt(); - if (in.readBoolean()) { - builder.highlightBuilder = in.readBytesReference(); - } - boolean hasIndexBoost = in.readBoolean(); - if (hasIndexBoost) { - int size = in.readVInt(); - ObjectFloatHashMap indexBoost = new ObjectFloatHashMap(size); - for (int i = 0; i < size; i++) { - indexBoost.put(in.readString(), in.readFloat()); - } - builder.indexBoost = indexBoost; - } - if (in.readBoolean()) { - builder.innerHitsBuilder = in.readBytesReference(); - } - if (in.readBoolean()) { - builder.minScore = in.readFloat(); - } - if (in.readBoolean()) { - builder.postQueryBuilder = in.readQuery(); - } - if (in.readBoolean()) { - builder.queryBuilder = in.readQuery(); - } - if (in.readBoolean()) { - int size = in.readVInt(); - List rescoreBuilders = new ArrayList<>(); - for (int i = 0; i < size; i++) { - rescoreBuilders.add(in.readBytesReference()); - } - builder.rescoreBuilders = rescoreBuilders; - } - if (in.readBoolean()) { - int size = in.readVInt(); - List scriptFields = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - scriptFields.add(ScriptField.PROTOTYPE.readFrom(in)); - } - builder.scriptFields = scriptFields; - } - builder.size = in.readVInt(); - if (in.readBoolean()) { - int size = in.readVInt(); - List sorts = new ArrayList<>(); - for (int i = 0; i < size; i++) { - sorts.add(in.readBytesReference()); - } - builder.sorts = sorts; - } - if (in.readBoolean()) { - builder.stats = in.readStringArray(); - } - if (in.readBoolean()) { - builder.suggestBuilder = in.readBytesReference(); - } - builder.terminateAfter = in.readVInt(); - builder.timeoutInMillis = in.readLong(); - builder.trackScores = in.readBoolean(); - builder.version = in.readOptionalBoolean(); - return builder; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - boolean hasAggregations = aggregations != null; - out.writeBoolean(hasAggregations); - if (hasAggregations) { - out.writeVInt(aggregations.size()); - for (BytesReference aggregation : aggregations) { - out.writeBytesReference(aggregation); - } - } - boolean hasDefaultRescoreWindowSize = defaultRescoreWindowSize != null; - out.writeBoolean(hasDefaultRescoreWindowSize); - if (hasDefaultRescoreWindowSize) { - out.writeVInt(defaultRescoreWindowSize); - } - out.writeOptionalBoolean(explain); - FetchSourceContext.optionalWriteToStream(fetchSourceContext, out); - boolean hasFieldDataFields = fieldDataFields != null; - out.writeBoolean(hasFieldDataFields); - if (hasFieldDataFields) { - out.writeVInt(fieldDataFields.size()); - for (String field : fieldDataFields) { - out.writeString(field); - } - } - boolean hasFieldNames = fieldNames != null; - out.writeBoolean(hasFieldNames); - if (hasFieldNames) { - out.writeVInt(fieldNames.size()); - for (String field : fieldNames) { - out.writeString(field); - } - } - out.writeVInt(from); - boolean hasHighlightBuilder = highlightBuilder != null; - out.writeBoolean(hasHighlightBuilder); - if (hasHighlightBuilder) { - out.writeBytesReference(highlightBuilder); - } - boolean hasIndexBoost = indexBoost != null; - out.writeBoolean(hasIndexBoost); - if (hasIndexBoost) { - out.writeVInt(indexBoost.size()); - for (ObjectCursor key : indexBoost.keys()) { - out.writeString(key.value); - out.writeFloat(indexBoost.get(key.value)); - } - } - boolean hasInnerHitsBuilder = innerHitsBuilder != null; - out.writeBoolean(hasInnerHitsBuilder); - if (hasInnerHitsBuilder) { - out.writeBytesReference(innerHitsBuilder); - } - boolean hasMinScore = minScore != null; - out.writeBoolean(hasMinScore); - if (hasMinScore) { - out.writeFloat(minScore); - } - boolean hasPostQuery = postQueryBuilder != null; - out.writeBoolean(hasPostQuery); - if (hasPostQuery) { - out.writeQuery(postQueryBuilder); - } - boolean hasQuery = queryBuilder != null; - out.writeBoolean(hasQuery); - if (hasQuery) { - out.writeQuery(queryBuilder); - } - boolean hasRescoreBuilders = rescoreBuilders != null; - out.writeBoolean(hasRescoreBuilders); - if (hasRescoreBuilders) { - out.writeVInt(rescoreBuilders.size()); - for (BytesReference rescoreBuilder : rescoreBuilders) { - out.writeBytesReference(rescoreBuilder); - } - } - boolean hasScriptFields = scriptFields != null; - out.writeBoolean(hasScriptFields); - if (hasScriptFields) { - out.writeVInt(scriptFields.size()); - for (ScriptField scriptField : scriptFields) { - scriptField.writeTo(out); - } - } - out.writeVInt(size); - boolean hasSorts = sorts != null; - out.writeBoolean(hasSorts); - if (hasSorts) { - out.writeVInt(sorts.size()); - for (BytesReference sort : sorts) { - out.writeBytesReference(sort); - } - } - boolean hasStats = stats != null; - out.writeBoolean(hasStats); - if (hasStats) { - out.writeStringArray(stats); - } - boolean hasSuggestBuilder = suggestBuilder != null; - out.writeBoolean(hasSuggestBuilder); - if (hasSuggestBuilder) { - out.writeBytesReference(suggestBuilder); - } - out.writeVInt(terminateAfter); - out.writeLong(timeoutInMillis); - out.writeBoolean(trackScores); - out.writeOptionalBoolean(version); - } - - @Override - public int hashCode() { - return Objects.hash(aggregations, defaultRescoreWindowSize, explain, fetchSourceContext, fieldDataFields, fieldNames, from, - highlightBuilder, indexBoost, innerHitsBuilder, minScore, postQueryBuilder, queryBuilder, rescoreBuilders, scriptFields, - size, sorts, Arrays.hashCode(stats), suggestBuilder, terminateAfter, timeoutInMillis, trackScores, version); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (obj.getClass() != getClass()) { - return false; - } - NewSearchSourceBuilder other = (NewSearchSourceBuilder) obj; - return Objects.equals(aggregations, other.aggregations) - && Objects.equals(defaultRescoreWindowSize, other.defaultRescoreWindowSize) - && Objects.equals(explain, other.explain) - && Objects.equals(fetchSourceContext, other.fetchSourceContext) - && Objects.equals(fieldDataFields, other.fieldDataFields) - && Objects.equals(fieldNames, other.fieldNames) - && Objects.equals(from, other.from) - && Objects.equals(highlightBuilder, other.highlightBuilder) - && Objects.equals(indexBoost, other.indexBoost) - && Objects.equals(innerHitsBuilder, other.innerHitsBuilder) - && Objects.equals(minScore, other.minScore) - && Objects.equals(postQueryBuilder, other.postQueryBuilder) - && Objects.equals(queryBuilder, other.queryBuilder) - && Objects.equals(rescoreBuilders, other.rescoreBuilders) - && Objects.equals(scriptFields, other.scriptFields) - && Objects.equals(size, other.size) - && Objects.equals(sorts, other.sorts) - && Objects.deepEquals(stats, other.stats) - && Objects.equals(suggestBuilder, other.suggestBuilder) - && Objects.equals(terminateAfter, other.terminateAfter) - && Objects.equals(timeoutInMillis, other.timeoutInMillis) - && Objects.equals(trackScores, other.trackScores) - && Objects.equals(version, other.version); - } -} diff --git a/core/src/main/java/org/elasticsearch/search/builder/OldSearchSourceBuilder.java b/core/src/main/java/org/elasticsearch/search/builder/OldSearchSourceBuilder.java new file mode 100644 index 00000000000..8e8f2316860 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/builder/OldSearchSourceBuilder.java @@ -0,0 +1,843 @@ +/* + * 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()} + * . + * + * @see org.elasticsearch.action.search.SearchRequest#source(OldSearchSourceBuilder) + */ +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/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index 3b87030fec8..f38a09cca69 100644 --- a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -20,19 +20,25 @@ package org.elasticsearch.search.builder; import com.carrotsearch.hppc.ObjectFloatHashMap; -import java.nio.charset.StandardCharsets; -import org.elasticsearch.ElasticsearchGenerationException; -import org.elasticsearch.action.support.QuerySourceBuilder; +import com.carrotsearch.hppc.cursors.ObjectCursor; + import org.elasticsearch.action.support.ToXContentToBytes; -import org.elasticsearch.client.Requests; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +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.QueryParsingException; import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.fetch.innerhits.InnerHitsBuilder; @@ -47,19 +53,48 @@ import org.elasticsearch.search.suggest.SuggestBuilder; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.util.Iterator; import java.util.List; -import java.util.Map; +import java.util.Objects; /** * A search source builder allowing to easily build search source. Simple * construction using - * {@link org.elasticsearch.search.builder.SearchSourceBuilder#searchSource()}. + * {@link org.elasticsearch.search.builder.NewSearchSourceBuilder#searchSource()}. * - * @see org.elasticsearch.action.search.SearchRequest#source(SearchSourceBuilder) + * @see org.elasticsearch.action.search.SearchRequest#source(NewSearchSourceBuilder) */ -public class SearchSourceBuilder extends ToXContentToBytes { +/** + * + */ +public final class SearchSourceBuilder extends ToXContentToBytes implements Writeable { + + public static final ParseField FROM_FIELD = new ParseField("from"); + public static final ParseField SIZE_FIELD = new ParseField("size"); + public static final ParseField TIMEOUT_FIELD = new ParseField("timeout"); + public static final ParseField TERMINATE_AFTER_FIELD = new ParseField("terminate_after"); + public static final ParseField QUERY_FIELD = new ParseField("query"); + public static final ParseField POST_FILTER_FIELD = new ParseField("post_filter"); + public static final ParseField MIN_SCORE_FIELD = new ParseField("min_score"); + public static final ParseField VERSION_FIELD = new ParseField("version"); + public static final ParseField EXPLAIN_FIELD = new ParseField("explain"); + public static final ParseField _SOURCE_FIELD = new ParseField("_source"); + public static final ParseField FIELDS_FIELD = new ParseField("fields"); + public static final ParseField FIELDDATA_FIELDS_FIELD = new ParseField("fielddata_fields"); + public static final ParseField SCRIPT_FIELDS_FIELD = new ParseField("script_fields"); + public static final ParseField SCRIPT_FIELD = new ParseField("script"); + public static final ParseField SORT_FIELD = new ParseField("sort"); + public static final ParseField TRACK_SCORES_FIELD = new ParseField("track_scores"); + public static final ParseField INDICES_BOOST_FIELD = new ParseField("indices_boost"); + public static final ParseField AGGREGATIONS_FIELD = new ParseField("aggregations"); + public static final ParseField HIGHLIGHT_FIELD = new ParseField("highlight"); + public static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits"); + public static final ParseField SUGGEST_FIELD = new ParseField("suggest"); + public static final ParseField RESCORE_FIELD = new ParseField("rescore"); + public static final ParseField STATS_FIELD = new ParseField("stats"); + + public static final SearchSourceBuilder PROTOTYPE = new SearchSourceBuilder(); /** * A static factory method to construct a new search source. @@ -75,11 +110,9 @@ public class SearchSourceBuilder extends ToXContentToBytes { return new HighlightBuilder(); } - private QuerySourceBuilder querySourceBuilder; + private QueryBuilder queryBuilder; - private QueryBuilder postQueryBuilder; - - private BytesReference filterBinary; + private QueryBuilder postQueryBuilder; private int from = -1; @@ -89,7 +122,7 @@ public class SearchSourceBuilder extends ToXContentToBytes { private Boolean version; - private List sorts; + private List sorts; private boolean trackScores = false; @@ -103,16 +136,15 @@ public class SearchSourceBuilder extends ToXContentToBytes { private List scriptFields; private FetchSourceContext fetchSourceContext; - private List aggregations; - private BytesReference aggregationsBinary; + private List aggregations; - private HighlightBuilder highlightBuilder; + private BytesReference highlightBuilder; - private SuggestBuilder suggestBuilder; + private BytesReference suggestBuilder; - private InnerHitsBuilder innerHitsBuilder; + private BytesReference innerHitsBuilder; - private List rescoreBuilders; + private List rescoreBuilders; private Integer defaultRescoreWindowSize; private ObjectFloatHashMap indexBoost = null; @@ -126,77 +158,20 @@ public class SearchSourceBuilder extends ToXContentToBytes { } /** - * Sets the query provided as a {@link QuerySourceBuilder} - */ - public SearchSourceBuilder query(QuerySourceBuilder querySourceBuilder) { - this.querySourceBuilder = querySourceBuilder; - return this; - } - - /** - * Constructs a new search source builder with a search query. + * Sets the search query for this request. * * @see org.elasticsearch.index.query.QueryBuilders */ - public SearchSourceBuilder query(QueryBuilder query) { - if (this.querySourceBuilder == null) { - this.querySourceBuilder = new QuerySourceBuilder(); - } - this.querySourceBuilder.setQuery(query); + public SearchSourceBuilder query(QueryBuilder query) { + this.queryBuilder = query; return this; } /** - * Constructs a new search source builder with a raw search query. + * Gets the query for this request */ - public SearchSourceBuilder query(byte[] queryBinary) { - return query(queryBinary, 0, queryBinary.length); - } - - /** - * Constructs a new search source builder with a raw search query. - */ - public SearchSourceBuilder 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 SearchSourceBuilder 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 SearchSourceBuilder query(String queryString) { - return query(queryString.getBytes(StandardCharsets.UTF_8)); - } - - /** - * Constructs a new search source builder with a query from a builder. - */ - public SearchSourceBuilder query(XContentBuilder query) { - return query(query.bytes()); - } - - /** - * Constructs a new search source builder with a query from a map. - */ - @SuppressWarnings("unchecked") - public SearchSourceBuilder 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); - } + public QueryBuilder query() { + return queryBuilder; } /** @@ -204,63 +179,16 @@ public class SearchSourceBuilder extends ToXContentToBytes { * only has affect on the search hits (not aggregations). This filter is * always executed as last filtering mechanism. */ - public SearchSourceBuilder postFilter(QueryBuilder postFilter) { + public SearchSourceBuilder 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). + * Gets the post filter for this request */ - public SearchSourceBuilder 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 SearchSourceBuilder 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 SearchSourceBuilder 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 SearchSourceBuilder postFilter(BytesReference postFilterBinary) { - this.filterBinary = postFilterBinary; - return this; - } - - /** - * Constructs a new search source builder with a query from a builder. - */ - public SearchSourceBuilder postFilter(XContentBuilder postFilter) { - return postFilter(postFilter.bytes()); - } - - /** - * Constructs a new search source builder with a query from a map. - */ - @SuppressWarnings("unchecked") - public SearchSourceBuilder 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); - } + public QueryBuilder postFilter() { + return postQueryBuilder; } /** @@ -271,6 +199,13 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the from index to start the search from. + **/ + public int from() { + return from; + } + /** * The number of search hits to return. Defaults to 10. */ @@ -279,6 +214,13 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the number of search hits to return. + */ + public int size() { + return size; + } + /** * Sets the minimum score below which docs will be filtered out. */ @@ -287,6 +229,13 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the minimum score below which docs will be filtered out. + */ + public float minScore() { + return minScore; + } + /** * Should each {@link org.elasticsearch.search.SearchHit} be returned with * an explanation of the hit (ranking). @@ -296,6 +245,14 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Indicates whether each search hit will be returned with an explanation of + * the hit (ranking) + */ + public Boolean explain() { + return explain; + } + /** * Should each {@link org.elasticsearch.search.SearchHit} be returned with a * version associated with it. @@ -305,6 +262,14 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Indicates whether the document's version will be included in the search + * hits. + */ + public Boolean version() { + return version; + } + /** * An optional timeout to control how long search is allowed to take. */ @@ -314,11 +279,10 @@ public class SearchSourceBuilder extends ToXContentToBytes { } /** - * An optional timeout to control how long search is allowed to take. + * Gets the timeout to control how long search is allowed to take. */ - public SearchSourceBuilder timeout(String timeout) { - this.timeoutInMillis = TimeValue.parseTimeValue(timeout, null, getClass().getSimpleName() + ".timeout").millis(); - return this; + public long timeoutInMillis() { + return timeoutInMillis; } /** @@ -333,6 +297,13 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the number of documents to terminate after collecting. + */ + public int terminateAfter() { + return terminateAfter; + } + /** * Adds a sort against the given field name and the sort ordering. * @@ -350,6 +321,7 @@ public class SearchSourceBuilder extends ToXContentToBytes { * * @param name * The name of the field to sort by + * @throws IOException */ public SearchSourceBuilder sort(String name) { return sort(SortBuilders.fieldSort(name)); @@ -359,11 +331,26 @@ public class SearchSourceBuilder extends ToXContentToBytes { * Adds a sort builder. */ public SearchSourceBuilder sort(SortBuilder sort) { - if (sorts == null) { - sorts = new ArrayList<>(); + try { + if (sorts == null) { + sorts = new ArrayList<>(); + } + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + sort.toXContent(builder, EMPTY_PARAMS); + builder.endObject(); + sorts.add(builder.bytes()); + return this; + } catch (IOException e) { + throw new RuntimeException(e); } - sorts.add(sort); - return this; + } + + /** + * Gets the bytes representing the sort builders for this request. + */ + public List sorts() { + return sorts; } /** @@ -376,43 +363,36 @@ public class SearchSourceBuilder extends ToXContentToBytes { } /** - * Add an get to perform as part of the search. + * Indicates whether scores will be tracked for this request. + */ + public boolean trackScores() { + return trackScores; + } + + /** + * Add an aggregation to perform as part of the search. */ public SearchSourceBuilder aggregation(AbstractAggregationBuilder aggregation) { - if (aggregations == null) { - aggregations = new ArrayList<>(); + try { + if (aggregations == null) { + aggregations = new ArrayList<>(); + } + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + aggregation.toXContent(builder, EMPTY_PARAMS); + builder.endObject(); + aggregations.add(builder.bytes()); + return this; + } catch (IOException e) { + throw new RuntimeException(e); } - aggregations.add(aggregation); - return this; } /** - * Sets a raw (xcontent / json) addAggregation. + * Gets the bytes representing the aggregation builders for this request. */ - public SearchSourceBuilder aggregations(byte[] aggregationsBinary) { - return aggregations(aggregationsBinary, 0, aggregationsBinary.length); - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - public SearchSourceBuilder aggregations(byte[] aggregationsBinary, int aggregationsBinaryOffset, int aggregationsBinaryLength) { - return aggregations(new BytesArray(aggregationsBinary, aggregationsBinaryOffset, aggregationsBinaryLength)); - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - public SearchSourceBuilder aggregations(BytesReference aggregationsBinary) { - this.aggregationsBinary = aggregationsBinary; - return this; - } - - /** - * Sets a raw (xcontent / json) addAggregation. - */ - public SearchSourceBuilder aggregations(XContentBuilder aggs) { - return aggregations(aggs.bytes()); + public List aggregations() { + return aggregations; } /** @@ -424,54 +404,89 @@ public class SearchSourceBuilder extends ToXContentToBytes { } /** - * Sets a raw (xcontent / json) addAggregation. + * Get the rescore window size for rescores that don't specify their window. */ - @SuppressWarnings("unchecked") - public SearchSourceBuilder 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; + public int defaultRescoreWindowSize() { + return defaultRescoreWindowSize; } /** * Adds highlight to perform as part of the search. */ - public SearchSourceBuilder highlight(HighlightBuilder highlightBuilder) { - this.highlightBuilder = highlightBuilder; - return this; + public SearchSourceBuilder highlighter(HighlightBuilder highlightBuilder) { + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + highlightBuilder.innerXContent(builder, EMPTY_PARAMS); + builder.endObject(); + this.highlightBuilder = builder.bytes(); + return this; + } catch (IOException e) { + throw new RuntimeException(e); + } } - public InnerHitsBuilder innerHitsBuilder() { - if (innerHitsBuilder == null) { - innerHitsBuilder = new InnerHitsBuilder(); + /** + * Gets the bytes representing the hightlighter builder for this request. + */ + public BytesReference highlighter() { + return highlightBuilder; + } + + public SearchSourceBuilder innerHits(InnerHitsBuilder innerHitsBuilder) { + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + innerHitsBuilder.innerXContent(builder, EMPTY_PARAMS); + builder.endObject(); + this.innerHitsBuilder = builder.bytes(); + return this; + } catch (IOException e) { + throw new RuntimeException(e); } + } + + /** + * Gets the bytes representing the inner hits builder for this request. + */ + public BytesReference innerHits() { return innerHitsBuilder; } - public SuggestBuilder suggest() { - if (suggestBuilder == null) { - suggestBuilder = new SuggestBuilder("suggest"); + public SearchSourceBuilder suggest(SuggestBuilder suggestBuilder) { + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + suggestBuilder.toXContent(builder, EMPTY_PARAMS); + builder.endObject(); + this.suggestBuilder = builder.bytes(); + return this; + } catch (IOException e) { + throw new RuntimeException(e); } + } + + /** + * Gets the bytes representing the suggester builder for this request. + */ + public BytesReference suggest() { return suggestBuilder; } public SearchSourceBuilder addRescorer(RescoreBuilder rescoreBuilder) { - if (rescoreBuilders == null) { - rescoreBuilders = new ArrayList<>(); + try { + if (rescoreBuilders == null) { + rescoreBuilders = new ArrayList<>(); + } + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + rescoreBuilder.toXContent(builder, EMPTY_PARAMS); + builder.endObject(); + rescoreBuilders.add(builder.bytes()); + return this; + } catch (IOException e) { + throw new RuntimeException(e); } - rescoreBuilders.add(rescoreBuilder); - return this; } public SearchSourceBuilder clearRescorers() { @@ -479,6 +494,13 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the bytes representing the rescore builders for this request. + */ + public List rescores() { + return rescoreBuilders; + } + /** * Indicates whether the response should contain the stored _source for * every hit @@ -535,33 +557,11 @@ public class SearchSourceBuilder extends ToXContentToBytes { } /** - * Sets no fields to be loaded, resulting in only id and type to be returned - * per field. + * Gets the {@link FetchSourceContext} which defines how the _source should + * be fetched. */ - public SearchSourceBuilder 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 SearchSourceBuilder 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 SearchSourceBuilder fields(String... fields) { - if (fieldNames == null) { - fieldNames = new ArrayList<>(); - } - Collections.addAll(fieldNames, fields); - return this; + public FetchSourceContext fetchSource() { + return fetchSourceContext; } /** @@ -577,6 +577,31 @@ public class SearchSourceBuilder extends ToXContentToBytes { 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 SearchSourceBuilder fields(List fields) { + this.fieldNames = fields; + return this; + } + + /** + * Sets no fields to be loaded, resulting in only id and type to be returned + * per field. + */ + public SearchSourceBuilder noFields() { + this.fieldNames = Collections.emptyList(); + return this; + } + + /** + * Gets the fields to load and return as part of the search request. + */ + public List fields() { + return fieldNames; + } + /** * Adds a field to load from the field data cache and return as part of the * search request. @@ -589,6 +614,13 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the field-data fields. + */ + public List fieldDataFields() { + return fieldDataFields; + } + /** * Adds a script field under the given name with the provided script. * @@ -605,6 +637,13 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the script fields. + */ + public List scriptFields() { + return scriptFields; + } + /** * Sets the boost a specific index will receive when the query is executeed * against it. @@ -622,6 +661,14 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * Gets the boost a specific indices will receive when the query is + * executeed against them. + */ + public ObjectFloatHashMap indexBoost() { + return indexBoost; + } + /** * The stats groups this request will be aggregated under. */ @@ -630,6 +677,196 @@ public class SearchSourceBuilder extends ToXContentToBytes { return this; } + /** + * The stats groups this request will be aggregated under. + */ + public String[] stats() { + return stats; + } + + public SearchSourceBuilder fromXContent(XContentParser parser, QueryParseContext context) throws IOException { + SearchSourceBuilder builder = new SearchSourceBuilder(); + XContentParser.Token token; + String currentFieldName = null; + if ((token = parser.nextToken()) != XContentParser.Token.START_OBJECT) { + throw new QueryParsingException(context, "Expected [" + XContentParser.Token.START_OBJECT + "] but found [" + token + "]", + parser.getTokenLocation()); + } + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (context.parseFieldMatcher().match(currentFieldName, FROM_FIELD)) { + builder.from = parser.intValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, SIZE_FIELD)) { + builder.size = parser.intValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, TIMEOUT_FIELD)) { + builder.timeoutInMillis = parser.longValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, TERMINATE_AFTER_FIELD)) { + builder.terminateAfter = parser.intValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, MIN_SCORE_FIELD)) { + builder.minScore = parser.floatValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, VERSION_FIELD)) { + builder.version = parser.booleanValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, EXPLAIN_FIELD)) { + builder.explain = parser.booleanValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, TRACK_SCORES_FIELD)) { + builder.trackScores = parser.booleanValue(); + } else if (context.parseFieldMatcher().match(currentFieldName, _SOURCE_FIELD)) { + FetchSourceContext fetchSourceContext = FetchSourceContext.parse(parser, context); + builder.fetchSourceContext = fetchSourceContext; + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", + parser.getTokenLocation()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + if (context.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) { + builder.queryBuilder = context.parseInnerQueryBuilder(); + } else if (context.parseFieldMatcher().match(currentFieldName, POST_FILTER_FIELD)) { + builder.postQueryBuilder = context.parseInnerQueryBuilder(); + } else if (context.parseFieldMatcher().match(currentFieldName, _SOURCE_FIELD)) { + FetchSourceContext fetchSourceContext = FetchSourceContext.parse(parser, context); + builder.fetchSourceContext = fetchSourceContext; + } else if (context.parseFieldMatcher().match(currentFieldName, SCRIPT_FIELDS_FIELD)) { + List scriptFields = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + String scriptFieldName = parser.currentName(); + token = parser.nextToken(); + if (token == XContentParser.Token.START_OBJECT) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (context.parseFieldMatcher().match(currentFieldName, SCRIPT_FIELD)) { + scriptFields + .add(new ScriptField(scriptFieldName, Script.parse(parser, context.parseFieldMatcher()))); + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + + "].", parser.getTokenLocation()); + } + } else if (token == XContentParser.Token.START_OBJECT) { + if (context.parseFieldMatcher().match(currentFieldName, SCRIPT_FIELD)) { + scriptFields + .add(new ScriptField(scriptFieldName, Script.parse(parser, context.parseFieldMatcher()))); + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + + "].", parser.getTokenLocation()); + } + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + + "].", parser.getTokenLocation()); + } + } + } else { + throw new QueryParsingException(context, "Expected [" + XContentParser.Token.START_OBJECT + "] in [" + + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); + } + } + builder.scriptFields = scriptFields; + } else if (context.parseFieldMatcher().match(currentFieldName, INDICES_BOOST_FIELD)) { + ObjectFloatHashMap indexBoost = new ObjectFloatHashMap(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + indexBoost.put(currentFieldName, parser.floatValue()); + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", + parser.getTokenLocation()); + } + } + builder.indexBoost = indexBoost; + } else if (context.parseFieldMatcher().match(currentFieldName, AGGREGATIONS_FIELD)) { + List aggregations = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + currentFieldName = parser.currentName(); + token = parser.nextToken(); + if (token == XContentParser.Token.START_OBJECT) { + XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()); + xContentBuilder.startObject(); + xContentBuilder.field(currentFieldName); + xContentBuilder.copyCurrentStructure(parser); + xContentBuilder.endObject(); + aggregations.add(xContentBuilder.bytes()); + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", + parser.getTokenLocation()); + } + } + builder.aggregations = aggregations; + } else if (context.parseFieldMatcher().match(currentFieldName, HIGHLIGHT_FIELD)) { + XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); + builder.highlightBuilder = xContentBuilder.bytes(); + } else if (context.parseFieldMatcher().match(currentFieldName, INNER_HITS_FIELD)) { + XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); + builder.innerHitsBuilder = xContentBuilder.bytes(); + } else if (context.parseFieldMatcher().match(currentFieldName, SUGGEST_FIELD)) { + XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); + builder.suggestBuilder = xContentBuilder.bytes(); + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", + parser.getTokenLocation()); + } + } else if (token == XContentParser.Token.START_ARRAY) { + if (context.parseFieldMatcher().match(currentFieldName, FIELDS_FIELD)) { + List fieldNames = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token == XContentParser.Token.VALUE_STRING) { + fieldNames.add(parser.text()); + } else { + throw new QueryParsingException(context, "Expected [" + XContentParser.Token.VALUE_STRING + "] in [" + + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); + } + } + builder.fieldNames = fieldNames; + } else if (context.parseFieldMatcher().match(currentFieldName, FIELDDATA_FIELDS_FIELD)) { + List fieldDataFields = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token == XContentParser.Token.VALUE_STRING) { + fieldDataFields.add(parser.text()); + } else { + throw new QueryParsingException(context, "Expected [" + XContentParser.Token.VALUE_STRING + "] in [" + + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); + } + } + builder.fieldDataFields = fieldDataFields; + } else if (context.parseFieldMatcher().match(currentFieldName, SORT_FIELD)) { + List sorts = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); + sorts.add(xContentBuilder.bytes()); + } + builder.sorts = sorts; + } else if (context.parseFieldMatcher().match(currentFieldName, RESCORE_FIELD)) { + List rescoreBuilders = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + XContentBuilder xContentBuilder = XContentFactory.contentBuilder(parser.contentType()).copyCurrentStructure(parser); + rescoreBuilders.add(xContentBuilder.bytes()); + } + builder.rescoreBuilders = rescoreBuilders; + } else if (context.parseFieldMatcher().match(currentFieldName, STATS_FIELD)) { + List stats = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token == XContentParser.Token.VALUE_STRING) { + stats.add(parser.text()); + } else { + throw new QueryParsingException(context, "Expected [" + XContentParser.Token.VALUE_STRING + "] in [" + + currentFieldName + "] but found [" + token + "]", parser.getTokenLocation()); + } + } + builder.stats = stats.toArray(new String[stats.size()]); + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", + parser.getTokenLocation()); + } + } else { + throw new QueryParsingException(context, "Unknown key for a " + token + " in [" + currentFieldName + "].", + parser.getTokenLocation()); + } + } + return builder; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -640,65 +877,49 @@ public class SearchSourceBuilder extends ToXContentToBytes { public void innerToXContent(XContentBuilder builder, Params params) throws IOException { if (from != -1) { - builder.field("from", from); + builder.field(FROM_FIELD.getPreferredName(), from); } if (size != -1) { - builder.field("size", size); + builder.field(SIZE_FIELD.getPreferredName(), size); } if (timeoutInMillis != -1) { - builder.field("timeout", timeoutInMillis); + builder.field(TIMEOUT_FIELD.getPreferredName(), timeoutInMillis); } if (terminateAfter != SearchContext.DEFAULT_TERMINATE_AFTER) { - builder.field("terminate_after", terminateAfter); + builder.field(TERMINATE_AFTER_FIELD.getPreferredName(), terminateAfter); } - if (querySourceBuilder != null) { - querySourceBuilder.innerToXContent(builder, params); + if (queryBuilder != null) { + builder.field(QUERY_FIELD.getPreferredName(), queryBuilder); } 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); - } + builder.field(POST_FILTER_FIELD.getPreferredName(), postQueryBuilder); } if (minScore != null) { - builder.field("min_score", minScore); + builder.field(MIN_SCORE_FIELD.getPreferredName(), minScore); } if (version != null) { - builder.field("version", version); + builder.field(VERSION_FIELD.getPreferredName(), version); } if (explain != null) { - builder.field("explain", explain); + builder.field(EXPLAIN_FIELD.getPreferredName(), 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(); - } + builder.field(_SOURCE_FIELD.getPreferredName(), fetchSourceContext); } if (fieldNames != null) { if (fieldNames.size() == 1) { - builder.field("fields", fieldNames.get(0)); + builder.field(FIELDS_FIELD.getPreferredName(), fieldNames.get(0)); } else { - builder.startArray("fields"); + builder.startArray(FIELDS_FIELD.getPreferredName()); for (String fieldName : fieldNames) { builder.value(fieldName); } @@ -707,15 +928,15 @@ public class SearchSourceBuilder extends ToXContentToBytes { } if (fieldDataFields != null) { - builder.startArray("fielddata_fields"); - for (String fieldName : fieldDataFields) { - builder.value(fieldName); + builder.startArray(FIELDDATA_FIELDS_FIELD.getPreferredName()); + for (String fieldDataField : fieldDataFields) { + builder.value(fieldDataField); } builder.endArray(); } if (scriptFields != null) { - builder.startObject("script_fields"); + builder.startObject(SCRIPT_FIELDS_FIELD.getPreferredName()); for (ScriptField scriptField : scriptFields) { builder.startObject(scriptField.fieldName()); builder.field("script", scriptField.script()); @@ -725,21 +946,21 @@ public class SearchSourceBuilder extends ToXContentToBytes { } if (sorts != null) { - builder.startArray("sort"); - for (SortBuilder sort : sorts) { - builder.startObject(); - sort.toXContent(builder, params); - builder.endObject(); + builder.startArray(SORT_FIELD.getPreferredName()); + for (BytesReference sort : sorts) { + XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(sort); + parser.nextToken(); + builder.copyCurrentStructure(parser); } builder.endArray(); } if (trackScores) { - builder.field("track_scores", true); + builder.field(TRACK_SCORES_FIELD.getPreferredName(), true); } if (indexBoost != null) { - builder.startObject("indices_boost"); + builder.startObject(INDICES_BOOST_FIELD.getPreferredName()); assert !indexBoost.containsKey(null); final Object[] keys = indexBoost.keys; final float[] values = indexBoost.values; @@ -752,76 +973,57 @@ public class SearchSourceBuilder extends ToXContentToBytes { } if (aggregations != null) { - builder.field("aggregations"); + builder.field(AGGREGATIONS_FIELD.getPreferredName()); builder.startObject(); - for (AbstractAggregationBuilder aggregation : aggregations) { - aggregation.toXContent(builder, params); + for (BytesReference aggregation : aggregations) { + XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(aggregation); + parser.nextToken(); + parser.nextToken(); + builder.copyCurrentStructure(parser); } 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); + builder.field(HIGHLIGHT_FIELD.getPreferredName()); + XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(highlightBuilder); + parser.nextToken(); + builder.copyCurrentStructure(parser); } if (innerHitsBuilder != null) { - innerHitsBuilder.toXContent(builder, params); + builder.field(INNER_HITS_FIELD.getPreferredName()); + XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(innerHitsBuilder); + parser.nextToken(); + builder.copyCurrentStructure(parser); } if (suggestBuilder != null) { - suggestBuilder.toXContent(builder, params); + builder.field(SUGGEST_FIELD.getPreferredName()); + XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(suggestBuilder); + parser.nextToken(); + builder.copyCurrentStructure(parser); } 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.startArray(RESCORE_FIELD.getPreferredName()); + for (BytesReference rescoreBuilder : rescoreBuilders) { + XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(rescoreBuilder); + parser.nextToken(); + builder.copyCurrentStructure(parser); } builder.endArray(); } + + if (stats != null) { + builder.array(STATS_FIELD.getPreferredName(), stats); + } } - private static class ScriptField { + private static class ScriptField implements Writeable, ToXContent { + + public static final ScriptField PROTOTYPE = new ScriptField(null, null); + private final String fieldName; private final Script script; @@ -837,5 +1039,288 @@ public class SearchSourceBuilder extends ToXContentToBytes { public Script script() { return script; } + + @Override + public ScriptField readFrom(StreamInput in) throws IOException { + return new ScriptField(in.readString(), Script.readScript(in)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(fieldName); + script.writeTo(out); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(fieldName); + builder.field(SCRIPT_FIELD.getPreferredName(), script); + builder.endObject(); + return builder; + } + + @Override + public int hashCode() { + return Objects.hash(fieldName, script); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ScriptField other = (ScriptField) obj; + return Objects.equals(fieldName, other.fieldName) && Objects.equals(script, other.script); + } + } + + @Override + public SearchSourceBuilder readFrom(StreamInput in) throws IOException { + SearchSourceBuilder builder = new SearchSourceBuilder(); + if (in.readBoolean()) { + int size = in.readVInt(); + List aggregations = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + aggregations.add(in.readBytesReference()); + } + builder.aggregations = aggregations; + } + if (in.readBoolean()) { + builder.defaultRescoreWindowSize = in.readVInt(); + } + builder.explain = in.readOptionalBoolean(); + builder.fetchSourceContext = FetchSourceContext.optionalReadFromStream(in); + boolean hasFieldDataFields = in.readBoolean(); + if (hasFieldDataFields) { + int size = in.readVInt(); + List fieldDataFields = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + fieldDataFields.add(in.readString()); + } + builder.fieldDataFields = fieldDataFields; + } + boolean hasFieldNames = in.readBoolean(); + if (hasFieldNames) { + int size = in.readVInt(); + List fieldNames = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + fieldNames.add(in.readString()); + } + builder.fieldNames = fieldNames; + } + builder.from = in.readVInt(); + if (in.readBoolean()) { + builder.highlightBuilder = in.readBytesReference(); + } + boolean hasIndexBoost = in.readBoolean(); + if (hasIndexBoost) { + int size = in.readVInt(); + ObjectFloatHashMap indexBoost = new ObjectFloatHashMap(size); + for (int i = 0; i < size; i++) { + indexBoost.put(in.readString(), in.readFloat()); + } + builder.indexBoost = indexBoost; + } + if (in.readBoolean()) { + builder.innerHitsBuilder = in.readBytesReference(); + } + if (in.readBoolean()) { + builder.minScore = in.readFloat(); + } + if (in.readBoolean()) { + builder.postQueryBuilder = in.readQuery(); + } + if (in.readBoolean()) { + builder.queryBuilder = in.readQuery(); + } + if (in.readBoolean()) { + int size = in.readVInt(); + List rescoreBuilders = new ArrayList<>(); + for (int i = 0; i < size; i++) { + rescoreBuilders.add(in.readBytesReference()); + } + builder.rescoreBuilders = rescoreBuilders; + } + if (in.readBoolean()) { + int size = in.readVInt(); + List scriptFields = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + scriptFields.add(ScriptField.PROTOTYPE.readFrom(in)); + } + builder.scriptFields = scriptFields; + } + builder.size = in.readVInt(); + if (in.readBoolean()) { + int size = in.readVInt(); + List sorts = new ArrayList<>(); + for (int i = 0; i < size; i++) { + sorts.add(in.readBytesReference()); + } + builder.sorts = sorts; + } + if (in.readBoolean()) { + builder.stats = in.readStringArray(); + } + if (in.readBoolean()) { + builder.suggestBuilder = in.readBytesReference(); + } + builder.terminateAfter = in.readVInt(); + builder.timeoutInMillis = in.readLong(); + builder.trackScores = in.readBoolean(); + builder.version = in.readOptionalBoolean(); + return builder; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + boolean hasAggregations = aggregations != null; + out.writeBoolean(hasAggregations); + if (hasAggregations) { + out.writeVInt(aggregations.size()); + for (BytesReference aggregation : aggregations) { + out.writeBytesReference(aggregation); + } + } + boolean hasDefaultRescoreWindowSize = defaultRescoreWindowSize != null; + out.writeBoolean(hasDefaultRescoreWindowSize); + if (hasDefaultRescoreWindowSize) { + out.writeVInt(defaultRescoreWindowSize); + } + out.writeOptionalBoolean(explain); + FetchSourceContext.optionalWriteToStream(fetchSourceContext, out); + boolean hasFieldDataFields = fieldDataFields != null; + out.writeBoolean(hasFieldDataFields); + if (hasFieldDataFields) { + out.writeVInt(fieldDataFields.size()); + for (String field : fieldDataFields) { + out.writeString(field); + } + } + boolean hasFieldNames = fieldNames != null; + out.writeBoolean(hasFieldNames); + if (hasFieldNames) { + out.writeVInt(fieldNames.size()); + for (String field : fieldNames) { + out.writeString(field); + } + } + out.writeVInt(from); + boolean hasHighlightBuilder = highlightBuilder != null; + out.writeBoolean(hasHighlightBuilder); + if (hasHighlightBuilder) { + out.writeBytesReference(highlightBuilder); + } + boolean hasIndexBoost = indexBoost != null; + out.writeBoolean(hasIndexBoost); + if (hasIndexBoost) { + out.writeVInt(indexBoost.size()); + for (ObjectCursor key : indexBoost.keys()) { + out.writeString(key.value); + out.writeFloat(indexBoost.get(key.value)); + } + } + boolean hasInnerHitsBuilder = innerHitsBuilder != null; + out.writeBoolean(hasInnerHitsBuilder); + if (hasInnerHitsBuilder) { + out.writeBytesReference(innerHitsBuilder); + } + boolean hasMinScore = minScore != null; + out.writeBoolean(hasMinScore); + if (hasMinScore) { + out.writeFloat(minScore); + } + boolean hasPostQuery = postQueryBuilder != null; + out.writeBoolean(hasPostQuery); + if (hasPostQuery) { + out.writeQuery(postQueryBuilder); + } + boolean hasQuery = queryBuilder != null; + out.writeBoolean(hasQuery); + if (hasQuery) { + out.writeQuery(queryBuilder); + } + boolean hasRescoreBuilders = rescoreBuilders != null; + out.writeBoolean(hasRescoreBuilders); + if (hasRescoreBuilders) { + out.writeVInt(rescoreBuilders.size()); + for (BytesReference rescoreBuilder : rescoreBuilders) { + out.writeBytesReference(rescoreBuilder); + } + } + boolean hasScriptFields = scriptFields != null; + out.writeBoolean(hasScriptFields); + if (hasScriptFields) { + out.writeVInt(scriptFields.size()); + for (ScriptField scriptField : scriptFields) { + scriptField.writeTo(out); + } + } + out.writeVInt(size); + boolean hasSorts = sorts != null; + out.writeBoolean(hasSorts); + if (hasSorts) { + out.writeVInt(sorts.size()); + for (BytesReference sort : sorts) { + out.writeBytesReference(sort); + } + } + boolean hasStats = stats != null; + out.writeBoolean(hasStats); + if (hasStats) { + out.writeStringArray(stats); + } + boolean hasSuggestBuilder = suggestBuilder != null; + out.writeBoolean(hasSuggestBuilder); + if (hasSuggestBuilder) { + out.writeBytesReference(suggestBuilder); + } + out.writeVInt(terminateAfter); + out.writeLong(timeoutInMillis); + out.writeBoolean(trackScores); + out.writeOptionalBoolean(version); + } + + @Override + public int hashCode() { + return Objects.hash(aggregations, defaultRescoreWindowSize, explain, fetchSourceContext, fieldDataFields, fieldNames, from, + highlightBuilder, indexBoost, innerHitsBuilder, minScore, postQueryBuilder, queryBuilder, rescoreBuilders, scriptFields, + size, sorts, Arrays.hashCode(stats), suggestBuilder, terminateAfter, timeoutInMillis, trackScores, version); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj.getClass() != getClass()) { + return false; + } + SearchSourceBuilder other = (SearchSourceBuilder) obj; + return Objects.equals(aggregations, other.aggregations) + && Objects.equals(defaultRescoreWindowSize, other.defaultRescoreWindowSize) + && Objects.equals(explain, other.explain) + && Objects.equals(fetchSourceContext, other.fetchSourceContext) + && Objects.equals(fieldDataFields, other.fieldDataFields) + && Objects.equals(fieldNames, other.fieldNames) + && Objects.equals(from, other.from) + && Objects.equals(highlightBuilder, other.highlightBuilder) + && Objects.equals(indexBoost, other.indexBoost) + && Objects.equals(innerHitsBuilder, other.innerHitsBuilder) + && Objects.equals(minScore, other.minScore) + && Objects.equals(postQueryBuilder, other.postQueryBuilder) + && Objects.equals(queryBuilder, other.queryBuilder) + && Objects.equals(rescoreBuilders, other.rescoreBuilders) + && Objects.equals(scriptFields, other.scriptFields) + && Objects.equals(size, other.size) + && Objects.equals(sorts, other.sorts) + && Objects.deepEquals(stats, other.stats) + && Objects.equals(suggestBuilder, other.suggestBuilder) + && Objects.equals(terminateAfter, other.terminateAfter) + && Objects.equals(timeoutInMillis, other.timeoutInMillis) + && Objects.equals(trackScores, other.trackScores) + && Objects.equals(version, other.version); } } diff --git a/core/src/test/java/org/elasticsearch/search/builder/NewSearchSourceBuilderTests.java b/core/src/test/java/org/elasticsearch/search/builder/NewSearchSourceBuilderTests.java index 1e4986b8787..e1b09af24d7 100644 --- a/core/src/test/java/org/elasticsearch/search/builder/NewSearchSourceBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/builder/NewSearchSourceBuilderTests.java @@ -247,8 +247,8 @@ public class NewSearchSourceBuilderTests extends ESTestCase { SearchContext.removeCurrent(); } - protected final NewSearchSourceBuilder createSearchSourceBuilder() throws IOException { - NewSearchSourceBuilder builder = new NewSearchSourceBuilder(); + protected final SearchSourceBuilder createSearchSourceBuilder() throws IOException { + SearchSourceBuilder builder = new SearchSourceBuilder(); if (randomBoolean()) { builder.from(randomIntBetween(0, 10000)); } @@ -385,7 +385,7 @@ public class NewSearchSourceBuilderTests extends ESTestCase { } if (randomBoolean()) { // NORELEASE need a random highlight builder method - builder.highlight(new HighlightBuilder().field(randomAsciiOfLengthBetween(5, 20))); + builder.highlighter(new HighlightBuilder().field(randomAsciiOfLengthBetween(5, 20))); } if (randomBoolean()) { // NORELEASE need a random suggest builder method @@ -422,20 +422,20 @@ public class NewSearchSourceBuilderTests extends ESTestCase { */ @Test public void testFromXContent() throws IOException { - NewSearchSourceBuilder testBuilder = createSearchSourceBuilder(); - NewSearchSourceBuilder newBuilder = parseQuery(testBuilder.toString(), ParseFieldMatcher.STRICT); + SearchSourceBuilder testBuilder = createSearchSourceBuilder(); + SearchSourceBuilder newBuilder = parseQuery(testBuilder.toString(), ParseFieldMatcher.STRICT); assertNotSame(testBuilder, newBuilder); assertEquals(testBuilder, newBuilder); assertEquals(testBuilder.hashCode(), newBuilder.hashCode()); } - protected NewSearchSourceBuilder parseQuery(String queryAsString, ParseFieldMatcher matcher) throws IOException { + 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); System.out.println(queryAsString); - return NewSearchSourceBuilder.PROTOTYPE.fromXContent(parser, context); + return SearchSourceBuilder.PROTOTYPE.fromXContent(parser, context); } /** @@ -443,11 +443,11 @@ public class NewSearchSourceBuilderTests extends ESTestCase { */ @Test public void testSerialization() throws IOException { - NewSearchSourceBuilder testBuilder = createSearchSourceBuilder(); + SearchSourceBuilder testBuilder = createSearchSourceBuilder(); try (BytesStreamOutput output = new BytesStreamOutput()) { testBuilder.writeTo(output); try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) { - NewSearchSourceBuilder deserializedBuilder = NewSearchSourceBuilder.PROTOTYPE.readFrom(in); + SearchSourceBuilder deserializedBuilder = SearchSourceBuilder.PROTOTYPE.readFrom(in); assertEquals(deserializedBuilder, testBuilder); assertEquals(deserializedBuilder.hashCode(), testBuilder.hashCode()); assertNotSame(deserializedBuilder, testBuilder); @@ -457,20 +457,20 @@ public class NewSearchSourceBuilderTests extends ESTestCase { @Test public void testEqualsAndHashcode() throws IOException { - NewSearchSourceBuilder firstBuilder = createSearchSourceBuilder(); + 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())); - NewSearchSourceBuilder secondBuilder = copyBuilder(firstBuilder); + 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())); - NewSearchSourceBuilder thirdBuilder = copyBuilder(secondBuilder); + 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())); @@ -481,11 +481,11 @@ public class NewSearchSourceBuilderTests extends ESTestCase { } //we use the streaming infra to create a copy of the query provided as argument - protected NewSearchSourceBuilder copyBuilder(NewSearchSourceBuilder builder) throws IOException { + 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)) { - NewSearchSourceBuilder secondQuery = NewSearchSourceBuilder.PROTOTYPE.readFrom(in); + SearchSourceBuilder secondQuery = SearchSourceBuilder.PROTOTYPE.readFrom(in); return secondQuery; } }