Minor improvements to indices filter and query & updated docs

Slightly simplified indices filter and query parsers code
Trimmed down tests where possible
This commit is contained in:
Luca Cavanna 2013-11-14 15:53:13 +01:00
parent fa80ca97b2
commit 0aaa39d00a
6 changed files with 134 additions and 226 deletions

View File

@ -71,6 +71,8 @@ include::filters/has-parent-filter.asciidoc[]
include::filters/ids-filter.asciidoc[]
include::filters/indices-filter.asciidoc[]
include::filters/limit-filter.asciidoc[]
include::filters/match-all-filter.asciidoc[]

View File

@ -26,13 +26,13 @@ You can use the `index` field to provide a single index.
`no_match_filter` can also have "string" value of `none` (to match no
documents), and `all` (to match all). Defaults to `all`.
`filter` is mandatory. You must provide the indices.
It is forbidden to omit or to give `indices` or `index` multiple times,
or to give both.
`filter` is mandatory, as well as `indices` (or `index`).
Please note that the fields order is important: If the indices are
provided before `filter` or `no_match_filter`, the filter parsing is
skipped altogether.
For instance, this feature is useful to prevent a query that runs
against multiple indices to fail because of a missing type.
See `has_child`, `has_parent`, `top_children` and `nested`.
coming[0.90.8]
[TIP]
===================================================================
The fields order is important: if the `indices` are provided before `filter`
or `no_match_filter`, the related filters get parsed only against the indices
that they are going to be executed on. This is useful to avoid parsing filters
when it is not necessary and prevent potential mapping errors.
===================================================================

View File

@ -26,13 +26,13 @@ You can use the `index` field to provide a single index.
`no_match_query` can also have "string" value of `none` (to match no
documents), and `all` (to match all). Defaults to `all`.
`query` is mandatory. You must provide the indices.
It is forbidden to omit or to give `indices` or `index` multiple times,
or to give both.
`query` is mandatory, as well as `indices` (or `index`).
Please note that the fields order is important: If the indices are
provided before `query` or `no_match_query`, the query parsing is
skipped altogether.
For instance, this feature is useful to prevent a query that runs
against multiple indices to fail because of a missing type.
See `has_child`, `has_parent`, `top_children` and `nested`.
coming[0.90.8]
[TIP]
===================================================================
The fields order is important: if the `indices` are provided before `query`
or `no_match_query`, the related queries get parsed only against the indices
that they are going to be executed on. This is useful to avoid parsing queries
when it is not necessary and prevent potential mapping errors.
===================================================================

View File

