# 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
This commit is contained in:
parent
a127f2d2e8
commit
25bd9cecd0
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<SuggestRequest, SuggestResponse, SuggestRequestBuilder> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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...)}.
|
||||
* <p/>
|
||||
* <p>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<SuggestRequest> {
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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<SuggestRequest, SuggestResponse, SuggestRequestBuilder> {
|
||||
|
||||
final SuggestBuilder suggest = new SuggestBuilder();
|
||||
|
||||
public SuggestRequestBuilder(Client client) {
|
||||
super((InternalClient) client, new SuggestRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a definition for suggestions to the request
|
||||
*/
|
||||
public <T> SuggestRequestBuilder addSuggestion(SuggestionBuilder<T> 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
|
||||
* <tt>_local</tt> to prefer local shards, <tt>_primary</tt> 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<SuggestResponse> 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);
|
||||
}
|
||||
}
|
|
@ -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<ShardOperationFailedException> 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+"]";
|
||||
}
|
||||
}
|
|
@ -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<SuggestRequest, SuggestResponse, ShardSuggestRequest, ShardSuggestResponse> {
|
||||
|
||||
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<String, Set<String>> 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<String, List<Suggest.Suggestion>> groupedSuggestions = new HashMap<String, List<Suggest.Suggestion>>();
|
||||
|
||||
List<ShardOperationFailedException> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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<ShardOperationFailedException> 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<? extends ShardOperationFailedException> 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<ShardOperationFailedException>(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);
|
||||
}
|
||||
|
|
|
@ -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<SuggestResponse> 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<SuggestResponse> listener);
|
||||
|
||||
/**
|
||||
* Suggestions matching a specific phrase.
|
||||
*/
|
||||
SuggestRequestBuilder prepareSuggest(String... indices);
|
||||
|
||||
/**
|
||||
* Search across one or more indices and one or more types with a query.
|
||||
*
|
||||
|
|
|
@ -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<SuggestResponse> suggest(final SuggestRequest request) {
|
||||
return execute(SuggestAction.INSTANCE, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggest(final SuggestRequest request, final ActionListener<SuggestResponse> listener) {
|
||||
execute(SuggestAction.INSTANCE, request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestRequestBuilder prepareSuggest(String... indices) {
|
||||
return new SuggestRequestBuilder(this).setIndices(indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<SearchResponse> moreLikeThis(final MoreLikeThisRequest request) {
|
||||
return execute(MoreLikeThisAction.INSTANCE, request);
|
||||
|
|
|
@ -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<SuggestResponse> suggest(SuggestRequest request) {
|
||||
return internalClient.suggest(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggest(SuggestRequest request, ActionListener<SuggestResponse> listener) {
|
||||
internalClient.suggest(request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<SearchResponse> search(SearchRequest request) {
|
||||
return internalClient.search(request);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<SuggestResponse>() {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -408,7 +408,7 @@ public class SearchSourceBuilder implements ToXContent {
|
|||
|
||||
public SuggestBuilder suggest() {
|
||||
if (suggestBuilder == null) {
|
||||
suggestBuilder = new SuggestBuilder();
|
||||
suggestBuilder = new SuggestBuilder("suggest");
|
||||
}
|
||||
return suggestBuilder;
|
||||
}
|
||||
|
|
|
@ -380,36 +380,24 @@ public class SearchPhaseController extends AbstractComponent {
|
|||
// merge suggest results
|
||||
Suggest suggest = null;
|
||||
if (!queryResults.isEmpty()) {
|
||||
|
||||
final Map<String, List<Suggest.Suggestion>> groupedSuggestions = new HashMap<String, List<Suggest.Suggestion>>();
|
||||
boolean hasSuggestions = false;
|
||||
for (QuerySearchResultProvider resultProvider : queryResults.values()) {
|
||||
Suggest shardResult = resultProvider.queryResult().suggest();
|
||||
|
||||
if (shardResult == null) {
|
||||
continue;
|
||||
}
|
||||
hasSuggestions = true;
|
||||
for (Suggestion<? extends Entry<? extends Option>> suggestion : shardResult) {
|
||||
List<Suggestion> list = groupedSuggestions.get(suggestion.getName());
|
||||
if (list == null) {
|
||||
list = new ArrayList<Suggest.Suggestion>();
|
||||
groupedSuggestions.put(suggestion.getName(), list);
|
||||
}
|
||||
list.add(suggestion);
|
||||
}
|
||||
|
||||
Suggest.group(groupedSuggestions, shardResult);
|
||||
}
|
||||
List<Suggestion<? extends Entry<? extends Option>>> reduced = new ArrayList<Suggestion<? extends Entry<? extends Option>>>();
|
||||
for (java.util.Map.Entry<String, List<Suggestion>> unmergedResults : groupedSuggestions.entrySet()) {
|
||||
List<Suggestion> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<Suggest.Suggestion<? extends Entry<? extends Option>>>, 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<Option> COMPARATOR = new Comparator<Suggest.Suggestion.Entry.Option>() {
|
||||
|
@ -60,14 +59,26 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex
|
|||
}
|
||||
};
|
||||
|
||||
private final XContentBuilderString name;
|
||||
|
||||
private List<Suggestion<? extends Entry<? extends Option>>> suggestions;
|
||||
|
||||
private Map<String, Suggestion<? extends Entry<? extends Option>>> suggestMap;
|
||||
|
||||
public Suggest() {
|
||||
this.name = null;
|
||||
}
|
||||
|
||||
public Suggest(XContentBuilderString name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Suggest(List<Suggestion<? extends Entry<? extends Option>>> suggestions) {
|
||||
this(null, suggestions);
|
||||
}
|
||||
|
||||
public Suggest(XContentBuilderString name, List<Suggestion<? extends Entry<? extends Option>>> suggestions) {
|
||||
this.name = name;
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
|
@ -128,19 +139,49 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex
|
|||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(Fields.SUGGEST);
|
||||
for (Suggestion<?> suggestion : suggestions) {
|
||||
suggestion.toXContent(builder, params);
|
||||
if(name == null) {
|
||||
for (Suggestion<?> suggestion : suggestions) {
|
||||
suggestion.toXContent(builder, params);
|
||||
}
|
||||
} else {
|
||||
builder.startObject(name);
|
||||
for (Suggestion<?> suggestion : suggestions) {
|
||||
suggestion.toXContent(builder, params);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
return null;
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Suggest readSuggest(StreamInput in) throws IOException {
|
||||
Suggest result = new Suggest();
|
||||
public static Suggest readSuggest(XContentBuilderString name, StreamInput in) throws IOException {
|
||||
Suggest result = new Suggest(name);
|
||||
result.readFrom(in);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<String, List<Suggest.Suggestion>> group(Map<String, List<Suggest.Suggestion>> groupedSuggestions, Suggest suggest) {
|
||||
for (Suggestion<? extends Entry<? extends Option>> suggestion : suggest) {
|
||||
List<Suggestion> list = groupedSuggestions.get(suggestion.getName());
|
||||
if (list == null) {
|
||||
list = new ArrayList<Suggest.Suggestion>();
|
||||
groupedSuggestions.put(suggestion.getName(), list);
|
||||
}
|
||||
list.add(suggestion);
|
||||
}
|
||||
return groupedSuggestions;
|
||||
}
|
||||
|
||||
public static List<Suggestion<? extends Entry<? extends Option>>> reduce(Map<String, List<Suggest.Suggestion>> groupedSuggestions) {
|
||||
List<Suggestion<? extends Entry<? extends Option>>> reduced = new ArrayList<Suggestion<? extends Entry<? extends Option>>>(groupedSuggestions.size());
|
||||
for (java.util.Map.Entry<String, List<Suggestion>> unmergedResults : groupedSuggestions.entrySet()) {
|
||||
List<Suggestion> value = unmergedResults.getValue();
|
||||
Suggestion reduce = value.get(0).reduce(value);
|
||||
reduce.trim();
|
||||
reduced.add(reduce);
|
||||
}
|
||||
return reduced;
|
||||
}
|
||||
|
||||
/**
|
||||
* The suggestion responses corresponding with the suggestions in the request.
|
||||
|
|
|
@ -37,10 +37,19 @@ import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
|
|||
*/
|
||||
public class SuggestBuilder implements ToXContent {
|
||||
|
||||
private final String name;
|
||||
private String globalText;
|
||||
|
||||
private final List<SuggestionBuilder<?>> suggestions = new ArrayList<SuggestionBuilder<?>>();
|
||||
|
||||
public SuggestBuilder() {
|
||||
this.name = null;
|
||||
}
|
||||
|
||||
public SuggestBuilder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text to provide suggestions for. The suggest text is a required option that needs
|
||||
* to be set either via this setter or via the {@link org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder#setText(String)} method.
|
||||
|
@ -61,7 +70,7 @@ public class SuggestBuilder implements ToXContent {
|
|||
suggestions.add(suggestion);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all suggestions with the defined names.
|
||||
*/
|
||||
|
@ -71,7 +80,12 @@ public class SuggestBuilder implements ToXContent {
|
|||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject("suggest");
|
||||
if(name == null) {
|
||||
builder.startObject();
|
||||
} else {
|
||||
builder.startObject(name);
|
||||
}
|
||||
|
||||
if (globalText != null) {
|
||||
builder.field("text", globalText);
|
||||
}
|
||||
|
|
|
@ -21,9 +21,9 @@ package org.elasticsearch.search.suggest;
|
|||
import java.io.IOException;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
|
||||
public interface SuggestContextParser {
|
||||
public SuggestionSearchContext.SuggestionContext parse(XContentParser parser, SearchContext context) throws IOException;
|
||||
public SuggestionSearchContext.SuggestionContext parse(XContentParser parser, MapperService mapperService) throws IOException;
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@ import java.io.IOException;
|
|||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.ElasticSearchIllegalArgumentException;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.search.SearchParseElement;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext;
|
||||
|
@ -38,6 +39,11 @@ public class SuggestParseElement implements SearchParseElement {
|
|||
|
||||
@Override
|
||||
public void parse(XContentParser parser, SearchContext context) throws Exception {
|
||||
SuggestionSearchContext suggestionSearchContext = parseInternal(parser, context.mapperService());
|
||||
context.suggest(suggestionSearchContext);
|
||||
}
|
||||
|
||||
public SuggestionSearchContext parseInternal(XContentParser parser, MapperService mapperService) throws IOException {
|
||||
SuggestionSearchContext suggestionSearchContext = new SuggestionSearchContext();
|
||||
BytesRef globalText = null;
|
||||
String fieldName = null;
|
||||
|
@ -76,20 +82,20 @@ public class SuggestParseElement implements SearchParseElement {
|
|||
} else {
|
||||
throw new ElasticSearchIllegalArgumentException("Suggester[" + fieldName + "] not supported");
|
||||
}
|
||||
parseAndVerify(parser, context, suggestionSearchContext, globalText, suggestionName, suggestText, contextParser);
|
||||
parseAndVerify(parser, mapperService, suggestionSearchContext, globalText, suggestionName, suggestText, contextParser);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
context.suggest(suggestionSearchContext);
|
||||
return suggestionSearchContext;
|
||||
}
|
||||
|
||||
public void parseAndVerify(XContentParser parser, SearchContext context, SuggestionSearchContext suggestionSearchContext,
|
||||
public void parseAndVerify(XContentParser parser, MapperService mapperService, SuggestionSearchContext suggestionSearchContext,
|
||||
BytesRef globalText, String suggestionName, BytesRef suggestText, SuggestContextParser suggestParser ) throws IOException {
|
||||
SuggestionContext suggestion = suggestParser.parse(parser, context);
|
||||
SuggestionContext suggestion = suggestParser.parse(parser, mapperService);
|
||||
suggestion.setText(suggestText);
|
||||
SuggestUtils.verifySuggestion(context, globalText, suggestion);
|
||||
SuggestUtils.verifySuggestion(mapperService, globalText, suggestion);
|
||||
suggestionSearchContext.addSuggestion(suggestionName, suggestion);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.util.CharsRef;
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
|
@ -60,27 +61,27 @@ public class SuggestPhase extends AbstractComponent implements SearchPhase {
|
|||
|
||||
@Override
|
||||
public void execute(SearchContext context) throws ElasticSearchException {
|
||||
try {
|
||||
SuggestionSearchContext suggest = context.suggest();
|
||||
final SuggestionSearchContext suggest = context.suggest();
|
||||
if (suggest == null) {
|
||||
return;
|
||||
}
|
||||
context.queryResult().suggest(execute(suggest, context.searcher().getIndexReader()));
|
||||
}
|
||||
|
||||
public Suggest execute(SuggestionSearchContext suggest, IndexReader reader) {
|
||||
try {
|
||||
CharsRef spare = new CharsRef(); // Maybe add CharsRef to CacheRecycler?
|
||||
final List<Suggestion<? extends Entry<? extends Option>>> suggestions = new ArrayList<Suggestion<? extends Entry<? extends Option>>>(suggest.suggestions().size());
|
||||
for (Map.Entry<String, SuggestionSearchContext.SuggestionContext> entry : suggest.suggestions().entrySet()) {
|
||||
SuggestionSearchContext.SuggestionContext suggestion = entry.getValue();
|
||||
Suggester<SuggestionContext> suggester = suggestion.getSuggester();
|
||||
Suggestion<? extends Entry<? extends Option>> result = suggester.execute(entry.getKey(), suggestion, context, spare);
|
||||
Suggestion<? extends Entry<? extends Option>> result = suggester.execute(entry.getKey(), suggestion, reader, spare);
|
||||
assert entry.getKey().equals(result.name);
|
||||
suggestions.add(result);
|
||||
}
|
||||
context.queryResult().suggest(new Suggest(suggestions));
|
||||
return new Suggest(Suggest.Fields.SUGGEST, suggestions);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticSearchException("I/O exception during suggest phase", e);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.elasticsearch.index.analysis.CustomAnalyzer;
|
|||
import org.elasticsearch.index.analysis.NamedAnalyzer;
|
||||
import org.elasticsearch.index.analysis.ShingleTokenFilterFactory;
|
||||
import org.elasticsearch.index.analysis.TokenFilterFactory;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext;
|
||||
|
||||
|
@ -232,12 +233,12 @@ public final class SuggestUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static boolean parseSuggestContext(XContentParser parser, SearchContext context, String fieldName,
|
||||
public static boolean parseSuggestContext(XContentParser parser, MapperService mapperService, String fieldName,
|
||||
SuggestionSearchContext.SuggestionContext suggestion) throws IOException {
|
||||
|
||||
if ("analyzer".equals(fieldName)) {
|
||||
String analyzerName = parser.text();
|
||||
Analyzer analyzer = context.mapperService().analysisService().analyzer(analyzerName);
|
||||
Analyzer analyzer = mapperService.analysisService().analyzer(analyzerName);
|
||||
if (analyzer == null) {
|
||||
throw new ElasticSearchIllegalArgumentException("Analyzer [" + analyzerName + "] doesn't exists");
|
||||
}
|
||||
|
@ -256,7 +257,7 @@ public final class SuggestUtils {
|
|||
}
|
||||
|
||||
|
||||
public static void verifySuggestion(SearchContext context, BytesRef globalText, SuggestionContext suggestion) {
|
||||
public static void verifySuggestion(MapperService mapperService, BytesRef globalText, SuggestionContext suggestion) {
|
||||
// Verify options and set defaults
|
||||
if (suggestion.getField() == null) {
|
||||
throw new ElasticSearchIllegalArgumentException("The required field option is missing");
|
||||
|
@ -268,7 +269,7 @@ public final class SuggestUtils {
|
|||
suggestion.setText(globalText);
|
||||
}
|
||||
if (suggestion.getAnalyzer() == null) {
|
||||
suggestion.setAnalyzer(context.mapperService().searchAnalyzer());
|
||||
suggestion.setAnalyzer(mapperService.searchAnalyzer());
|
||||
}
|
||||
if (suggestion.getShardSize() == -1) {
|
||||
suggestion.setShardSize(Math.max(suggestion.getSize(), 5));
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
package org.elasticsearch.search.suggest;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.util.CharsRef;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.search.suggest.Suggest.Suggestion;
|
||||
import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry;
|
||||
import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option;
|
||||
|
||||
public interface Suggester<T extends SuggestionSearchContext.SuggestionContext> {
|
||||
public abstract Suggestion<? extends Entry<? extends Option>> execute(String name, T suggestion, SearchContext context, CharsRef spare)
|
||||
public abstract Suggestion<? extends Entry<? extends Option>> execute(String name, T suggestion, IndexReader indexReader, CharsRef spare)
|
||||
throws IOException;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public class SuggestionSearchContext {
|
|||
this.text = text;
|
||||
}
|
||||
|
||||
protected SuggestionContext(Suggester suggester) {
|
||||
protected SuggestionContext(Suggester suggester) {
|
||||
this.suggester = suggester;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ import org.elasticsearch.ElasticSearchIllegalArgumentException;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentParser.Token;
|
||||
import org.elasticsearch.index.analysis.ShingleTokenFilterFactory;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.search.suggest.SuggestContextParser;
|
||||
import org.elasticsearch.search.suggest.SuggestUtils;
|
||||
import org.elasticsearch.search.suggest.SuggestionSearchContext;
|
||||
|
@ -54,7 +54,7 @@ public final class PhraseSuggestParser implements SuggestContextParser {
|
|||
|
||||
private final PhraseSuggester suggester = new PhraseSuggester();
|
||||
|
||||
public SuggestionSearchContext.SuggestionContext parse(XContentParser parser, SearchContext context) throws IOException {
|
||||
public SuggestionSearchContext.SuggestionContext parse(XContentParser parser, MapperService mapperService) throws IOException {
|
||||
PhraseSuggestionContext suggestion = new PhraseSuggestionContext(suggester);
|
||||
XContentParser.Token token;
|
||||
String fieldName = null;
|
||||
|
@ -63,7 +63,7 @@ public final class PhraseSuggestParser implements SuggestContextParser {
|
|||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
fieldName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
if (!SuggestUtils.parseSuggestContext(parser, context, fieldName, suggestion)) {
|
||||
if (!SuggestUtils.parseSuggestContext(parser, mapperService, fieldName, suggestion)) {
|
||||
if ("real_word_error_likelihood".equals(fieldName)) {
|
||||
suggestion.setRealWordErrorLikelihood(parser.floatValue());
|
||||
if (suggestion.realworldErrorLikelyhood() <= 0.0) {
|
||||
|
@ -103,10 +103,10 @@ public final class PhraseSuggestParser implements SuggestContextParser {
|
|||
fieldName = parser.currentName();
|
||||
}
|
||||
if (token.isValue()) {
|
||||
parseCandidateGenerator(parser, context, fieldName, generator);
|
||||
parseCandidateGenerator(parser, mapperService, fieldName, generator);
|
||||
}
|
||||
}
|
||||
verifyGenerator(context, generator);
|
||||
verifyGenerator(generator);
|
||||
suggestion.addGenerator(generator);
|
||||
}
|
||||
} else {
|
||||
|
@ -128,7 +128,7 @@ public final class PhraseSuggestParser implements SuggestContextParser {
|
|||
}
|
||||
|
||||
if (!gramSizeSet || suggestion.generators().isEmpty()) {
|
||||
final ShingleTokenFilterFactory.Factory shingleFilterFactory = SuggestUtils.getShingleFilterFactory(suggestion.getAnalyzer() == null ? context.mapperService().fieldSearchAnalyzer(suggestion.getField()) : suggestion.getAnalyzer()); ;
|
||||
final ShingleTokenFilterFactory.Factory shingleFilterFactory = SuggestUtils.getShingleFilterFactory(suggestion.getAnalyzer() == null ? mapperService.fieldSearchAnalyzer(suggestion.getField()) : suggestion.getAnalyzer()); ;
|
||||
if (!gramSizeSet) {
|
||||
// try to detect the shingle size
|
||||
if (shingleFilterFactory != null) {
|
||||
|
@ -257,14 +257,14 @@ public final class PhraseSuggestParser implements SuggestContextParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void verifyGenerator(SearchContext context, PhraseSuggestionContext.DirectCandidateGenerator suggestion) {
|
||||
private void verifyGenerator(PhraseSuggestionContext.DirectCandidateGenerator suggestion) {
|
||||
// Verify options and set defaults
|
||||
if (suggestion.field() == null) {
|
||||
throw new ElasticSearchIllegalArgumentException("The required field option is missing");
|
||||
}
|
||||
}
|
||||
|
||||
private void parseCandidateGenerator(XContentParser parser, SearchContext context, String fieldName,
|
||||
private void parseCandidateGenerator(XContentParser parser, MapperService mapperService, String fieldName,
|
||||
PhraseSuggestionContext.DirectCandidateGenerator generator) throws IOException {
|
||||
if (!SuggestUtils.parseDirectSpellcheckerSettings(parser, fieldName, generator)) {
|
||||
if ("field".equals(fieldName)) {
|
||||
|
@ -273,14 +273,14 @@ public final class PhraseSuggestParser implements SuggestContextParser {
|
|||
generator.size(parser.intValue());
|
||||
} else if ("pre_filter".equals(fieldName) || "preFilter".equals(fieldName)) {
|
||||
String analyzerName = parser.text();
|
||||
Analyzer analyzer = context.mapperService().analysisService().analyzer(analyzerName);
|
||||
Analyzer analyzer = mapperService.analysisService().analyzer(analyzerName);
|
||||
if (analyzer == null) {
|
||||
throw new ElasticSearchIllegalArgumentException("Analyzer [" + analyzerName + "] doesn't exists");
|
||||
}
|
||||
generator.preFilter(analyzer);
|
||||
} else if ("post_filter".equals(fieldName) || "postFilter".equals(fieldName)) {
|
||||
String analyzerName = parser.text();
|
||||
Analyzer analyzer = context.mapperService().analysisService().analyzer(analyzerName);
|
||||
Analyzer analyzer = mapperService.analysisService().analyzer(analyzerName);
|
||||
if (analyzer == null) {
|
||||
throw new ElasticSearchIllegalArgumentException("Analyzer [" + analyzerName + "] doesn't exists");
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.apache.lucene.util.CharsRef;
|
|||
import org.apache.lucene.util.UnicodeUtil;
|
||||
import org.elasticsearch.common.text.StringText;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.search.suggest.Suggest.Suggestion;
|
||||
import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry;
|
||||
import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option;
|
||||
|
@ -50,8 +49,7 @@ final class PhraseSuggester implements Suggester<PhraseSuggestionContext> {
|
|||
*/
|
||||
@Override
|
||||
public Suggestion<? extends Entry<? extends Option>> execute(String name, PhraseSuggestionContext suggestion,
|
||||
SearchContext context, CharsRef spare) throws IOException {
|
||||
final IndexReader indexReader = context.searcher().getIndexReader();
|
||||
IndexReader indexReader, CharsRef spare) throws IOException {
|
||||
double realWordErrorLikelihood = suggestion.realworldErrorLikelyhood();
|
||||
List<PhraseSuggestionContext.DirectCandidateGenerator> generators = suggestion.generators();
|
||||
CandidateGenerator[] gens = new CandidateGenerator[generators.size()];
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.io.IOException;
|
|||
|
||||
import org.elasticsearch.ElasticSearchIllegalArgumentException;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.search.suggest.DirectSpellcheckerSettings;
|
||||
import org.elasticsearch.search.suggest.SuggestContextParser;
|
||||
import org.elasticsearch.search.suggest.SuggestUtils;
|
||||
|
@ -31,7 +31,7 @@ import org.elasticsearch.search.suggest.SuggestionSearchContext;
|
|||
public final class TermSuggestParser implements SuggestContextParser {
|
||||
private final TermSuggester suggester = new TermSuggester();
|
||||
|
||||
public SuggestionSearchContext.SuggestionContext parse(XContentParser parser, SearchContext context) throws IOException {
|
||||
public SuggestionSearchContext.SuggestionContext parse(XContentParser parser, MapperService mapperService) throws IOException {
|
||||
XContentParser.Token token;
|
||||
String fieldName = null;
|
||||
TermSuggestionContext suggestion = new TermSuggestionContext(suggester);
|
||||
|
@ -40,7 +40,7 @@ public final class TermSuggestParser implements SuggestContextParser {
|
|||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
fieldName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
parseTokenValue(parser, context, fieldName, suggestion, settings);
|
||||
parseTokenValue(parser, mapperService, fieldName, suggestion, settings);
|
||||
} else {
|
||||
throw new ElasticSearchIllegalArgumentException("suggester[term] doesn't support field [" + fieldName + "]");
|
||||
}
|
||||
|
@ -48,9 +48,9 @@ public final class TermSuggestParser implements SuggestContextParser {
|
|||
return suggestion;
|
||||
}
|
||||
|
||||
private void parseTokenValue(XContentParser parser, SearchContext context, String fieldName, TermSuggestionContext suggestion,
|
||||
private void parseTokenValue(XContentParser parser, MapperService mapperService, String fieldName, TermSuggestionContext suggestion,
|
||||
DirectSpellcheckerSettings settings) throws IOException {
|
||||
if (!(SuggestUtils.parseSuggestContext(parser, context, fieldName, suggestion) || SuggestUtils.parseDirectSpellcheckerSettings(
|
||||
if (!(SuggestUtils.parseSuggestContext(parser, mapperService, fieldName, suggestion) || SuggestUtils.parseDirectSpellcheckerSettings(
|
||||
parser, fieldName, settings))) {
|
||||
throw new ElasticSearchIllegalArgumentException("suggester[term] doesn't support [" + fieldName + "]");
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.elasticsearch.common.bytes.BytesArray;
|
|||
import org.elasticsearch.common.text.BytesText;
|
||||
import org.elasticsearch.common.text.StringText;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.search.suggest.SuggestUtils;
|
||||
import org.elasticsearch.search.suggest.Suggester;
|
||||
import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext;
|
||||
|
@ -40,7 +39,7 @@ import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContex
|
|||
final class TermSuggester implements Suggester<TermSuggestionContext> {
|
||||
|
||||
@Override
|
||||
public TermSuggestion execute(String name, TermSuggestionContext suggestion, SearchContext context, CharsRef spare) throws IOException {
|
||||
public TermSuggestion execute(String name, TermSuggestionContext suggestion, IndexReader indexReader, CharsRef spare) throws IOException {
|
||||
DirectSpellChecker directSpellChecker = SuggestUtils.getDirectSpellChecker(suggestion.getDirectSpellCheckerSettings());
|
||||
|
||||
TermSuggestion response = new TermSuggestion(
|
||||
|
@ -48,7 +47,6 @@ final class TermSuggester implements Suggester<TermSuggestionContext> {
|
|||
);
|
||||
List<Token> tokens = queryTerms(suggestion, spare);
|
||||
for (Token token : tokens) {
|
||||
IndexReader indexReader = context.searcher().getIndexReader();
|
||||
// TODO: Extend DirectSpellChecker in 4.1, to get the raw suggested words as BytesRef
|
||||
SuggestWord[] suggestedWords = directSpellChecker.suggestSimilar(
|
||||
token.term, suggestion.getShardSize(), indexReader, suggestion.getDirectSpellCheckerSettings().suggestMode()
|
||||
|
|
|
@ -220,7 +220,7 @@ public class DocumentActionsTests extends AbstractNodesTests {
|
|||
for (int i = 0; i < 5; i++) {
|
||||
// test successful
|
||||
CountResponse countResponse = client1.prepareCount("test").setQuery(termQuery("_type", "type1")).setOperationThreading(BroadcastOperationThreading.NO_THREADS).execute().actionGet();
|
||||
assertThat("Failures " + countResponse.getShardFailures(), countResponse.getShardFailures().size(), equalTo(0));
|
||||
assertThat("Failures " + countResponse.getShardFailures(), countResponse.getShardFailures()==null?0:countResponse.getShardFailures().length, equalTo(0));
|
||||
assertThat(countResponse.getCount(), equalTo(2l));
|
||||
assertThat(countResponse.getSuccessfulShards(), equalTo(5));
|
||||
assertThat(countResponse.getFailedShards(), equalTo(0));
|
||||
|
@ -244,7 +244,7 @@ public class DocumentActionsTests extends AbstractNodesTests {
|
|||
|
||||
// count with no query is a match all one
|
||||
countResponse = client1.prepareCount("test").execute().actionGet();
|
||||
assertThat("Failures " + countResponse.getShardFailures(), countResponse.getShardFailures().size(), equalTo(0));
|
||||
assertThat("Failures " + countResponse.getShardFailures(), countResponse.getShardFailures()==null?0:countResponse.getShardFailures().length, equalTo(0));
|
||||
assertThat(countResponse.getCount(), equalTo(2l));
|
||||
assertThat(countResponse.getSuccessfulShards(), equalTo(5));
|
||||
assertThat(countResponse.getFailedShards(), equalTo(0));
|
||||
|
|
|
@ -894,31 +894,31 @@ public class SimpleChildQuerySearchTests extends AbstractNodesTests {
|
|||
.setQuery(topChildrenQuery("child", termQuery("c_field1", "1")))
|
||||
.execute().actionGet();
|
||||
assertThat(countResponse.getFailedShards(), equalTo(1));
|
||||
assertThat(countResponse.getShardFailures().get(0).reason().contains("top_children query hasn't executed properly"), equalTo(true));
|
||||
assertThat(countResponse.getShardFailures()[0].reason().contains("top_children query hasn't executed properly"), equalTo(true));
|
||||
|
||||
countResponse = client.prepareCount("test")
|
||||
.setQuery(hasChildQuery("child", termQuery("c_field1", "2")).scoreType("max"))
|
||||
.execute().actionGet();
|
||||
assertThat(countResponse.getFailedShards(), equalTo(1));
|
||||
assertThat(countResponse.getShardFailures().get(0).reason().contains("has_child query hasn't executed properly"), equalTo(true));
|
||||
assertThat(countResponse.getShardFailures()[0].reason().contains("has_child query hasn't executed properly"), equalTo(true));
|
||||
|
||||
countResponse = client.prepareCount("test")
|
||||
.setQuery(hasParentQuery("parent", termQuery("p_field1", "1")).scoreType("score"))
|
||||
.execute().actionGet();
|
||||
assertThat(countResponse.getFailedShards(), equalTo(1));
|
||||
assertThat(countResponse.getShardFailures().get(0).reason().contains("has_parent query hasn't executed properly"), equalTo(true));
|
||||
assertThat(countResponse.getShardFailures()[0].reason().contains("has_parent query hasn't executed properly"), equalTo(true));
|
||||
|
||||
countResponse = client.prepareCount("test")
|
||||
.setQuery(constantScoreQuery(hasChildFilter("child", termQuery("c_field1", "2"))))
|
||||
.execute().actionGet();
|
||||
assertThat(countResponse.getFailedShards(), equalTo(1));
|
||||
assertThat(countResponse.getShardFailures().get(0).reason().contains("has_child filter hasn't executed properly"), equalTo(true));
|
||||
assertThat(countResponse.getShardFailures()[0].reason().contains("has_child filter hasn't executed properly"), equalTo(true));
|
||||
|
||||
countResponse = client.prepareCount("test")
|
||||
.setQuery(constantScoreQuery(hasParentFilter("parent", termQuery("p_field1", "1"))))
|
||||
.execute().actionGet();
|
||||
assertThat(countResponse.getFailedShards(), equalTo(1));
|
||||
assertThat(countResponse.getShardFailures().get(0).reason().contains("has_parent filter hasn't executed properly"), equalTo(true));
|
||||
assertThat(countResponse.getShardFailures()[0].reason().contains("has_parent filter hasn't executed properly"), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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.test.unit.action.suggest;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||
import org.elasticsearch.action.suggest.SuggestRequestBuilder;
|
||||
import org.elasticsearch.action.suggest.SuggestResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.search.suggest.Suggest;
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder;
|
||||
import org.elasticsearch.test.integration.search.suggest.SuggestSearchTests;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*/
|
||||
@Test
|
||||
public class SuggestActionTests extends SuggestSearchTests {
|
||||
|
||||
protected Suggest searchSuggest(Client client, String suggestText, int expectShardsFailed, SuggestionBuilder<?>... suggestions) {
|
||||
SuggestRequestBuilder builder = client.prepareSuggest();
|
||||
|
||||
if (suggestText != null) {
|
||||
builder.setSuggestText(suggestText);
|
||||
}
|
||||
for (SuggestionBuilder<?> suggestion : suggestions) {
|
||||
builder.addSuggestion(suggestion);
|
||||
}
|
||||
|
||||
SuggestResponse actionGet = builder.execute().actionGet();
|
||||
assertThat(Arrays.toString(actionGet.getShardFailures()), actionGet.getFailedShards(), equalTo(expectShardsFailed));
|
||||
if (expectShardsFailed > 0) {
|
||||
throw new SearchPhaseExecutionException("suggest", "Suggest execution failed", new ShardSearchFailure[0]);
|
||||
}
|
||||
return actionGet.getSuggest();
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue