From 25bd9cecd066e7920776eef0885b1c4a905a3156 Mon Sep 17 00:00:00 2001 From: Florian Schilling Date: Mon, 11 Mar 2013 17:39:18 +0100 Subject: [PATCH] # REST Suggester API The REST Suggester API binds the 'Suggest API' to the REST Layer directly. Hence there is no need to touch the query layer for requesting suggestions. This API extracts the Phrase Suggester API and makes 'suggestion request' top-level objects in suggestion requests. The complete API can be found in the underlying ["Suggest Feature API"](http://www.elasticsearch.org/guide/reference/api/search/suggest.html). # API Example The following examples show how Suggest Actions work on the REST layer. According to this a simple request and its response will be shown. ## Suggestion Request ```json curl -XPOST 'localhost:9200/_suggest?pretty=true' -d '{ "text" : "Xor the Got-Jewel", "simple_phrase" : { "phrase" : { "analyzer" : "bigram", "field" : "bigram", "size" : 1, "real_word_error_likelihood" : 0.95, "max_errors" : 0.5, "gram_size" : 2 } } }' ``` This example shows how to query a suggestion for the global text 'Xor the Got-Jewel'. A 'simple phrase' suggestion is requested and a 'direct generator' is configured to generate the candidates. ## Suggestion Response On success the request above will reply with a response like the following: ```json { "simple_phrase" : [ { "text" : "Xor the Got-Jewel", "offset" : 0, "length" : 17, "options" : [ { "text" : "xorr the the got got jewel", "score" : 3.5283546E-4 } ] } ] } ``` The 'suggest'-response contains a single 'simple phrase' which contains an 'option' in turn. This option represents a suggestion of the queried text. It contains the corrected text and a score indicating the probability of this option to be meant. Closes #2774 --- .../elasticsearch/action/ActionModule.java | 3 + .../action/suggest/ShardSuggestRequest.java | 59 ++ .../action/suggest/ShardSuggestResponse.java | 60 ++ .../action/suggest/SuggestAction.java | 46 + .../action/suggest/SuggestRequest.java | 168 ++++ .../action/suggest/SuggestRequestBuilder.java | 99 +++ .../action/suggest/SuggestResponse.java | 83 ++ .../suggest/TransportSuggestAction.java | 176 ++++ .../action/suggest/package-info.java | 23 + .../broadcast/BroadcastOperationResponse.java | 32 +- .../java/org/elasticsearch/client/Client.java | 26 + .../client/support/AbstractClient.java | 19 + .../client/transport/TransportClient.java | 12 + .../rest/action/RestActionModule.java | 2 + .../action/suggest/RestSuggestAction.java | 131 +++ .../rest/action/support/RestActions.java | 2 +- .../search/builder/SearchSourceBuilder.java | 2 +- .../controller/SearchPhaseController.java | 24 +- .../internal/InternalSearchResponse.java | 2 +- .../search/query/QuerySearchResult.java | 2 +- .../elasticsearch/search/suggest/Suggest.java | 61 +- .../search/suggest/SuggestBuilder.java | 18 +- .../search/suggest/SuggestContextParser.java | 4 +- .../search/suggest/SuggestParseElement.java | 16 +- .../search/suggest/SuggestPhase.java | 15 +- .../search/suggest/SuggestUtils.java | 9 +- .../search/suggest/Suggester.java | 4 +- .../suggest/SuggestionSearchContext.java | 2 +- .../suggest/phrase/PhraseSuggestParser.java | 20 +- .../suggest/phrase/PhraseSuggester.java | 4 +- .../suggest/term/TermSuggestParser.java | 10 +- .../search/suggest/term/TermSuggester.java | 4 +- .../document/DocumentActionsTests.java | 4 +- .../child/SimpleChildQuerySearchTests.java | 10 +- .../search/suggest/SuggestSearchTests.java | 819 ++++++++---------- .../action/suggest/SuggestActionTests.java | 60 ++ 36 files changed, 1476 insertions(+), 555 deletions(-) create mode 100644 src/main/java/org/elasticsearch/action/suggest/ShardSuggestRequest.java create mode 100644 src/main/java/org/elasticsearch/action/suggest/ShardSuggestResponse.java create mode 100644 src/main/java/org/elasticsearch/action/suggest/SuggestAction.java create mode 100644 src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java create mode 100644 src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java create mode 100644 src/main/java/org/elasticsearch/action/suggest/SuggestResponse.java create mode 100644 src/main/java/org/elasticsearch/action/suggest/TransportSuggestAction.java create mode 100644 src/main/java/org/elasticsearch/action/suggest/package-info.java create mode 100644 src/main/java/org/elasticsearch/rest/action/suggest/RestSuggestAction.java create mode 100644 src/test/java/org/elasticsearch/test/unit/action/suggest/SuggestActionTests.java diff --git a/src/main/java/org/elasticsearch/action/ActionModule.java b/src/main/java/org/elasticsearch/action/ActionModule.java index 14f60621926..2a6cfbdff4f 100644 --- a/src/main/java/org/elasticsearch/action/ActionModule.java +++ b/src/main/java/org/elasticsearch/action/ActionModule.java @@ -112,6 +112,8 @@ import org.elasticsearch.action.percolate.PercolateAction; import org.elasticsearch.action.percolate.TransportPercolateAction; import org.elasticsearch.action.search.*; import org.elasticsearch.action.search.type.*; +import org.elasticsearch.action.suggest.SuggestAction; +import org.elasticsearch.action.suggest.TransportSuggestAction; import org.elasticsearch.action.support.TransportAction; import org.elasticsearch.action.update.TransportUpdateAction; import org.elasticsearch.action.update.UpdateAction; @@ -205,6 +207,7 @@ public class ActionModule extends AbstractModule { registerAction(DeleteAction.INSTANCE, TransportDeleteAction.class, TransportIndexDeleteAction.class, TransportShardDeleteAction.class); registerAction(CountAction.INSTANCE, TransportCountAction.class); + registerAction(SuggestAction.INSTANCE, TransportSuggestAction.class); registerAction(UpdateAction.INSTANCE, TransportUpdateAction.class); registerAction(MultiGetAction.INSTANCE, TransportMultiGetAction.class, TransportShardMultiGetAction.class); diff --git a/src/main/java/org/elasticsearch/action/suggest/ShardSuggestRequest.java b/src/main/java/org/elasticsearch/action/suggest/ShardSuggestRequest.java new file mode 100644 index 00000000000..d78c4cacb6d --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/ShardSuggestRequest.java @@ -0,0 +1,59 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.suggest; + +import java.io.IOException; + +import org.elasticsearch.action.support.broadcast.BroadcastShardOperationRequest; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; + +/** + * Internal suggest request executed directly against a specific index shard. + */ +final class ShardSuggestRequest extends BroadcastShardOperationRequest { + + private BytesReference suggestSource; + + ShardSuggestRequest() { + } + + public ShardSuggestRequest(String index, int shardId, SuggestRequest request) { + super(index, shardId, request); + this.suggestSource = request.suggest(); + } + + public BytesReference suggest() { + return suggestSource; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + super.readFrom(in); + suggestSource = in.readBytesReference(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeBytesReference(suggestSource); + } +} diff --git a/src/main/java/org/elasticsearch/action/suggest/ShardSuggestResponse.java b/src/main/java/org/elasticsearch/action/suggest/ShardSuggestResponse.java new file mode 100644 index 00000000000..4570b1bc200 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/ShardSuggestResponse.java @@ -0,0 +1,60 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.suggest; + +import org.elasticsearch.action.support.broadcast.BroadcastShardOperationResponse; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.search.suggest.Suggest; + +import java.io.IOException; + +/** + * Internal suggest response of a shard suggest request executed directly against a specific shard. + */ +class ShardSuggestResponse extends BroadcastShardOperationResponse { + + private final Suggest suggest; + + ShardSuggestResponse() { + this.suggest = new Suggest(); + } + + public ShardSuggestResponse(String index, int shardId, Suggest suggest) { + super(index, shardId); + this.suggest = suggest; + } + + public Suggest getSuggest() { + return this.suggest; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + super.readFrom(in); + suggest.readFrom(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + suggest.writeTo(out); + } +} diff --git a/src/main/java/org/elasticsearch/action/suggest/SuggestAction.java b/src/main/java/org/elasticsearch/action/suggest/SuggestAction.java new file mode 100644 index 00000000000..fad368d6177 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/SuggestAction.java @@ -0,0 +1,46 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.suggest; + +import org.elasticsearch.action.Action; +import org.elasticsearch.client.Client; +import org.elasticsearch.search.suggest.Suggest; + +/** + */ +public class SuggestAction extends Action { + + public static final SuggestAction INSTANCE = new SuggestAction(); + public static final String NAME = "suggest"; + + private SuggestAction() { + super(NAME); + } + + @Override + public SuggestResponse newResponse() { + return new SuggestResponse(new Suggest()); + } + + @Override + public SuggestRequestBuilder newRequestBuilder(Client client) { + return new SuggestRequestBuilder(client); + } +} diff --git a/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java b/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java new file mode 100644 index 00000000000..4a2f8798ab6 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java @@ -0,0 +1,168 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.suggest; + +import java.io.IOException; +import java.util.Arrays; + +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.support.broadcast.BroadcastOperationRequest; +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.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentType; + +/** + * A request to get suggestions for corrections of phrases. Best created with + * {@link org.elasticsearch.client.Requests#suggestRequest(String...)}. + *

+ *

The request requires the query source to be set either using {@link #query(org.elasticsearch.index.query.QueryBuilder)}, + * or {@link #query(byte[])}. + * + * @see SuggestResponse + * @see org.elasticsearch.client.Client#suggest(SuggestRequest) + * @see org.elasticsearch.client.Requests#suggestRequest(String...) + */ +public final class SuggestRequest extends BroadcastOperationRequest { + + static final XContentType contentType = Requests.CONTENT_TYPE; + + @Nullable + private String routing; + + @Nullable + private String preference; + + private BytesReference suggestSource; + private boolean suggestSourceUnsafe; + + SuggestRequest() { + } + + /** + * Constructs a new suggest request against the provided indices. No indices provided means it will + * run against all indices. + */ + public SuggestRequest(String... indices) { + super(indices); + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = super.validate(); + return validationException; + } + + @Override + protected void beforeStart() { + if (suggestSourceUnsafe) { + suggest(suggestSource.copyBytesArray(), false); + } + } + + /** + * The Phrase to get correction suggestions for + */ + BytesReference suggest() { + return suggestSource; + } + + /** + * set a new source for the suggest query + */ + public SuggestRequest suggest(BytesReference suggestSource) { + return suggest(suggestSource, false); + } + + /** + * A comma separated list of routing values to control the shards the search will be executed on. + */ + public String routing() { + return this.routing; + } + + /** + * A comma separated list of routing values to control the shards the search will be executed on. + */ + public SuggestRequest routing(String routing) { + this.routing = routing; + return this; + } + + /** + * The routing values to control the shards that the search will be executed on. + */ + public SuggestRequest routing(String... routings) { + this.routing = Strings.arrayToCommaDelimitedString(routings); + return this; + } + + public SuggestRequest preference(String preference) { + this.preference = preference; + return this; + } + + public String preference() { + return this.preference; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + super.readFrom(in); + routing = in.readOptionalString(); + preference = in.readOptionalString(); + suggest(in.readBytesReference()); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeOptionalString(routing); + out.writeOptionalString(preference); + out.writeBytesReference(suggestSource); + } + + @Override + public String toString() { + String sSource = "_na_"; + try { + sSource = XContentHelper.convertToJson(suggestSource, false); + } catch (Exception e) { + // ignore + } + return "[" + Arrays.toString(indices) + "]" + ", suggestSource[" + sSource + "]"; + } + + public SuggestRequest suggest(BytesReference suggestSource, boolean contentUnsafe) { + this.suggestSource = suggestSource; + this.suggestSourceUnsafe = contentUnsafe; + return this; + } + + public SuggestRequest suggest(String source) { + return suggest(new BytesArray(source)); + } + +} diff --git a/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java b/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java new file mode 100644 index 00000000000..6ff2672771d --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java @@ -0,0 +1,99 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.suggest; + +import java.io.IOException; + +import org.elasticsearch.ElasticSearchException; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.broadcast.BroadcastOperationRequestBuilder; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.internal.InternalClient; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.search.suggest.SuggestBuilder; +import org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder; + +/** + * A suggest action request builder. + */ +public class SuggestRequestBuilder extends BroadcastOperationRequestBuilder { + + final SuggestBuilder suggest = new SuggestBuilder(); + + public SuggestRequestBuilder(Client client) { + super((InternalClient) client, new SuggestRequest()); + } + + /** + * Add a definition for suggestions to the request + */ + public SuggestRequestBuilder addSuggestion(SuggestionBuilder suggestion) { + suggest.addSuggestion(suggestion); + return this; + } + + /** + * A comma separated list of routing values to control the shards the search will be executed on. + */ + public SuggestRequestBuilder setRouting(String routing) { + request.routing(routing); + return this; + } + + public SuggestRequestBuilder setSuggestText(String globalText) { + this.suggest.setText(globalText); + return this; + } + + /** + * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to + * _local to prefer local shards, _primary to execute only on primary shards, + * _shards:x,y to operate on shards x & y, or a custom value, which guarantees that the same order + * will be used across different requests. + */ + public SuggestRequestBuilder setPreference(String preference) { + request.preference(preference); + return this; + } + + /** + * The routing values to control the shards that the search will be executed on. + */ + public SuggestRequestBuilder setRouting(String... routing) { + request.routing(routing); + return this; + } + + @Override + protected void doExecute(ActionListener listener) { + try { + XContentBuilder builder = XContentFactory.contentBuilder(SuggestRequest.contentType); + XContentBuilder content = suggest.toXContent(builder, ToXContent.EMPTY_PARAMS); + content.close(); + request.suggest(content.bytes()); + } catch (IOException e) { + throw new ElasticSearchException("Unable to build suggestion request", e); + } + + ((InternalClient) client).suggest(request, listener); + } +} diff --git a/src/main/java/org/elasticsearch/action/suggest/SuggestResponse.java b/src/main/java/org/elasticsearch/action/suggest/SuggestResponse.java new file mode 100644 index 00000000000..7abe4417513 --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/SuggestResponse.java @@ -0,0 +1,83 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.suggest; + +import java.io.IOException; +import java.util.List; + +import org.elasticsearch.action.ShardOperationFailedException; +import org.elasticsearch.action.support.broadcast.BroadcastOperationResponse; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.XContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.search.suggest.Suggest; + +/** + * The response of the suggest action. + */ +public final class SuggestResponse extends BroadcastOperationResponse { + + private final Suggest suggest; + + SuggestResponse(Suggest suggest) { + this.suggest = suggest; + } + + SuggestResponse(Suggest suggest, int totalShards, int successfulShards, int failedShards, List shardFailures) { + super(totalShards, successfulShards, failedShards, shardFailures); + this.suggest = suggest; + } + + /** + * The Suggestions of the phrase. + */ + public Suggest getSuggest() { + return suggest; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + super.readFrom(in); + this.suggest.readFrom(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + this.suggest.writeTo(out); + } + + @Override + public String toString() { + String source; + try { + XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + suggest.toXContent(builder, null); + source = XContentHelper.convertToJson(builder.bytes(), true); + } catch (IOException e) { + source = "Error: " + e.getMessage(); + } + return "Suggest Response["+source+"]"; + } +} diff --git a/src/main/java/org/elasticsearch/action/suggest/TransportSuggestAction.java b/src/main/java/org/elasticsearch/action/suggest/TransportSuggestAction.java new file mode 100644 index 00000000000..f2554e1fede --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/TransportSuggestAction.java @@ -0,0 +1,176 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.action.suggest; + +import static com.google.common.collect.Lists.newArrayList; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReferenceArray; + +import org.elasticsearch.ElasticSearchException; +import org.elasticsearch.ElasticSearchIllegalArgumentException; +import org.elasticsearch.action.ShardOperationFailedException; +import org.elasticsearch.action.support.DefaultShardOperationFailedException; +import org.elasticsearch.action.support.broadcast.BroadcastShardOperationFailedException; +import org.elasticsearch.action.support.broadcast.TransportBroadcastOperationAction; +import org.elasticsearch.cluster.ClusterService; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlockException; +import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.cluster.routing.GroupShardsIterator; +import org.elasticsearch.cluster.routing.ShardRouting; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.engine.Engine; +import org.elasticsearch.index.service.IndexService; +import org.elasticsearch.index.shard.service.IndexShard; +import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.search.suggest.SuggestParseElement; +import org.elasticsearch.search.suggest.SuggestPhase; +import org.elasticsearch.search.suggest.SuggestionSearchContext; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; + +/** + * Defines the transport of a suggestion request across the cluster + */ +public class TransportSuggestAction extends TransportBroadcastOperationAction { + + private final IndicesService indicesService; + + private final SuggestPhase suggestPhase; + + @Inject + public TransportSuggestAction(Settings settings, ThreadPool threadPool, ClusterService clusterService, TransportService transportService, + IndicesService indicesService) { + super(settings, threadPool, clusterService, transportService); + this.indicesService = indicesService; + this.suggestPhase = new SuggestPhase(settings); + } + + @Override + protected String executor() { + return ThreadPool.Names.SEARCH; + } + + @Override + protected String transportAction() { + return SuggestAction.NAME; + } + + @Override + protected SuggestRequest newRequest() { + return new SuggestRequest(); + } + + @Override + protected ShardSuggestRequest newShardRequest() { + return new ShardSuggestRequest(); + } + + @Override + protected ShardSuggestRequest newShardRequest(ShardRouting shard, SuggestRequest request) { + return new ShardSuggestRequest(shard.index(), shard.id(), request); + } + + @Override + protected ShardSuggestResponse newShardResponse() { + return new ShardSuggestResponse(); + } + + @Override + protected GroupShardsIterator shards(ClusterState clusterState, SuggestRequest request, String[] concreteIndices) { + Map> routingMap = clusterState.metaData().resolveSearchRouting(request.routing(), request.indices()); + return clusterService.operationRouting().searchShards(clusterState, request.indices(), concreteIndices, routingMap, request.preference()); + } + + @Override + protected ClusterBlockException checkGlobalBlock(ClusterState state, SuggestRequest request) { + return state.blocks().globalBlockedException(ClusterBlockLevel.READ); + } + + @Override + protected ClusterBlockException checkRequestBlock(ClusterState state, SuggestRequest countRequest, String[] concreteIndices) { + return state.blocks().indicesBlockedException(ClusterBlockLevel.READ, concreteIndices); + } + + @Override + protected SuggestResponse newResponse(SuggestRequest request, AtomicReferenceArray shardsResponses, ClusterState clusterState) { + int successfulShards = 0; + int failedShards = 0; + + final Map> groupedSuggestions = new HashMap>(); + + List shardFailures = null; + for (int i = 0; i < shardsResponses.length(); i++) { + Object shardResponse = shardsResponses.get(i); + if (shardResponse == null) { + failedShards++; + } else if (shardResponse instanceof BroadcastShardOperationFailedException) { + failedShards++; + if (shardFailures == null) { + shardFailures = newArrayList(); + } + shardFailures.add(new DefaultShardOperationFailedException((BroadcastShardOperationFailedException) shardResponse)); + } else { + Suggest suggest = ((ShardSuggestResponse) shardResponse).getSuggest(); + Suggest.group(groupedSuggestions, suggest); + successfulShards++; + } + } + + return new SuggestResponse(new Suggest(Suggest.reduce(groupedSuggestions)), shardsResponses.length(), successfulShards, failedShards, shardFailures); + } + + @Override + protected ShardSuggestResponse shardOperation(ShardSuggestRequest request) throws ElasticSearchException { + IndexService indexService = indicesService.indexServiceSafe(request.index()); + IndexShard indexShard = indexService.shardSafe(request.shardId()); + final Engine.Searcher searcher = indexShard.searcher(); + try { + BytesReference suggest = request.suggest(); + if (suggest != null && suggest.length() > 0) { + final SuggestParseElement element = new SuggestParseElement(); + final XContentParser parser = XContentFactory.xContent(suggest).createParser(suggest); + + if(parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new ElasticSearchIllegalArgumentException("Object expected"); + } + + final SuggestionSearchContext context = element.parseInternal(parser, indexService.mapperService()); + final Suggest result = suggestPhase.execute(context, searcher.reader()); + return new ShardSuggestResponse(request.index(), request.shardId(), result); + } + return new ShardSuggestResponse(request.index(), request.shardId(), new Suggest()); + + } catch(Throwable ex) { + throw new ElasticSearchException("Failed to execute suggest", ex); + } finally { + searcher.release(); + } + } +} diff --git a/src/main/java/org/elasticsearch/action/suggest/package-info.java b/src/main/java/org/elasticsearch/action/suggest/package-info.java new file mode 100644 index 00000000000..2499b54c7ed --- /dev/null +++ b/src/main/java/org/elasticsearch/action/suggest/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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. + */ + +/** + * Suggest action. + */ +package org.elasticsearch.action.suggest; \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastOperationResponse.java b/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastOperationResponse.java index 215c330c18d..4ec088dc6bb 100644 --- a/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastOperationResponse.java +++ b/src/main/java/org/elasticsearch/action/support/broadcast/BroadcastOperationResponse.java @@ -19,27 +19,25 @@ package org.elasticsearch.action.support.broadcast; -import com.google.common.collect.ImmutableList; +import static org.elasticsearch.action.support.DefaultShardOperationFailedException.readShardOperationFailed; + +import java.io.IOException; +import java.util.List; + import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ShardOperationFailedException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static org.elasticsearch.action.support.DefaultShardOperationFailedException.readShardOperationFailed; - /** * Base class for all broadcast operation based responses. */ public abstract class BroadcastOperationResponse extends ActionResponse { - + private static final ShardOperationFailedException[] EMPTY = new ShardOperationFailedException[0]; private int totalShards; private int successfulShards; private int failedShards; - private List shardFailures = ImmutableList.of(); + private ShardOperationFailedException[] shardFailures = EMPTY; protected BroadcastOperationResponse() { } @@ -48,10 +46,7 @@ public abstract class BroadcastOperationResponse extends ActionResponse { this.totalShards = totalShards; this.successfulShards = successfulShards; this.failedShards = failedShards; - this.shardFailures = shardFailures; - if (shardFailures == null) { - this.shardFailures = ImmutableList.of(); - } + this.shardFailures = shardFailures == null ? EMPTY : shardFailures.toArray(new ShardOperationFailedException[shardFailures.size()]); } /** @@ -78,10 +73,7 @@ public abstract class BroadcastOperationResponse extends ActionResponse { /** * The list of shard failures exception. */ - public List getShardFailures() { - if (shardFailures == null) { - return ImmutableList.of(); - } + public ShardOperationFailedException[] getShardFailures() { return shardFailures; } @@ -93,9 +85,9 @@ public abstract class BroadcastOperationResponse extends ActionResponse { failedShards = in.readVInt(); int size = in.readVInt(); if (size > 0) { - shardFailures = new ArrayList(size); + shardFailures = new ShardOperationFailedException[size]; for (int i = 0; i < size; i++) { - shardFailures.add(readShardOperationFailed(in)); + shardFailures[i] = readShardOperationFailed(in); } } } @@ -106,7 +98,7 @@ public abstract class BroadcastOperationResponse extends ActionResponse { out.writeVInt(totalShards); out.writeVInt(successfulShards); out.writeVInt(failedShards); - out.writeVInt(shardFailures.size()); + out.writeVInt(shardFailures.length); for (ShardOperationFailedException exp : shardFailures) { exp.writeTo(out); } diff --git a/src/main/java/org/elasticsearch/client/Client.java b/src/main/java/org/elasticsearch/client/Client.java index 14f3acfef03..755baff8472 100644 --- a/src/main/java/org/elasticsearch/client/Client.java +++ b/src/main/java/org/elasticsearch/client/Client.java @@ -45,6 +45,9 @@ import org.elasticsearch.action.percolate.PercolateRequest; import org.elasticsearch.action.percolate.PercolateRequestBuilder; import org.elasticsearch.action.percolate.PercolateResponse; import org.elasticsearch.action.search.*; +import org.elasticsearch.action.suggest.SuggestRequest; +import org.elasticsearch.action.suggest.SuggestRequestBuilder; +import org.elasticsearch.action.suggest.SuggestResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateRequestBuilder; import org.elasticsearch.action.update.UpdateResponse; @@ -331,6 +334,29 @@ public interface Client { */ CountRequestBuilder prepareCount(String... indices); + /** + * Suggestion matching a specific phrase. + * + * @param request The suggest request + * @return The result future + * @see Requests#suggestRequest(String...) + */ + ActionFuture suggest(SuggestRequest request); + + /** + * Suggestions matching a specific phrase. + * + * @param request The suggest request + * @param listener A listener to be notified of the result + * @see Requests#suggestRequest(String...) + */ + void suggest(SuggestRequest request, ActionListener listener); + + /** + * Suggestions matching a specific phrase. + */ + SuggestRequestBuilder prepareSuggest(String... indices); + /** * Search across one or more indices and one or more types with a query. * diff --git a/src/main/java/org/elasticsearch/client/support/AbstractClient.java b/src/main/java/org/elasticsearch/client/support/AbstractClient.java index 09a10fd8efd..9261d3034ef 100644 --- a/src/main/java/org/elasticsearch/client/support/AbstractClient.java +++ b/src/main/java/org/elasticsearch/client/support/AbstractClient.java @@ -53,6 +53,10 @@ import org.elasticsearch.action.percolate.PercolateRequest; import org.elasticsearch.action.percolate.PercolateRequestBuilder; import org.elasticsearch.action.percolate.PercolateResponse; import org.elasticsearch.action.search.*; +import org.elasticsearch.action.suggest.SuggestAction; +import org.elasticsearch.action.suggest.SuggestRequest; +import org.elasticsearch.action.suggest.SuggestRequestBuilder; +import org.elasticsearch.action.suggest.SuggestResponse; import org.elasticsearch.action.update.UpdateAction; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateRequestBuilder; @@ -260,6 +264,21 @@ public abstract class AbstractClient implements InternalClient { return new CountRequestBuilder(this).setIndices(indices); } + @Override + public ActionFuture suggest(final SuggestRequest request) { + return execute(SuggestAction.INSTANCE, request); + } + + @Override + public void suggest(final SuggestRequest request, final ActionListener listener) { + execute(SuggestAction.INSTANCE, request, listener); + } + + @Override + public SuggestRequestBuilder prepareSuggest(String... indices) { + return new SuggestRequestBuilder(this).setIndices(indices); + } + @Override public ActionFuture moreLikeThis(final MoreLikeThisRequest request) { return execute(MoreLikeThisAction.INSTANCE, request); diff --git a/src/main/java/org/elasticsearch/client/transport/TransportClient.java b/src/main/java/org/elasticsearch/client/transport/TransportClient.java index c466e1aafad..6bf45f63186 100644 --- a/src/main/java/org/elasticsearch/client/transport/TransportClient.java +++ b/src/main/java/org/elasticsearch/client/transport/TransportClient.java @@ -42,6 +42,8 @@ import org.elasticsearch.action.mlt.MoreLikeThisRequest; import org.elasticsearch.action.percolate.PercolateRequest; import org.elasticsearch.action.percolate.PercolateResponse; import org.elasticsearch.action.search.*; +import org.elasticsearch.action.suggest.SuggestRequest; +import org.elasticsearch.action.suggest.SuggestResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.AdminClient; @@ -378,6 +380,16 @@ public class TransportClient extends AbstractClient { internalClient.count(request, listener); } + @Override + public ActionFuture suggest(SuggestRequest request) { + return internalClient.suggest(request); + } + + @Override + public void suggest(SuggestRequest request, ActionListener listener) { + internalClient.suggest(request, listener); + } + @Override public ActionFuture search(SearchRequest request) { return internalClient.search(request); diff --git a/src/main/java/org/elasticsearch/rest/action/RestActionModule.java b/src/main/java/org/elasticsearch/rest/action/RestActionModule.java index e089e607cca..2d45ee21f43 100644 --- a/src/main/java/org/elasticsearch/rest/action/RestActionModule.java +++ b/src/main/java/org/elasticsearch/rest/action/RestActionModule.java @@ -77,6 +77,7 @@ import org.elasticsearch.rest.action.percolate.RestPercolateAction; import org.elasticsearch.rest.action.search.RestMultiSearchAction; import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.rest.action.search.RestSearchScrollAction; +import org.elasticsearch.rest.action.suggest.RestSuggestAction; import org.elasticsearch.rest.action.update.RestUpdateAction; import java.util.List; @@ -153,6 +154,7 @@ public class RestActionModule extends AbstractModule { bind(RestDeleteAction.class).asEagerSingleton(); bind(RestDeleteByQueryAction.class).asEagerSingleton(); bind(RestCountAction.class).asEagerSingleton(); + bind(RestSuggestAction.class).asEagerSingleton(); bind(RestBulkAction.class).asEagerSingleton(); bind(RestUpdateAction.class).asEagerSingleton(); bind(RestPercolateAction.class).asEagerSingleton(); diff --git a/src/main/java/org/elasticsearch/rest/action/suggest/RestSuggestAction.java b/src/main/java/org/elasticsearch/rest/action/suggest/RestSuggestAction.java new file mode 100644 index 00000000000..cb6da4ba315 --- /dev/null +++ b/src/main/java/org/elasticsearch/rest/action/suggest/RestSuggestAction.java @@ -0,0 +1,131 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.rest.action.suggest; + +import static org.elasticsearch.rest.RestRequest.Method.GET; +import static org.elasticsearch.rest.RestRequest.Method.POST; +import static org.elasticsearch.rest.RestStatus.BAD_REQUEST; +import static org.elasticsearch.rest.RestStatus.OK; +import static org.elasticsearch.rest.action.support.RestActions.buildBroadcastShardsHeader; + +import java.io.IOException; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.suggest.SuggestRequest; +import org.elasticsearch.action.suggest.SuggestResponse; +import org.elasticsearch.action.support.IgnoreIndices; +import org.elasticsearch.action.support.broadcast.BroadcastOperationThreading; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.rest.BaseRestHandler; +import org.elasticsearch.rest.RestChannel; +import org.elasticsearch.rest.RestController; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.XContentRestResponse; +import org.elasticsearch.rest.XContentThrowableRestResponse; +import org.elasticsearch.rest.action.support.RestActions; +import org.elasticsearch.rest.action.support.RestXContentBuilder; +import org.elasticsearch.search.suggest.Suggest; + +/** + * + */ +public class RestSuggestAction extends BaseRestHandler { + + @Inject + public RestSuggestAction(Settings settings, Client client, RestController controller) { + super(settings, client); + controller.registerHandler(POST, "/_suggest", this); + controller.registerHandler(GET, "/_suggest", this); + controller.registerHandler(POST, "/{index}/_suggest", this); + controller.registerHandler(GET, "/{index}/_suggest", this); + } + + @Override + public void handleRequest(final RestRequest request, final RestChannel channel) { + SuggestRequest suggestRequest = new SuggestRequest(RestActions.splitIndices(request.param("index"))); + if (request.hasParam("ignore_indices")) { + suggestRequest.ignoreIndices(IgnoreIndices.fromString(request.param("ignore_indices"))); + } + suggestRequest.listenerThreaded(false); + try { + BroadcastOperationThreading operationThreading = BroadcastOperationThreading.fromString(request.param("operation_threading"), BroadcastOperationThreading.SINGLE_THREAD); + if (operationThreading == BroadcastOperationThreading.NO_THREADS) { + // since we don't spawn, don't allow no_threads, but change it to a single thread + operationThreading = BroadcastOperationThreading.SINGLE_THREAD; + } + suggestRequest.operationThreading(operationThreading); + if (request.hasContent()) { + suggestRequest.suggest(request.content(), request.contentUnsafe()); + } else { + String source = request.param("source"); + if (source != null) { + suggestRequest.suggest(source); + } else { + BytesReference querySource = RestActions.parseQuerySource(request); + if (querySource != null) { + suggestRequest.suggest(querySource, false); + } + } + } + suggestRequest.routing(request.param("routing")); + suggestRequest.preference(request.param("preference")); + } catch (Exception e) { + try { + XContentBuilder builder = RestXContentBuilder.restContentBuilder(request); + channel.sendResponse(new XContentRestResponse(request, BAD_REQUEST, builder.startObject().field("error", e.getMessage()).endObject())); + } catch (IOException e1) { + logger.error("Failed to send failure response", e1); + } + return; + } + + client.suggest(suggestRequest, new ActionListener() { + @Override + public void onResponse(SuggestResponse response) { + try { + XContentBuilder builder = RestXContentBuilder.restContentBuilder(request); + builder.startObject(); + buildBroadcastShardsHeader(builder, response); + Suggest suggest = response.getSuggest(); + if (suggest != null) { + suggest.toXContent(builder, request); + } + builder.endObject(); + channel.sendResponse(new XContentRestResponse(request, OK, builder)); + } catch (Exception e) { + onFailure(e); + } + } + + @Override + public void onFailure(Throwable e) { + try { + channel.sendResponse(new XContentThrowableRestResponse(request, e)); + } catch (IOException e1) { + logger.error("Failed to send failure response", e1); + } + } + }); + } +} diff --git a/src/main/java/org/elasticsearch/rest/action/support/RestActions.java b/src/main/java/org/elasticsearch/rest/action/support/RestActions.java index 01bb306beaa..ee5fe187ba5 100644 --- a/src/main/java/org/elasticsearch/rest/action/support/RestActions.java +++ b/src/main/java/org/elasticsearch/rest/action/support/RestActions.java @@ -52,7 +52,7 @@ public class RestActions { builder.field("total", response.getTotalShards()); builder.field("successful", response.getSuccessfulShards()); builder.field("failed", response.getFailedShards()); - if (!response.getShardFailures().isEmpty()) { + if (response.getShardFailures()!=null && response.getShardFailures().length>0) { builder.startArray("failures"); for (ShardOperationFailedException shardFailure : response.getShardFailures()) { builder.startObject(); diff --git a/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index fe7cd5248d0..a980182a1b2 100644 --- a/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -408,7 +408,7 @@ public class SearchSourceBuilder implements ToXContent { public SuggestBuilder suggest() { if (suggestBuilder == null) { - suggestBuilder = new SuggestBuilder(); + suggestBuilder = new SuggestBuilder("suggest"); } return suggestBuilder; } diff --git a/src/main/java/org/elasticsearch/search/controller/SearchPhaseController.java b/src/main/java/org/elasticsearch/search/controller/SearchPhaseController.java index 375a421b96d..5c739c0de53 100644 --- a/src/main/java/org/elasticsearch/search/controller/SearchPhaseController.java +++ b/src/main/java/org/elasticsearch/search/controller/SearchPhaseController.java @@ -380,36 +380,24 @@ public class SearchPhaseController extends AbstractComponent { // merge suggest results Suggest suggest = null; if (!queryResults.isEmpty()) { + final Map> groupedSuggestions = new HashMap>(); boolean hasSuggestions = false; for (QuerySearchResultProvider resultProvider : queryResults.values()) { Suggest shardResult = resultProvider.queryResult().suggest(); + if (shardResult == null) { continue; } hasSuggestions = true; - for (Suggestion> suggestion : shardResult) { - List list = groupedSuggestions.get(suggestion.getName()); - if (list == null) { - list = new ArrayList(); - groupedSuggestions.put(suggestion.getName(), list); - } - list.add(suggestion); - } - + Suggest.group(groupedSuggestions, shardResult); } - List>> reduced = new ArrayList>>(); - for (java.util.Map.Entry> unmergedResults : groupedSuggestions.entrySet()) { - List value = unmergedResults.getValue(); - Suggestion reduce = value.get(0).reduce(value); - reduce.trim(); - reduced.add(reduce); - - } - suggest = hasSuggestions ? new Suggest(reduced) : null; + + suggest = hasSuggestions ? new Suggest(Suggest.Fields.SUGGEST, Suggest.reduce(groupedSuggestions)) : null; } InternalSearchHits searchHits = new InternalSearchHits(hits.toArray(new InternalSearchHit[hits.size()]), totalHits, maxScore); return new InternalSearchResponse(searchHits, facets, suggest, timedOut); } + } diff --git a/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java b/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java index e3ffc1fbe0c..fa5698b3181 100644 --- a/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java +++ b/src/main/java/org/elasticsearch/search/internal/InternalSearchResponse.java @@ -99,7 +99,7 @@ public class InternalSearchResponse implements Streamable, ToXContent { facets = InternalFacets.readFacets(in); } if (in.readBoolean()) { - suggest = Suggest.readSuggest(in); + suggest = Suggest.readSuggest(Suggest.Fields.SUGGEST, in); } timedOut = in.readBoolean(); } diff --git a/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java b/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java index decf5dca374..7e0b3c27ee1 100644 --- a/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java +++ b/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java @@ -147,7 +147,7 @@ public class QuerySearchResult extends TransportResponse implements QuerySearchR facets = InternalFacets.readFacets(in); } if (in.readBoolean()) { - suggest = Suggest.readSuggest(in); + suggest = Suggest.readSuggest(Suggest.Fields.SUGGEST, in); } searchTimedOut = in.readBoolean(); } diff --git a/src/main/java/org/elasticsearch/search/suggest/Suggest.java b/src/main/java/org/elasticsearch/search/suggest/Suggest.java index 72b45e619b3..9efd09bd007 100644 --- a/src/main/java/org/elasticsearch/search/suggest/Suggest.java +++ b/src/main/java/org/elasticsearch/search/suggest/Suggest.java @@ -35,7 +35,6 @@ import org.elasticsearch.common.text.Text; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilderString; -import org.elasticsearch.search.suggest.Suggest.Suggestion; import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry; import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option; import org.elasticsearch.search.suggest.term.TermSuggestion; @@ -45,8 +44,8 @@ import org.elasticsearch.search.suggest.term.TermSuggestion; */ public class Suggest implements Iterable>>, Streamable, ToXContent { - static class Fields { - static final XContentBuilderString SUGGEST = new XContentBuilderString("suggest"); + public static class Fields { + public static final XContentBuilderString SUGGEST = new XContentBuilderString("suggest"); } private static final Comparator