@ -22,7 +22,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.Filter;
import org.elasticsearch.action.support.IgnoreIndices;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.Queries;
@ -31,7 +30,6 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
@ -59,10 +57,9 @@ public class IndicesFilterParser implements FilterParser {
Filter filter = null;
Filter noMatchFilter = Queries.MATCH_ALL_FILTER;
Filter chosenFilter = null;
boolean filterFound = false;
boolean indicesFound = false;
boolean matchesConcreteIndices = false;
boolean currentIndexMatchesIndices = false;
String currentFieldName = null;
XContentParser.Token token;
@ -72,29 +69,16 @@ public class IndicesFilterParser implements FilterParser {
} else if (token == XContentParser.Token.START_OBJECT) {
if ("filter".equals(currentFieldName)) {
filterFound = true;
if (indicesFound) {
// Because we know the indices, we can either skip, or parse and use the query
if (matchesConcreteIndices) {
filter = parseContext.parseInnerFilter();
chosenFilter = filter;
} else {
parseContext.parser().skipChildren(); // skip the filter object without parsing it into a Filter
}
//TODO We are able to decide whether to parse the filter or not only if indices in the query appears first
if (indicesFound && !currentIndexMatchesIndices) {
parseContext.parser().skipChildren(); // skip the filter object without parsing it
} else {
// We do not know the indices, we must parse the query
filter = parseContext.parseInnerFilter();
}
} else if ("no_match_filter".equals(currentFieldName)) {
if (indicesFound) {
// Because we know the indices, we can either skip, or parse and use the query
if (!matchesConcreteIndices) {
noMatchFilter = parseContext.parseInnerFilter();
chosenFilter = noMatchFilter;
} else {
parseContext.parser().skipChildren(); // skip the filter object without parsing it into a Filter
}
if (indicesFound && currentIndexMatchesIndices) {
parseContext.parser().skipChildren(); // skip the filter object without parsing it
} else {
// We do not know the indices, we must parse the query
noMatchFilter = parseContext.parseInnerFilter();
}
} else {
@ -103,28 +87,28 @@ public class IndicesFilterParser implements FilterParser {
} else if (token == XContentParser.Token.START_ARRAY) {
if ("indices".equals(currentFieldName)) {
if (indicesFound) {
throw new QueryParsingException(parseContext.index(), "[indices] indices already specified");
throw new QueryParsingException(parseContext.index(), "[indices] indices or index already specified");
}
indicesFound = true;
Collection<String> indices = new ArrayList<String>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
String value = parser.textOrNull();
if (value == null) {
throw new QueryParsingException(parseContext.index(), "No value specified for term filter");
throw new QueryParsingException(parseContext.index(), "[indices] no value specified for 'indices' entry");
}
indices.add(value);
}
matchesConcreteIndices = matchesIndices(parseContext, getConcreteIndices(indices));
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), indices.toArray(new String[indices.size()]));
} else {
throw new QueryParsingException(parseContext.index(), "[indices] filter does not support [" + currentFieldName + "]");
}
} else if (token.isValue()) {
if ("index".equals(currentFieldName)) {
if (indicesFound) {
throw new QueryParsingException(parseContext.index(), "[indices] indices already specified");
throw new QueryParsingException(parseContext.index(), "[indices] indices or index already specified");
}
indicesFound = true;
matchesConcreteIndices = matchesIndices(parseContext, getConcreteIndices(Arrays.asList(parser.text())));
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), parser.text());
} else if ("no_match_filter".equals(currentFieldName)) {
String type = parser.text();
if ("all".equals(type)) {
@ -132,11 +116,6 @@ public class IndicesFilterParser implements FilterParser {
} else if ("none".equals(type)) {
noMatchFilter = Queries.MATCH_NO_FILTER;
}
if (indicesFound) {
if (!matchesConcreteIndices) {
chosenFilter = noMatchFilter;
}
}
} else {
throw new QueryParsingException(parseContext.index(), "[indices] filter does not support [" + currentFieldName + "]");
}
@ -146,34 +125,19 @@ public class IndicesFilterParser implements FilterParser {
throw new QueryParsingException(parseContext.index(), "[indices] requires 'filter' element");
}
if (!indicesFound) {
throw new QueryParsingException(parseContext.index(), "[indices] requires 'indices' element");
throw new QueryParsingException(parseContext.index(), "[indices] requires 'indices' or 'index' element");
}
if (chosenFilter == null) {
// Indices were not provided before we encountered the queries, which we hence parsed
// We must now make a choice
if (matchesConcreteIndices) {
chosenFilter = filter;
} else {
chosenFilter = noMatchFilter;
}
if (currentIndexMatchesIndices) {
return filter;
}
return chosenFilter;
return noMatchFilter;
}
protected String[] getConcreteIndices(Collection<String> indices) {
String[] concreteIndices = indices.toArray(new String[indices.size()]);
if (clusterService != null) {
MetaData metaData = clusterService.state().metaData();
concreteIndices = metaData.concreteIndices(indices.toArray(new String[indices.size()]), IgnoreIndices.MISSING, true);
}
return concreteIndices;
}
protected boolean matchesIndices(QueryParseContext parseContext, String[] concreteIndices) {
protected boolean matchesIndices(String currentIndex, String... indices) {
String[] concreteIndices = clusterService.state().metaData().concreteIndices(indices, IgnoreIndices.MISSING, true);
for (String index : concreteIndices) {
if (Regex.simpleMatch(index, parseContext.index().name())) {
if (Regex.simpleMatch(index, currentIndex)) {
return true;
}
}

View File

@ -22,7 +22,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.action.support.IgnoreIndices;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.Queries;
@ -31,7 +30,6 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
@ -59,10 +57,9 @@ public class IndicesQueryParser implements QueryParser {
Query query = null;
Query noMatchQuery = Queries.newMatchAllQuery();
Query chosenQuery = null;
boolean queryFound = false;
boolean indicesFound = false;
boolean matchesConcreteIndices = false;
boolean currentIndexMatchesIndices = false;
String queryName = null;
String currentFieldName = null;
@ -72,30 +69,17 @@ public class IndicesQueryParser implements QueryParser {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
if ("query".equals(currentFieldName)) {
//TODO We are able to decide whether to parse the query or not only if indices in the query appears first
queryFound = true;
if (indicesFound) {
// Because we know the indices, we can either skip, or parse and use the query
if (matchesConcreteIndices) {
query = parseContext.parseInnerQuery();
chosenQuery = query;
} else {
parseContext.parser().skipChildren(); // skip the query object without parsing it into a Query
}
if (indicesFound && !currentIndexMatchesIndices) {
parseContext.parser().skipChildren(); // skip the query object without parsing it
} else {
// We do not know the indices, we must parse the query
query = parseContext.parseInnerQuery();
}
} else if ("no_match_query".equals(currentFieldName)) {
if (indicesFound) {
// Because we know the indices, we can either skip, or parse and use the query
if (!matchesConcreteIndices) {
noMatchQuery = parseContext.parseInnerQuery();
chosenQuery = noMatchQuery;
} else {
parseContext.parser().skipChildren(); // skip the query object without parsing it into a Query
}
if (indicesFound && currentIndexMatchesIndices) {
parseContext.parser().skipChildren(); // skip the query object without parsing it
} else {
// We do not know the indices, we must parse the query
noMatchQuery = parseContext.parseInnerQuery();
}
} else {
@ -104,28 +88,28 @@ public class IndicesQueryParser implements QueryParser {
} else if (token == XContentParser.Token.START_ARRAY) {
if ("indices".equals(currentFieldName)) {
if (indicesFound) {
throw new QueryParsingException(parseContext.index(), "[indices] indices already specified");
throw new QueryParsingException(parseContext.index(), "[indices] indices or index already specified");
}
indicesFound = true;
Collection<String> indices = new ArrayList<String>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
String value = parser.textOrNull();
if (value == null) {
throw new QueryParsingException(parseContext.index(), "No value specified for term filter");
throw new QueryParsingException(parseContext.index(), "[indices] no value specified for 'indices' entry");
}
indices.add(value);
}
matchesConcreteIndices = matchesIndices(parseContext, getConcreteIndices(indices));
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), indices.toArray(new String[indices.size()]));
} else {
throw new QueryParsingException(parseContext.index(), "[indices] query does not support [" + currentFieldName + "]");
}
} else if (token.isValue()) {
if ("index".equals(currentFieldName)) {
if (indicesFound) {
throw new QueryParsingException(parseContext.index(), "[indices] indices already specified");
throw new QueryParsingException(parseContext.index(), "[indices] indices or index already specified");
}
indicesFound = true;
matchesConcreteIndices = matchesIndices(parseContext, getConcreteIndices(Arrays.asList(parser.text())));
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), parser.text());
} else if ("no_match_query".equals(currentFieldName)) {
String type = parser.text();
if ("all".equals(type)) {
@ -133,11 +117,6 @@ public class IndicesQueryParser implements QueryParser {
} else if ("none".equals(type)) {
noMatchQuery = Queries.newMatchNoDocsQuery();
}
if (indicesFound) {
if (!matchesConcreteIndices) {
chosenQuery = noMatchQuery;
}
}
} else if ("_name".equals(currentFieldName)) {
queryName = parser.text();
} else {
@ -149,38 +128,25 @@ public class IndicesQueryParser implements QueryParser {
throw new QueryParsingException(parseContext.index(), "[indices] requires 'query' element");
}
if (!indicesFound) {
throw new QueryParsingException(parseContext.index(), "[indices] requires 'indices' element");
throw new QueryParsingException(parseContext.index(), "[indices] requires 'indices' or 'index' element");
}
if (chosenQuery == null) {
// Indices were not provided before we encountered the queries, which we hence parsed
// We must now make a choice
if (matchesConcreteIndices) {
chosenQuery = query;
} else {
chosenQuery = noMatchQuery;
}
Query chosenQuery;
if (currentIndexMatchesIndices) {
chosenQuery = query;
} else {
chosenQuery = noMatchQuery;
}
if (queryName != null && chosenQuery != null) {
if (queryName != null) {
parseContext.addNamedQuery(queryName, chosenQuery);
}
return chosenQuery;
}
protected String[] getConcreteIndices(Collection<String> indices) {
String[] concreteIndices = indices.toArray(new String[indices.size()]);
if (clusterService != null) {
MetaData metaData = clusterService.state().metaData();
concreteIndices = metaData.concreteIndices(indices.toArray(new String[indices.size()]), IgnoreIndices.MISSING, true);
}
return concreteIndices;
}
protected boolean matchesIndices(QueryParseContext parseContext, String[] concreteIndices) {
protected boolean matchesIndices(String currentIndex, String... indices) {
String[] concreteIndices = clusterService.state().metaData().concreteIndices(indices, IgnoreIndices.MISSING, true);
for (String index : concreteIndices) {
if (Regex.simpleMatch(index, parseContext.index().name())) {
if (Regex.simpleMatch(index, currentIndex)) {
return true;
}
}

View File

@ -26,6 +26,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
@ -1893,35 +1894,39 @@ public class SimpleQueryTests extends ElasticsearchIntegrationTest {
@Test
public void testIndicesQuery() throws Exception {
createIndex("index1", "index2");
createIndex("index1", "index2", "index3");
ensureGreen();
client().prepareIndex("index1", "type1").setId("1").setSource("text", "value").get();
client().prepareIndex("index2", "type2").setId("2").setSource("text", "value").get();
client().prepareIndex("index1", "type1").setId("1").setSource("text", "value1").get();
client().prepareIndex("index2", "type2").setId("2").setSource("text", "value2").get();
client().prepareIndex("index3", "type3").setId("3").setSource("text", "value3").get();
refresh();
SearchResponse response = client().prepareSearch("index1", "index2")
.setQuery(indicesQuery(matchQuery("text", "value"), "index1")
.noMatchQuery(matchQuery("text", "value"))).get();
SearchResponse response = client().prepareSearch("index1", "index2", "index3")
.setQuery(indicesQuery(matchQuery("text", "value1"), "index1")
.noMatchQuery(matchQuery("text", "value2"))).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
response = client().prepareSearch("index1", "index2")
.setQuery(indicesQuery(matchQuery("text", "value"), "index1")).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
//default no match query is match_all
response = client().prepareSearch("index1", "index2", "index3")
.setQuery(indicesQuery(matchQuery("text", "value1"), "index1")).get();
assertHitCount(response, 3l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(2).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
response = client().prepareSearch("index1", "index2")
.setQuery(indicesQuery(matchQuery("text", "value"), "index1")
response = client().prepareSearch("index1", "index2", "index3")
.setQuery(indicesQuery(matchQuery("text", "value1"), "index1")
.noMatchQuery("all")).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
assertHitCount(response, 3l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(2).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
response = client().prepareSearch("index1", "index2")
.setQuery(indicesQuery(matchQuery("text", "value"), "index1")
response = client().prepareSearch("index1", "index2", "index3")
.setQuery(indicesQuery(matchQuery("text", "value1"), "index1")
.noMatchQuery("none")).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
@ -1929,137 +1934,108 @@ public class SimpleQueryTests extends ElasticsearchIntegrationTest {
@Test
public void testIndicesFilter() throws Exception {
createIndex("index1", "index2");
createIndex("index1", "index2", "index3");
ensureGreen();
client().prepareIndex("index1", "type1").setId("1").setSource("text", "value").get();
client().prepareIndex("index2", "type2").setId("2").setSource("text", "value").get();
client().prepareIndex("index1", "type1").setId("1").setSource("text", "value1").get();
client().prepareIndex("index2", "type2").setId("2").setSource("text", "value2").get();
client().prepareIndex("index3", "type3").setId("3").setSource("text", "value3").get();
refresh();
SearchResponse response = client().prepareSearch("index1", "index2")
.setFilter(indicesFilter(termFilter("text", "value"), "index1")
.noMatchFilter(termFilter("text", "value"))).get();
SearchResponse response = client().prepareSearch("index1", "index2", "index3")
.setFilter(indicesFilter(termFilter("text", "value1"), "index1")
.noMatchFilter(termFilter("text", "value2"))).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
response = client().prepareSearch("index1", "index2")
.setFilter(indicesFilter(termFilter("text", "value"), "index1")).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
//default no match filter is "all"
response = client().prepareSearch("index1", "index2", "index3")
.setFilter(indicesFilter(termFilter("text", "value1"), "index1")).get();
assertHitCount(response, 3l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(2).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
response = client().prepareSearch("index1", "index2")
.setFilter(indicesFilter(termFilter("text", "value"), "index1")
response = client().prepareSearch("index1", "index2", "index3")
.setFilter(indicesFilter(termFilter("text", "value1"), "index1")
.noMatchFilter("all")).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
assertHitCount(response, 3l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
assertThat(response.getHits().getAt(2).getId(), either(equalTo("1")).or(equalTo("2")).or(equalTo("3")));
response = client().prepareSearch("index1", "index2")
.setFilter(indicesFilter(termFilter("text", "value"), "index1")
response = client().prepareSearch("index1", "index2", "index3")
.setFilter(indicesFilter(termFilter("text", "value1"), "index1")
.noMatchFilter("none")).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
}
@Test // https://github.com/elasticsearch/elasticsearch/issues/2416
public void testIndicesQueryHideParsingExceptions() throws Exception {
client().admin().indices().prepareCreate("simple")
.addMapping("lone", jsonBuilder().startObject().startObject("lone").endObject().endObject())
.get();
public void testIndicesQuerySkipParsing() throws Exception {
createIndex("simple");
client().admin().indices().prepareCreate("related")
.addMapping("parent", jsonBuilder().startObject().startObject("parent").endObject().endObject())
.addMapping("child", jsonBuilder().startObject().startObject("child").startObject("_parent").field("type", "parent")
.endObject().endObject().endObject())
.get();
ensureGreen();
client().prepareIndex("simple", "lone").setId("1").setSource("text", "value").get();
client().prepareIndex("simple", "lone").setId("1").setSource("text", "value1").get();
client().prepareIndex("related", "parent").setId("2").setSource("text", "parent").get();
client().prepareIndex("related", "child").setId("3").setParent("2").setSource("text", "value").get();
client().prepareIndex("related", "child").setId("3").setParent("2").setSource("text", "value2").get();
refresh();
SearchResponse response = client().prepareSearch("related")
.setQuery(hasChildQuery("child", matchQuery("text", "value"))).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).getId(), equalTo("2"));
response = client().prepareSearch("simple")
.setQuery(matchQuery("text", "value")).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
//has_child fails if executed on "simple" index
try {
client().prepareSearch("simple")
.setQuery(hasChildQuery("child", matchQuery("text", "value"))).get();
fail("Should have failed with a SearchPhaseExecutionException because all shards failed with a nested QueryParsingException");
// If no failure happens, the HasChildQuery may have changed behavior when provided with wrong types
fail("Should have failed as has_child query can only be executed against parent-child types");
} catch (SearchPhaseExecutionException e) {
// There is no easy way to ensure we got a QueryParsingException
assertThat(e.shardFailures().length, greaterThan(0));
for (ShardSearchFailure shardSearchFailure : e.shardFailures()) {
assertThat(shardSearchFailure.reason(), containsString("No mapping for for type [child]"));
}
}
response = client().prepareSearch("related", "simple")
.setQuery(indicesQuery(matchQuery("text", "parent"), "related")
.noMatchQuery(matchQuery("text", "value"))).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
response = client().prepareSearch("related", "simple")
.setQuery(indicesQuery(hasChildQuery("child", matchQuery("text", "value")), "related")
.noMatchQuery(matchQuery("text", "value"))).get();
//has_child doesn't get parsed for "simple" index
SearchResponse response = client().prepareSearch("related", "simple")
.setQuery(indicesQuery(hasChildQuery("child", matchQuery("text", "value2")), "related")
.noMatchQuery(matchQuery("text", "value1"))).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
}
@Test // https://github.com/elasticsearch/elasticsearch/issues/2416
public void testIndicesFilterHideParsingExceptions() throws Exception {
client().admin().indices().prepareCreate("simple")
.addMapping("lone", jsonBuilder().startObject().startObject("lone").endObject().endObject())
.get();
public void testIndicesFilterSkipParsing() throws Exception {
createIndex("simple");
client().admin().indices().prepareCreate("related")
.addMapping("parent", jsonBuilder().startObject().startObject("parent").endObject().endObject())
.addMapping("child", jsonBuilder().startObject().startObject("child").startObject("_parent").field("type", "parent")
.endObject().endObject().endObject())
.get();
ensureGreen();
client().prepareIndex("simple", "lone").setId("1").setSource("text", "value").get();
client().prepareIndex("simple", "lone").setId("1").setSource("text", "value1").get();
client().prepareIndex("related", "parent").setId("2").setSource("text", "parent").get();
client().prepareIndex("related", "child").setId("3").setParent("2").setSource("text", "value").get();
client().prepareIndex("related", "child").setId("3").setParent("2").setSource("text", "value2").get();
refresh();
SearchResponse response = client().prepareSearch("related")
.setFilter(hasChildFilter("child", termFilter("text", "value"))).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).getId(), equalTo("2"));
response = client().prepareSearch("simple")
.setFilter(termFilter("text", "value")).get();
assertHitCount(response, 1l);
assertThat(response.getHits().getAt(0).getId(), equalTo("1"));
//has_child fails if executed on "simple" index
try {
client().prepareSearch("simple")
.setFilter(hasChildFilter("child", termFilter("text", "value"))).get();
fail("Should have failed with a SearchPhaseExecutionException because all shards failed with a nested QueryParsingException");
// If no failure happens, the HasChildQuery may have changed behavior when provided with wrong types
.setFilter(hasChildFilter("child", termFilter("text", "value1"))).get();
fail("Should have failed as has_child query can only be executed against parent-child types");
} catch (SearchPhaseExecutionException e) {
// There is no easy way to ensure we got a QueryParsingException
assertThat(e.shardFailures().length, greaterThan(0));
for (ShardSearchFailure shardSearchFailure : e.shardFailures()) {
assertThat(shardSearchFailure.reason(), containsString("No mapping for for type [child]"));
}
}
response = client().prepareSearch("related", "simple")
.setFilter(indicesFilter(termFilter("text", "parent"), "related")
.noMatchFilter(termFilter("text", "value"))).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));
response = client().prepareSearch("related", "simple")
.setFilter(indicesFilter(hasChildFilter("child", termFilter("text", "value")), "related")
.noMatchFilter(termFilter("text", "value"))).get();
SearchResponse response = client().prepareSearch("related", "simple")
.setFilter(indicesFilter(hasChildFilter("child", termFilter("text", "value2")), "related")
.noMatchFilter(termFilter("text", "value1"))).get();
assertHitCount(response, 2l);
assertThat(response.getHits().getAt(0).getId(), either(equalTo("1")).or(equalTo("2")));
assertThat(response.getHits().getAt(1).getId(), either(equalTo("1")).or(equalTo("2")));