- Added explain api. #2184
This commit is contained in:
parent
a8656c8a6f
commit
8365e7ba0b
|
@ -97,6 +97,9 @@ import org.elasticsearch.action.deletebyquery.DeleteByQueryAction;
|
|||
import org.elasticsearch.action.deletebyquery.TransportDeleteByQueryAction;
|
||||
import org.elasticsearch.action.deletebyquery.TransportIndexDeleteByQueryAction;
|
||||
import org.elasticsearch.action.deletebyquery.TransportShardDeleteByQueryAction;
|
||||
import org.elasticsearch.action.explain.ExplainAction;
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.action.explain.TransportExplainAction;
|
||||
import org.elasticsearch.action.get.*;
|
||||
import org.elasticsearch.action.index.IndexAction;
|
||||
import org.elasticsearch.action.index.TransportIndexAction;
|
||||
|
@ -220,6 +223,7 @@ public class ActionModule extends AbstractModule {
|
|||
registerAction(MultiSearchAction.INSTANCE, TransportMultiSearchAction.class);
|
||||
registerAction(MoreLikeThisAction.INSTANCE, TransportMoreLikeThisAction.class);
|
||||
registerAction(PercolateAction.INSTANCE, TransportPercolateAction.class);
|
||||
registerAction(ExplainAction.INSTANCE, TransportExplainAction.class);
|
||||
|
||||
// register Name -> GenericAction Map that can be injected to instances.
|
||||
MapBinder<String, GenericAction> actionsBinder
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.explain;
|
||||
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.client.Client;
|
||||
|
||||
/**
|
||||
* Entry point for the explain feature.
|
||||
*/
|
||||
public class ExplainAction extends Action<ExplainRequest, ExplainResponse, ExplainRequestBuilder> {
|
||||
|
||||
public static final ExplainAction INSTANCE = new ExplainAction();
|
||||
public static final String NAME = "explain";
|
||||
|
||||
private ExplainAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
public ExplainRequestBuilder newRequestBuilder(Client client) {
|
||||
return new ExplainRequestBuilder(client);
|
||||
}
|
||||
|
||||
public ExplainResponse newResponse() {
|
||||
return new ExplainResponse();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* 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.explain;
|
||||
|
||||
import org.elasticsearch.ElasticSearchGenerationException;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.ValidateActions;
|
||||
import org.elasticsearch.action.support.single.shard.SingleShardOperationRequest;
|
||||
import org.elasticsearch.client.Requests;
|
||||
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.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Explain request encapsulating the explain query and document identifier to get an explanation for.
|
||||
*/
|
||||
public class ExplainRequest extends SingleShardOperationRequest {
|
||||
|
||||
private static final XContentType contentType = Requests.CONTENT_TYPE;
|
||||
|
||||
private String type = "_all";
|
||||
private String id;
|
||||
private String routing;
|
||||
private String preference;
|
||||
private BytesReference source;
|
||||
private boolean sourceUnsafe;
|
||||
|
||||
private String[] filteringAlias = Strings.EMPTY_ARRAY;
|
||||
|
||||
ExplainRequest(){
|
||||
}
|
||||
|
||||
public ExplainRequest(String index, String type, String id) {
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ExplainRequest index(String index) {
|
||||
this.index = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public ExplainRequest type(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ExplainRequest id(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String routing() {
|
||||
return routing;
|
||||
}
|
||||
|
||||
public ExplainRequest routing(String routing) {
|
||||
this.routing = routing;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple sets the routing. Since the parent is only used to get to the right shard.
|
||||
*/
|
||||
public ExplainRequest parent(String parent) {
|
||||
this.routing = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String preference() {
|
||||
return preference;
|
||||
}
|
||||
|
||||
public ExplainRequest preference(String preference) {
|
||||
this.preference = preference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BytesReference source() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public boolean sourceUnsafe() {
|
||||
return sourceUnsafe;
|
||||
}
|
||||
|
||||
public ExplainRequest source(ExplainSourceBuilder sourceBuilder) {
|
||||
this.source = sourceBuilder.buildAsBytes(contentType);
|
||||
this.sourceUnsafe = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExplainRequest source(BytesReference querySource, boolean unsafe) {
|
||||
this.source = querySource;
|
||||
this.sourceUnsafe = unsafe;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String[] filteringAlias() {
|
||||
return filteringAlias;
|
||||
}
|
||||
|
||||
public void filteringAlias(String[] filteringAlias) {
|
||||
if (filteringAlias == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.filteringAlias = filteringAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplainRequest listenerThreaded(boolean threadedListener) {
|
||||
super.listenerThreaded(threadedListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplainRequest operationThreaded(boolean threadedOperation) {
|
||||
super.operationThreaded(threadedOperation);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeLocalFork() {
|
||||
if (sourceUnsafe) {
|
||||
source = source.copyBytesArray();
|
||||
sourceUnsafe = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = super.validate();
|
||||
if (type == null) {
|
||||
validationException = ValidateActions.addValidationError("type is missing", validationException);
|
||||
}
|
||||
if (id == null) {
|
||||
validationException = ValidateActions.addValidationError("id is missing", validationException);
|
||||
}
|
||||
if (source == null) {
|
||||
validationException = ValidateActions.addValidationError("source is missing", validationException);
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
type = in.readString();
|
||||
id = in.readString();
|
||||
routing = in.readOptionalString();
|
||||
preference = in.readOptionalString();
|
||||
source = in.readBytesReference();
|
||||
sourceUnsafe = false;
|
||||
filteringAlias = in.readStringArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeString(type);
|
||||
out.writeString(id);
|
||||
out.writeOptionalString(routing);
|
||||
out.writeOptionalString(preference);
|
||||
out.writeBytesReference(source);
|
||||
out.writeStringArray(filteringAlias);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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.explain;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.BaseRequestBuilder;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A builder for {@link ExplainRequest}.
|
||||
*/
|
||||
public class ExplainRequestBuilder extends BaseRequestBuilder<ExplainRequest, ExplainResponse> {
|
||||
|
||||
private ExplainSourceBuilder sourceBuilder;
|
||||
|
||||
ExplainRequestBuilder(Client client) {
|
||||
super(client, new ExplainRequest());
|
||||
}
|
||||
|
||||
public ExplainRequestBuilder(Client client, String index, String type, String id) {
|
||||
super(client, new ExplainRequest());
|
||||
request().index(index);
|
||||
request().type(type);
|
||||
request().id(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index to get a score explanation for.
|
||||
*/
|
||||
public ExplainRequestBuilder setIndex(String index) {
|
||||
request().index(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type to get a score explanation for.
|
||||
*/
|
||||
public ExplainRequestBuilder setType(String type) {
|
||||
request().type(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id to get a score explanation for.
|
||||
*/
|
||||
public ExplainRequestBuilder setId(String id) {
|
||||
request().id(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the routing for sharding.
|
||||
*/
|
||||
public ExplainRequestBuilder setRouting(String routing) {
|
||||
request().routing(routing);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple sets the routing. Since the parent is only used to get to the right shard.
|
||||
*/
|
||||
public ExplainRequestBuilder setParent(String parent) {
|
||||
request().parent(parent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shard preference.
|
||||
*/
|
||||
public ExplainRequestBuilder setPreference(String preference) {
|
||||
request().preference(preference);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query to get a score explanation for.
|
||||
*/
|
||||
public ExplainRequestBuilder setQuery(QueryBuilder queryBuilder) {
|
||||
sourceBuilder().query(queryBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExplainRequestBuilder setSource(BytesReference querySource, boolean unsafe) {
|
||||
request().source(querySource, unsafe);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the actual explain action should occur in a different thread if executed locally.
|
||||
*/
|
||||
public ExplainRequestBuilder operationThreaded(boolean threadedOperation) {
|
||||
request().operationThreaded(threadedOperation);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void doExecute(ActionListener<ExplainResponse> listener) {
|
||||
if (sourceBuilder != null) {
|
||||
request.source(sourceBuilder);
|
||||
}
|
||||
|
||||
client.explain(request, listener);
|
||||
}
|
||||
|
||||
private ExplainSourceBuilder sourceBuilder() {
|
||||
if (sourceBuilder == null) {
|
||||
sourceBuilder = new ExplainSourceBuilder();
|
||||
}
|
||||
return sourceBuilder;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.explain;
|
||||
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.common.lucene.Lucene.readExplanation;
|
||||
import static org.elasticsearch.common.lucene.Lucene.writeExplanation;
|
||||
|
||||
/**
|
||||
* Response containing the score explanation.
|
||||
*/
|
||||
public class ExplainResponse implements ActionResponse {
|
||||
|
||||
private Explanation explanation;
|
||||
private boolean exists;
|
||||
|
||||
ExplainResponse() {
|
||||
}
|
||||
|
||||
public ExplainResponse(boolean exists) {
|
||||
this.exists = exists;
|
||||
}
|
||||
|
||||
public ExplainResponse(boolean exists, Explanation explanation) {
|
||||
this.exists = exists;
|
||||
this.explanation = explanation;
|
||||
}
|
||||
|
||||
public Explanation getExplanation() {
|
||||
return explanation();
|
||||
}
|
||||
|
||||
public Explanation explanation() {
|
||||
return explanation;
|
||||
}
|
||||
|
||||
public boolean isMatch() {
|
||||
return match();
|
||||
}
|
||||
|
||||
public boolean match() {
|
||||
return explanation != null && explanation.isMatch();
|
||||
}
|
||||
|
||||
public boolean hasExplanation() {
|
||||
return explanation != null;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return exists;
|
||||
}
|
||||
|
||||
public boolean isExists() {
|
||||
return exists();
|
||||
}
|
||||
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
exists = in.readBoolean();
|
||||
if (in.readBoolean()) {
|
||||
explanation = readExplanation(in);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeBoolean(exists);
|
||||
if (explanation == null) {
|
||||
out.writeBoolean(false);
|
||||
} else {
|
||||
out.writeBoolean(true);
|
||||
writeExplanation(out, explanation);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.explain;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilderException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExplainSourceBuilder implements ToXContent {
|
||||
|
||||
private QueryBuilder queryBuilder;
|
||||
|
||||
private BytesReference queryBinary;
|
||||
|
||||
public ExplainSourceBuilder query(QueryBuilder query) {
|
||||
this.queryBuilder = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExplainSourceBuilder query(BytesReference queryBinary) {
|
||||
this.queryBinary = queryBinary;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
if (queryBuilder != null) {
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
}
|
||||
|
||||
if (queryBinary != null) {
|
||||
if (XContentFactory.xContentType(queryBinary) == builder.contentType()) {
|
||||
builder.rawField("query", queryBinary);
|
||||
} else {
|
||||
builder.field("query_binary", queryBinary);
|
||||
}
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
public BytesReference buildAsBytes(XContentType contentType) throws SearchSourceBuilderException {
|
||||
try {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(contentType);
|
||||
toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
return builder.bytes();
|
||||
} catch (Exception e) {
|
||||
throw new SearchSourceBuilderException("Failed to build search source", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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.explain;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.*;
|
||||
import org.elasticsearch.ElasticSearchException;
|
||||
import org.elasticsearch.action.support.single.shard.TransportShardSingleOperationAction;
|
||||
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.ShardIterator;
|
||||
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.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.engine.Engine;
|
||||
import org.elasticsearch.index.mapper.ParsedDocument;
|
||||
import org.elasticsearch.index.mapper.Uid;
|
||||
import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
||||
import org.elasticsearch.index.query.ParsedQuery;
|
||||
import org.elasticsearch.index.service.IndexService;
|
||||
import org.elasticsearch.index.shard.service.IndexShard;
|
||||
import org.elasticsearch.indices.IndicesService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.SearchParseElement;
|
||||
import org.elasticsearch.search.internal.InternalSearchRequest;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.search.query.QueryParseElement;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Explain transport action. Computes the explain on the targeted shard.
|
||||
*/
|
||||
// TODO: AggregatedDfs. Currently the idf can be different then when executing a normal search with explain.
|
||||
public class TransportExplainAction extends TransportShardSingleOperationAction<ExplainRequest, ExplainResponse> {
|
||||
|
||||
private final IndicesService indicesService;
|
||||
|
||||
private final ScriptService scriptService;
|
||||
|
||||
@Inject
|
||||
public TransportExplainAction(Settings settings, ThreadPool threadPool, ClusterService clusterService,
|
||||
TransportService transportService, IndicesService indicesService,
|
||||
ScriptService scriptService) {
|
||||
super(settings, threadPool, clusterService, transportService);
|
||||
this.indicesService = indicesService;
|
||||
this.scriptService = scriptService;
|
||||
}
|
||||
|
||||
protected String transportAction() {
|
||||
return ExplainAction.NAME;
|
||||
}
|
||||
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.GET; // Or use Names.SEARCH?
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resolveRequest(ClusterState state, ExplainRequest request) {
|
||||
String concreteIndex = state.metaData().concreteIndex(request.index());
|
||||
request.filteringAlias(state.metaData().filteringAliases(concreteIndex, request.index()));
|
||||
request.index(state.metaData().concreteIndex(request.index()));
|
||||
}
|
||||
|
||||
protected ExplainResponse shardOperation(ExplainRequest request, int shardId) throws ElasticSearchException {
|
||||
IndexService indexService = indicesService.indexService(request.index());
|
||||
IndexShard indexShard = indexService.shardSafe(shardId);
|
||||
Term uidTerm = UidFieldMapper.TERM_FACTORY.createTerm(Uid.createUid(request.type(), request.id()));
|
||||
Engine.GetResult result = indexShard.get(new Engine.Get(false, uidTerm));
|
||||
if (!result.exists()) {
|
||||
return new ExplainResponse(false);
|
||||
}
|
||||
|
||||
SearchContext context = new SearchContext(
|
||||
0,
|
||||
new InternalSearchRequest().types(new String[]{request.type()})
|
||||
.filteringAliases(request.filteringAlias()),
|
||||
null, indexShard.searcher(), indexService, indexShard,
|
||||
scriptService
|
||||
);
|
||||
SearchContext.setCurrent(context);
|
||||
context.parsedQuery(retrieveParsedQuery(request, indexService));
|
||||
context.preProcess();
|
||||
int topLevelDocId = result.docIdAndVersion().docId + result.docIdAndVersion().docStart;
|
||||
|
||||
try {
|
||||
Explanation explanation = context.searcher().explain(context.query(), topLevelDocId);
|
||||
return new ExplainResponse(true, explanation);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticSearchException("Could not explain", e);
|
||||
} finally {
|
||||
context.release();
|
||||
SearchContext.removeCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
private ParsedQuery retrieveParsedQuery(ExplainRequest request, IndexService indexService) {
|
||||
try {
|
||||
XContentParser parser = XContentHelper.createParser(request.source());
|
||||
for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
String fieldName = parser.currentName();
|
||||
if ("query".equals(fieldName)) {
|
||||
return indexService.queryParserService().parse(parser);
|
||||
} else if ("query_binary".equals(fieldName)) {
|
||||
byte[] querySource = parser.binaryValue();
|
||||
XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource);
|
||||
return indexService.queryParserService().parse(qSourceParser);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ElasticSearchException("Couldn't parse query from source.", e);
|
||||
}
|
||||
|
||||
throw new ElasticSearchException("No query specified");
|
||||
}
|
||||
|
||||
protected ExplainRequest newRequest() {
|
||||
return new ExplainRequest();
|
||||
}
|
||||
|
||||
protected ExplainResponse newResponse() {
|
||||
return new ExplainResponse();
|
||||
}
|
||||
|
||||
protected ClusterBlockException checkGlobalBlock(ClusterState state, ExplainRequest request) {
|
||||
return state.blocks().globalBlockedException(ClusterBlockLevel.READ);
|
||||
}
|
||||
|
||||
protected ClusterBlockException checkRequestBlock(ClusterState state, ExplainRequest request) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.READ, request.index());
|
||||
}
|
||||
|
||||
protected ShardIterator shards(ClusterState state, ExplainRequest request) throws ElasticSearchException {
|
||||
return clusterService.operationRouting()
|
||||
.getShards(clusterService.state(), request.index(), request.type(), request.id(), request.routing(), request.preference());
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Explain action.
|
||||
*/
|
||||
package org.elasticsearch.action.explain;
|
|
@ -91,6 +91,9 @@ public abstract class SingleShardOperationRequest implements ActionRequest {
|
|||
return this;
|
||||
}
|
||||
|
||||
protected void beforeLocalFork() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
index = in.readUTF();
|
||||
|
|
|
@ -149,6 +149,7 @@ public abstract class TransportShardSingleOperationAction<Request extends Single
|
|||
|
||||
if (shardRouting.currentNodeId().equals(nodes.localNodeId())) {
|
||||
if (request.operationThreaded()) {
|
||||
request.beforeLocalFork();
|
||||
threadPool.executor(executor).execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
|
@ -32,6 +32,9 @@ import org.elasticsearch.action.delete.DeleteResponse;
|
|||
import org.elasticsearch.action.deletebyquery.DeleteByQueryRequest;
|
||||
import org.elasticsearch.action.deletebyquery.DeleteByQueryRequestBuilder;
|
||||
import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;
|
||||
import org.elasticsearch.action.explain.ExplainRequest;
|
||||
import org.elasticsearch.action.explain.ExplainRequestBuilder;
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.action.get.*;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||
|
@ -431,4 +434,29 @@ public interface Client {
|
|||
* @param type The type of the doc
|
||||
*/
|
||||
PercolateRequestBuilder preparePercolate(String index, String type);
|
||||
|
||||
/**
|
||||
* Computes a score explanation for the specified request.
|
||||
*
|
||||
* @param index The index this explain is targeted for
|
||||
* @param type The type this explain is targeted for
|
||||
* @param id The document identifier this explain is targeted for
|
||||
*/
|
||||
ExplainRequestBuilder prepareExplain(String index, String type, String id);
|
||||
|
||||
/**
|
||||
* Computes a score explanation for the specified request.
|
||||
*
|
||||
* @param request The request encapsulating the query and document identifier to compute a score explanation for
|
||||
*/
|
||||
ActionFuture<ExplainResponse> explain(ExplainRequest request);
|
||||
|
||||
/**
|
||||
* Computes a score explanation for the specified request.
|
||||
*
|
||||
* @param request The request encapsulating the query and document identifier to compute a score explanation for
|
||||
* @param listener A listener to be notified of the result
|
||||
*/
|
||||
void explain(ExplainRequest request, ActionListener<ExplainResponse> listener);
|
||||
|
||||
}
|
|
@ -36,6 +36,10 @@ import org.elasticsearch.action.deletebyquery.DeleteByQueryAction;
|
|||
import org.elasticsearch.action.deletebyquery.DeleteByQueryRequest;
|
||||
import org.elasticsearch.action.deletebyquery.DeleteByQueryRequestBuilder;
|
||||
import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;
|
||||
import org.elasticsearch.action.explain.ExplainAction;
|
||||
import org.elasticsearch.action.explain.ExplainRequest;
|
||||
import org.elasticsearch.action.explain.ExplainRequestBuilder;
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.action.get.*;
|
||||
import org.elasticsearch.action.index.IndexAction;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
|
@ -285,4 +289,19 @@ public abstract class AbstractClient implements InternalClient {
|
|||
public PercolateRequestBuilder preparePercolate(String index, String type) {
|
||||
return new PercolateRequestBuilder(this, index, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExplainRequestBuilder prepareExplain(String index, String type, String id) {
|
||||
return new ExplainRequestBuilder(this, index, type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<ExplainResponse> explain(ExplainRequest request) {
|
||||
return execute(ExplainAction.INSTANCE, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void explain(ExplainRequest request, ActionListener<ExplainResponse> listener) {
|
||||
execute(ExplainAction.INSTANCE, request, listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ import org.elasticsearch.action.delete.DeleteRequest;
|
|||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.deletebyquery.DeleteByQueryRequest;
|
||||
import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;
|
||||
import org.elasticsearch.action.explain.ExplainRequest;
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.get.MultiGetRequest;
|
||||
|
@ -425,4 +427,14 @@ public class TransportClient extends AbstractClient {
|
|||
public void percolate(PercolateRequest request, ActionListener<PercolateResponse> listener) {
|
||||
internalClient.percolate(request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<ExplainResponse> explain(ExplainRequest request) {
|
||||
return internalClient.explain(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void explain(ExplainRequest request, ActionListener<ExplainResponse> listener) {
|
||||
internalClient.explain(request, listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,23 +38,25 @@ public class UidField extends AbstractField {
|
|||
|
||||
public static class DocIdAndVersion {
|
||||
public final int docId;
|
||||
public final int docStart;
|
||||
public final long version;
|
||||
public final IndexReader reader;
|
||||
|
||||
public DocIdAndVersion(int docId, long version, IndexReader reader) {
|
||||
public DocIdAndVersion(int docId, long version, IndexReader reader, int docStart) {
|
||||
this.docId = docId;
|
||||
this.version = version;
|
||||
this.reader = reader;
|
||||
this.docStart = docStart;
|
||||
}
|
||||
}
|
||||
|
||||
// this works fine for nested docs since they don't have the payload which has the version
|
||||
// so we iterate till we find the one with the payload
|
||||
public static DocIdAndVersion loadDocIdAndVersion(IndexReader reader, Term term) {
|
||||
public static DocIdAndVersion loadDocIdAndVersion(IndexReader subReader, int docStart, Term term) {
|
||||
int docId = Lucene.NO_DOC;
|
||||
TermPositions uid = null;
|
||||
try {
|
||||
uid = reader.termPositions(term);
|
||||
uid = subReader.termPositions(term);
|
||||
if (!uid.next()) {
|
||||
return null; // no doc
|
||||
}
|
||||
|
@ -70,11 +72,11 @@ public class UidField extends AbstractField {
|
|||
continue;
|
||||
}
|
||||
byte[] payload = uid.getPayload(new byte[8], 0);
|
||||
return new DocIdAndVersion(docId, Numbers.bytesToLong(payload), reader);
|
||||
return new DocIdAndVersion(docId, Numbers.bytesToLong(payload), subReader, docStart);
|
||||
} while (uid.next());
|
||||
return new DocIdAndVersion(docId, -2, reader);
|
||||
return new DocIdAndVersion(docId, -2, subReader, docStart);
|
||||
} catch (Exception e) {
|
||||
return new DocIdAndVersion(docId, -2, reader);
|
||||
return new DocIdAndVersion(docId, -2, subReader, docStart);
|
||||
} finally {
|
||||
if (uid != null) {
|
||||
try {
|
||||
|
|
|
@ -341,13 +341,15 @@ public class RobinEngine extends AbstractIndexShardComponent implements Engine {
|
|||
Searcher searcher = searcher();
|
||||
try {
|
||||
UnicodeUtil.UTF8Result utf8 = Unicode.fromStringAsUtf8(get.uid().text());
|
||||
for (IndexReader reader : searcher.searcher().subReaders()) {
|
||||
BloomFilter filter = bloomCache.filter(reader, UidFieldMapper.NAME, asyncLoadBloomFilter);
|
||||
for (int i = 0; i < searcher.searcher().subReaders().length; i++) {
|
||||
IndexReader subReader = searcher.searcher().subReaders()[i];
|
||||
int docStart = searcher.searcher().docStarts()[i];
|
||||
BloomFilter filter = bloomCache.filter(subReader, UidFieldMapper.NAME, asyncLoadBloomFilter);
|
||||
// we know that its not there...
|
||||
if (!filter.isPresent(utf8.result, 0, utf8.length)) {
|
||||
continue;
|
||||
}
|
||||
UidField.DocIdAndVersion docIdAndVersion = UidField.loadDocIdAndVersion(reader, get.uid());
|
||||
UidField.DocIdAndVersion docIdAndVersion = UidField.loadDocIdAndVersion(subReader, docStart, get.uid());
|
||||
if (docIdAndVersion != null && docIdAndVersion.docId != Lucene.NO_DOC) {
|
||||
return new GetResult(searcher, docIdAndVersion);
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.elasticsearch.rest.action.bulk.RestBulkAction;
|
|||
import org.elasticsearch.rest.action.count.RestCountAction;
|
||||
import org.elasticsearch.rest.action.delete.RestDeleteAction;
|
||||
import org.elasticsearch.rest.action.deletebyquery.RestDeleteByQueryAction;
|
||||
import org.elasticsearch.rest.action.explain.RestExplainAction;
|
||||
import org.elasticsearch.rest.action.get.RestGetAction;
|
||||
import org.elasticsearch.rest.action.get.RestHeadAction;
|
||||
import org.elasticsearch.rest.action.get.RestMultiGetAction;
|
||||
|
@ -159,5 +160,7 @@ public class RestActionModule extends AbstractModule {
|
|||
bind(RestValidateQueryAction.class).asEagerSingleton();
|
||||
|
||||
bind(RestMoreLikeThisAction.class).asEagerSingleton();
|
||||
|
||||
bind(RestExplainAction.class).asEagerSingleton();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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.explain;
|
||||
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.elasticsearch.ElasticSearchIllegalArgumentException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.explain.ExplainRequest;
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.action.explain.ExplainSourceBuilder;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilderString;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.QueryStringQueryBuilder;
|
||||
import org.elasticsearch.rest.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
|
||||
import static org.elasticsearch.rest.RestStatus.OK;
|
||||
import static org.elasticsearch.rest.action.support.RestXContentBuilder.restContentBuilder;
|
||||
|
||||
/**
|
||||
* Rest action for computing a score explanation for specific documents.
|
||||
*/
|
||||
public class RestExplainAction extends BaseRestHandler {
|
||||
|
||||
@Inject
|
||||
public RestExplainAction(Settings settings, Client client, RestController controller) {
|
||||
super(settings, client);
|
||||
controller.registerHandler(GET, "/{index}/{type}/{id}/_explain", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequest(final RestRequest request, final RestChannel channel) {
|
||||
final ExplainRequest explainRequest = new ExplainRequest(request.param("index"), request.param("type"), request.param("id"));
|
||||
explainRequest.parent(request.param("parent"));
|
||||
explainRequest.routing(request.param("routing"));
|
||||
explainRequest.preference(request.param("preference"));
|
||||
String sourceString = request.param("source");
|
||||
String queryString = request.param("q");
|
||||
if (request.hasContent()) {
|
||||
explainRequest.source(request.content(), request.contentUnsafe());
|
||||
} else if (sourceString != null) {
|
||||
explainRequest.source(new BytesArray(request.param("source")), false);
|
||||
} else if (queryString != null) {
|
||||
QueryStringQueryBuilder queryStringBuilder = QueryBuilders.queryString(queryString);
|
||||
queryStringBuilder.defaultField(request.param("df"));
|
||||
queryStringBuilder.analyzer(request.param("analyzer"));
|
||||
queryStringBuilder.analyzeWildcard(request.paramAsBoolean("analyze_wildcard", false));
|
||||
queryStringBuilder.lowercaseExpandedTerms(request.paramAsBoolean("lowercase_expanded_terms", true));
|
||||
queryStringBuilder.lenient(request.paramAsBooleanOptional("lenient", null));
|
||||
String defaultOperator = request.param("default_operator");
|
||||
if (defaultOperator != null) {
|
||||
if ("OR".equals(defaultOperator)) {
|
||||
queryStringBuilder.defaultOperator(QueryStringQueryBuilder.Operator.OR);
|
||||
} else if ("AND".equals(defaultOperator)) {
|
||||
queryStringBuilder.defaultOperator(QueryStringQueryBuilder.Operator.AND);
|
||||
} else {
|
||||
throw new ElasticSearchIllegalArgumentException("Unsupported defaultOperator [" + defaultOperator + "], can either be [OR] or [AND]");
|
||||
}
|
||||
}
|
||||
|
||||
ExplainSourceBuilder explainSourceBuilder = new ExplainSourceBuilder();
|
||||
explainSourceBuilder.query(queryStringBuilder);
|
||||
explainRequest.source(explainSourceBuilder);
|
||||
}
|
||||
|
||||
client.explain(explainRequest, new ActionListener<ExplainResponse>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(ExplainResponse response) {
|
||||
try {
|
||||
XContentBuilder builder = restContentBuilder(request);
|
||||
builder.startObject();
|
||||
builder.field(Fields.OK, response.exists());
|
||||
builder.field(Fields.MATCHES, response.match());
|
||||
if (response.hasExplanation()) {
|
||||
builder.startObject(Fields.EXPLANATION);
|
||||
buildExplanation(builder, response.explanation());
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
channel.sendResponse(new XContentRestResponse(request, response.exists() ? OK : NOT_FOUND, builder));
|
||||
} catch (Exception e) {
|
||||
onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildExplanation(XContentBuilder builder, Explanation explanation) throws IOException {
|
||||
builder.field(Fields.VALUE, explanation.getValue());
|
||||
builder.field(Fields.DESCRIPTION, explanation.getDescription());
|
||||
Explanation[] innerExps = explanation.getDetails();
|
||||
if (innerExps != null) {
|
||||
builder.startArray(Fields.DETAILS);
|
||||
for (Explanation exp : innerExps) {
|
||||
builder.startObject();
|
||||
buildExplanation(builder, exp);
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
try {
|
||||
channel.sendResponse(new XContentThrowableRestResponse(request, e));
|
||||
} catch (IOException e1) {
|
||||
logger.error("Failed to send failure response", e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class Fields {
|
||||
static final XContentBuilderString OK = new XContentBuilderString("ok");
|
||||
static final XContentBuilderString MATCHES = new XContentBuilderString("matches");
|
||||
static final XContentBuilderString EXPLANATION = new XContentBuilderString("explanation");
|
||||
static final XContentBuilderString VALUE = new XContentBuilderString("value");
|
||||
static final XContentBuilderString DESCRIPTION = new XContentBuilderString("description");
|
||||
static final XContentBuilderString DETAILS = new XContentBuilderString("details");
|
||||
}
|
||||
}
|
|
@ -195,4 +195,14 @@ public class ContextIndexSearcher extends ExtendedIndexSearcher {
|
|||
super.search(weight, combinedFilter, collector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(Query query, int doc) throws IOException {
|
||||
if (searchContext.aliasFilter() == null) {
|
||||
return super.explain(query, doc);
|
||||
}
|
||||
|
||||
FilteredQuery filteredQuery = new FilteredQuery(query, searchContext.aliasFilter());
|
||||
return super.explain(filteredQuery, doc);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.integration.explain;
|
||||
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.index.query.FilterBuilders;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.test.integration.AbstractNodesTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class ExplainActionTests extends AbstractNodesTests {
|
||||
|
||||
protected Client client;
|
||||
|
||||
@BeforeClass
|
||||
public void startNodes() {
|
||||
startNode("node1");
|
||||
startNode("node2");
|
||||
client = client("node1");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void closeNodes() {
|
||||
client.close();
|
||||
closeAllNodes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimple() throws Exception {
|
||||
client.admin().indices().prepareDelete("test").execute().actionGet();
|
||||
client.admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder().put("index.refresh_interval", -1)
|
||||
).execute().actionGet();
|
||||
|
||||
client.prepareIndex("test", "test", "1")
|
||||
.setSource("field", "value1")
|
||||
.execute().actionGet();
|
||||
|
||||
ExplainResponse response = client.prepareExplain("test", "test", "1")
|
||||
.setQuery(QueryBuilders.matchAllQuery())
|
||||
.execute().actionGet();
|
||||
assertNotNull(response);
|
||||
assertFalse(response.exists()); // not a match b/c not realtime
|
||||
assertFalse(response.match()); // not a match b/c not realtime
|
||||
|
||||
client.admin().indices().prepareRefresh("test").execute().actionGet();
|
||||
response = client.prepareExplain("test", "test", "1")
|
||||
.setQuery(QueryBuilders.matchAllQuery())
|
||||
.execute().actionGet();
|
||||
assertNotNull(response);
|
||||
assertTrue(response.match());
|
||||
assertNotNull(response.explanation());
|
||||
assertTrue(response.explanation().isMatch());
|
||||
assertThat(response.explanation().getValue(), equalTo(1.0f));
|
||||
|
||||
client.admin().indices().prepareRefresh("test").execute().actionGet();
|
||||
response = client.prepareExplain("test", "test", "1")
|
||||
.setQuery(QueryBuilders.termQuery("field", "value2"))
|
||||
.execute().actionGet();
|
||||
assertNotNull(response);
|
||||
assertTrue(response.exists());
|
||||
assertFalse(response.match());
|
||||
assertNotNull(response.explanation());
|
||||
assertFalse(response.explanation().isMatch());
|
||||
|
||||
client.admin().indices().prepareRefresh("test").execute().actionGet();
|
||||
response = client.prepareExplain("test", "test", "1")
|
||||
.setQuery(QueryBuilders.boolQuery()
|
||||
.must(QueryBuilders.termQuery("field", "value1"))
|
||||
.must(QueryBuilders.termQuery("field", "value2"))
|
||||
)
|
||||
.execute().actionGet();
|
||||
assertNotNull(response);
|
||||
assertTrue(response.exists());
|
||||
assertFalse(response.match());
|
||||
assertNotNull(response.explanation());
|
||||
assertFalse(response.explanation().isMatch());
|
||||
assertThat(response.explanation().getDetails().length, equalTo(2));
|
||||
|
||||
response = client.prepareExplain("test", "test", "2")
|
||||
.setQuery(QueryBuilders.matchAllQuery())
|
||||
.execute().actionGet();
|
||||
assertNotNull(response);
|
||||
assertFalse(response.exists());
|
||||
assertFalse(response.match());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplainWithAlias() throws Exception {
|
||||
client.admin().indices().prepareDelete().execute().actionGet();
|
||||
client.admin().indices().prepareCreate("test")
|
||||
.execute().actionGet();
|
||||
|
||||
client.admin().indices().prepareAliases().addAlias("test", "alias1", FilterBuilders.termFilter("field2", "value2"))
|
||||
.execute().actionGet();
|
||||
client.prepareIndex("test", "test", "1").setSource("field1", "value1", "field2", "value1").execute().actionGet();
|
||||
client.admin().indices().prepareRefresh("test").execute().actionGet();
|
||||
|
||||
ExplainResponse response = client.prepareExplain("alias1", "test", "1")
|
||||
.setQuery(QueryBuilders.matchAllQuery())
|
||||
.execute().actionGet();
|
||||
assertNotNull(response);
|
||||
assertTrue(response.exists());
|
||||
assertFalse(response.match());
|
||||
}
|
||||
|
||||
}
|
|
@ -52,14 +52,14 @@ public class UidFieldTests {
|
|||
writer.addDocument(doc);
|
||||
reader = reader.reopen();
|
||||
assertThat(UidField.loadVersion(reader, new Term("_uid", "1")), equalTo(-2l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, new Term("_uid", "1")).version, equalTo(-2l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, 0, new Term("_uid", "1")).version, equalTo(-2l));
|
||||
|
||||
doc = new Document();
|
||||
doc.add(new UidField("_uid", "1", 1));
|
||||
writer.updateDocument(new Term("_uid", "1"), doc);
|
||||
reader = reader.reopen();
|
||||
assertThat(UidField.loadVersion(reader, new Term("_uid", "1")), equalTo(1l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, new Term("_uid", "1")).version, equalTo(1l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, 0, new Term("_uid", "1")).version, equalTo(1l));
|
||||
|
||||
doc = new Document();
|
||||
UidField uid = new UidField("_uid", "1", 2);
|
||||
|
@ -67,7 +67,7 @@ public class UidFieldTests {
|
|||
writer.updateDocument(new Term("_uid", "1"), doc);
|
||||
reader = reader.reopen();
|
||||
assertThat(UidField.loadVersion(reader, new Term("_uid", "1")), equalTo(2l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, new Term("_uid", "1")).version, equalTo(2l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, 0, new Term("_uid", "1")).version, equalTo(2l));
|
||||
|
||||
// test reuse of uid field
|
||||
doc = new Document();
|
||||
|
@ -76,11 +76,11 @@ public class UidFieldTests {
|
|||
writer.updateDocument(new Term("_uid", "1"), doc);
|
||||
reader = reader.reopen();
|
||||
assertThat(UidField.loadVersion(reader, new Term("_uid", "1")), equalTo(3l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, new Term("_uid", "1")).version, equalTo(3l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, 0, new Term("_uid", "1")).version, equalTo(3l));
|
||||
|
||||
writer.deleteDocuments(new Term("_uid", "1"));
|
||||
reader = reader.reopen();
|
||||
assertThat(UidField.loadVersion(reader, new Term("_uid", "1")), equalTo(-1l));
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, new Term("_uid", "1")), nullValue());
|
||||
assertThat(UidField.loadDocIdAndVersion(reader, 0, new Term("_uid", "1")), nullValue());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue