Added better error handling for has_child, has_parent and top_children.

If has_parent, has_child or top_children are executed incorrectly then a better exception is thrown. This gives a better error description when one of these queries or filters is being used in count api.

Closes #2261
This commit is contained in:
Martijn van Groningen 2012-09-18 13:20:00 +02:00
parent 2275b82549
commit d5aa35e0ea
5 changed files with 67 additions and 1 deletions

View File

@ -39,6 +39,7 @@ 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.SearchShardTarget;
import org.elasticsearch.search.internal.InternalSearchRequest;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.query.QueryPhaseExecutionException;
@ -145,9 +146,10 @@ public class TransportCountAction extends TransportBroadcastOperationAction<Coun
IndexService indexService = indicesService.indexServiceSafe(request.index());
IndexShard indexShard = indexService.shardSafe(request.shardId());
SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId());
SearchContext context = new SearchContext(0,
new InternalSearchRequest().types(request.types()).filteringAliases(request.filteringAliases()),
null, indexShard.searcher(), indexService, indexShard,
shardTarget, indexShard.searcher(), indexService, indexShard,
scriptService);
SearchContext.setCurrent(context);

View File

@ -107,6 +107,10 @@ public abstract class HasChildFilter extends Filter implements ScopePhase.Collec
}
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
if (parentDocs == null) {
throw new ElasticSearchIllegalStateException("has_child filter/query hasn't executed properly");
}
// ok to return null
return parentDocs.get(reader.getCoreCacheKey());
}
@ -135,6 +139,10 @@ public abstract class HasChildFilter extends Filter implements ScopePhase.Collec
}
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
if (collectedUids == null) {
throw new ElasticSearchIllegalStateException("has_child filter/query hasn't executed properly");
}
IdReaderTypeCache idReaderTypeCache = searchContext.idCache().reader(reader).type(parentType);
return new ParentDocSet(reader, collectedUids, idReaderTypeCache);
}

View File

@ -105,6 +105,10 @@ public abstract class HasParentFilter extends Filter implements ScopePhase.Colle
}
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
if (parents == null) {
throw new ElasticSearchIllegalStateException("has_parent filter/query hasn't executed properly");
}
IdReaderTypeCache idReaderTypeCache = context.idCache().reader(reader).type(parentType);
return new ChildrenDocSet(reader, parents, idReaderTypeCache);
}
@ -181,6 +185,10 @@ public abstract class HasParentFilter extends Filter implements ScopePhase.Colle
}
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
if (parentDocs == null) {
throw new ElasticSearchIllegalStateException("has_parent filter/query hasn't executed properly");
}
return new ChildrenDocSet(reader, parentDocs, context, parentType);
}

View File

@ -74,6 +74,9 @@ public class TopChildrenQuery extends Query implements ScopePhase.TopDocsPhase {
private int numHits = 0;
// Need to know if this query is properly used, otherwise the results are unexpected for example in the count api
private boolean properlyInvoked = false;
// Note, the query is expected to already be filtered to only child type docs
public TopChildrenQuery(Query query, String scope, String childType, String parentType, ScoreType scoreType, int factor, int incrementalFactor) {
this.query = query;
@ -97,6 +100,7 @@ public class TopChildrenQuery extends Query implements ScopePhase.TopDocsPhase {
@Override
public void clear() {
properlyInvoked = true;
parentDocs = null;
numHits = 0;
}
@ -202,6 +206,10 @@ public class TopChildrenQuery extends Query implements ScopePhase.TopDocsPhase {
@Override
public Weight createWeight(Searcher searcher) throws IOException {
if (!properlyInvoked) {
throw new ElasticSearchIllegalStateException("top_children query hasn't executed properly");
}
if (parentDocs != null) {
return new ParentWeight(searcher, query.weight(searcher));
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.test.integration.search.child;
import org.elasticsearch.action.count.CountResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.search.ShardSearchFailure;
@ -705,4 +706,43 @@ public class SimpleChildQuerySearchTests extends AbstractNodesTests {
assertThat(searchResponse.hits().getAt(0).id(), equalTo("p2"));
}
@Test
public void testCountApiUsage() throws Exception {
client.admin().indices().prepareDelete().execute().actionGet();
client.admin().indices().prepareCreate("test").setSettings(ImmutableSettings.settingsBuilder().put("index.number_of_shards", 1)).execute().actionGet();
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
client.admin().indices().preparePutMapping("test").setType("child").setSource(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("_parent").field("type", "parent").endObject()
.endObject().endObject()).execute().actionGet();
String parentId = "p1";
client.prepareIndex("test", "parent", parentId)
.setSource("p_field", "p_value1")
.execute().actionGet();
client.prepareIndex("test", "child", parentId + "_c1")
.setSource("c_field1", parentId)
.setParent(parentId)
.execute().actionGet();
client.admin().indices().prepareRefresh().execute().actionGet();
CountResponse countResponse = client.prepareCount("test")
.setQuery(topChildrenQuery("child", termQuery("c_field1", "1")))
.execute().actionGet();
assertThat(countResponse.failedShards(), equalTo(1));
assertThat(countResponse.shardFailures().get(0).reason().contains("top_children query hasn't executed properly"), equalTo(true));
countResponse = client.prepareCount("test")
.setQuery(hasChildQuery("child", termQuery("c_field1", "2")).executionType(getExecutionMethod()))
.execute().actionGet();
assertThat(countResponse.failedShards(), equalTo(1));
assertThat(countResponse.shardFailures().get(0).reason().contains("has_child filter/query hasn't executed properly"), equalTo(true));
countResponse = client.prepareCount("test")
.setQuery(hasParentQuery("parent", termQuery("p_field1", "1")).executionType(getExecutionMethod()))
.execute().actionGet();
assertThat(countResponse.failedShards(), equalTo(1));
assertThat(countResponse.shardFailures().get(0).reason().contains("has_parent filter/query hasn't executed properly"), equalTo(true));
}
}