Adds ignore_unmapped option to nested and P/C queries

The change adds a new option to the `nested`, `has_parent`, `has_children` and `parent_id` queries: `ignore_unmapped`. If this option is set to false, the `toQuery` method on the QueryBuilder will throw an exception if the type/path specified in the query is unmapped. If the option is set to true, the `toQuery` method on the QueryBuilder will return a MatchNoDocsQuery. The default value is `false`so the queries work how they do today (throwing an exception on unmapped paths/types)
This commit is contained in:
Colin Goodheart-Smithe 2016-04-14 09:37:18 +01:00
parent acec464eb8
commit 686aff1545
12 changed files with 287 additions and 29 deletions

View File

@ -63,6 +63,11 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
* The default minimum number of children that are required to match for the parent to be considered a match.
*/
public static final int DEFAULT_MIN_CHILDREN = 0;
/**
* The default value for ignore_unmapped.
*/
public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
/*
* The default score mode that is used to combine score coming from multiple parent documents.
*/
@ -74,6 +79,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
private static final ParseField MIN_CHILDREN_FIELD = new ParseField("min_children");
private static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode");
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
private final QueryBuilder<?> query;
@ -87,6 +93,8 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
private InnerHitBuilder innerHitBuilder;
private boolean ignoreUnmapped = false;
public HasChildQueryBuilder(String type, QueryBuilder<?> query, int maxChildren, int minChildren, ScoreMode scoreMode,
InnerHitBuilder innerHitBuilder) {
@ -123,6 +131,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
scoreMode = ScoreMode.values()[in.readVInt()];
query = in.readQuery();
innerHitBuilder = in.readOptionalWriteable(InnerHitBuilder::new);
ignoreUnmapped = in.readBoolean();
}
@Override
@ -133,6 +142,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
out.writeVInt(scoreMode.ordinal());
out.writeQuery(query);
out.writeOptionalWriteable(innerHitBuilder);
out.writeBoolean(ignoreUnmapped);
}
/**
@ -220,6 +230,25 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
*/
public int maxChildren() { return maxChildren; }
/**
* Sets whether the query builder should ignore unmapped types (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the type is unmapped.
*/
public HasChildQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
this.ignoreUnmapped = ignoreUnmapped;
return this;
}
/**
* Gets whether the query builder will ignore unmapped types (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the type is unmapped.
*/
public boolean ignoreUnmapped() {
return ignoreUnmapped;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
@ -229,6 +258,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
builder.field(SCORE_MODE_FIELD.getPreferredName(), scoreModeAsString(scoreMode));
builder.field(MIN_CHILDREN_FIELD.getPreferredName(), minChildren);
builder.field(MAX_CHILDREN_FIELD.getPreferredName(), maxChildren);
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
printBoostAndQueryName(builder);
if (innerHitBuilder != null) {
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHitBuilder, params);
@ -243,6 +273,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
ScoreMode scoreMode = HasChildQueryBuilder.DEFAULT_SCORE_MODE;
int minChildren = HasChildQueryBuilder.DEFAULT_MIN_CHILDREN;
int maxChildren = HasChildQueryBuilder.DEFAULT_MAX_CHILDREN;
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
String queryName = null;
InnerHitBuilder innerHitBuilder = null;
String currentFieldName = null;
@ -272,6 +303,8 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
minChildren = parser.intValue(true);
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_CHILDREN_FIELD)) {
maxChildren = parser.intValue(true);
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
ignoreUnmapped = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
queryName = parser.text();
} else {
@ -283,6 +316,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
scoreMode, innerHitBuilder);
hasChildQueryBuilder.queryName(queryName);
hasChildQueryBuilder.boost(boost);
hasChildQueryBuilder.ignoreUnmapped(ignoreUnmapped);
return hasChildQueryBuilder;
}
@ -331,7 +365,11 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
}
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
if (childDocMapper == null) {
throw new QueryShardException(context, "[" + NAME + "] no mapping found for type [" + type + "]");
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
throw new QueryShardException(context, "[" + NAME + "] no mapping found for type [" + type + "]");
}
}
ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
if (parentFieldMapper.active() == false) {
@ -344,8 +382,8 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
String parentType = parentFieldMapper.type();
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType);
if (parentDocMapper == null) {
throw new QueryShardException(context, "[" + NAME + "] Type [" + type + "] points to a non existent parent type ["
+ parentType + "]");
throw new QueryShardException(context,
"[" + NAME + "] Type [" + type + "] points to a non existent parent type [" + parentType + "]");
}
if (maxChildren > 0 && maxChildren < minChildren) {
@ -464,12 +502,13 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
&& Objects.equals(scoreMode, that.scoreMode)
&& Objects.equals(minChildren, that.minChildren)
&& Objects.equals(maxChildren, that.maxChildren)
&& Objects.equals(innerHitBuilder, that.innerHitBuilder);
&& Objects.equals(innerHitBuilder, that.innerHitBuilder)
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
}
@Override
protected int doHashCode() {
return Objects.hash(query, type, scoreMode, minChildren, maxChildren, innerHitBuilder);
return Objects.hash(query, type, scoreMode, minChildren, maxChildren, innerHitBuilder, ignoreUnmapped);
}
@Override

View File

@ -20,6 +20,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.common.ParseField;
@ -49,16 +50,23 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
public static final boolean DEFAULT_SCORE = false;
/**
* The default value for ignore_unmapped.
*/
public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
private static final ParseField QUERY_FIELD = new ParseField("query", "filter");
private static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode").withAllDeprecated("score");
private static final ParseField TYPE_FIELD = new ParseField("parent_type", "type");
private static final ParseField SCORE_FIELD = new ParseField("score");
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
private final QueryBuilder<?> query;
private final String type;
private boolean score = DEFAULT_SCORE;
private InnerHitBuilder innerHit;
private boolean ignoreUnmapped = false;
/**
* @param type The parent type
@ -94,6 +102,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
score = in.readBoolean();
query = in.readQuery();
innerHit = in.readOptionalWriteable(InnerHitBuilder::new);
ignoreUnmapped = in.readBoolean();
}
@Override
@ -102,6 +111,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
out.writeBoolean(score);
out.writeQuery(query);
out.writeOptionalWriteable(innerHit);
out.writeBoolean(ignoreUnmapped);
}
/**
@ -150,6 +160,25 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
return innerHit;
}
/**
* Sets whether the query builder should ignore unmapped types (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the type is unmapped.
*/
public HasParentQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
this.ignoreUnmapped = ignoreUnmapped;
return this;
}
/**
* Gets whether the query builder will ignore unmapped types (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the type is unmapped.
*/
public boolean ignoreUnmapped() {
return ignoreUnmapped;
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
Query innerQuery;
@ -166,8 +195,11 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
}
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(type);
if (parentDocMapper == null) {
throw new QueryShardException(context, "[" + NAME + "] query configured 'parent_type' [" + type
+ "] is not a valid type");
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
throw new QueryShardException(context, "[" + NAME + "] query configured 'parent_type' [" + type + "] is not a valid type");
}
}
if (innerHit != null) {
@ -220,6 +252,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
query.toXContent(builder, params);
builder.field(TYPE_FIELD.getPreferredName(), type);
builder.field(SCORE_FIELD.getPreferredName(), score);
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
printBoostAndQueryName(builder);
if (innerHit != null) {
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHit, params);
@ -234,6 +267,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
boolean score = HasParentQueryBuilder.DEFAULT_SCORE;
String queryName = null;
InnerHitBuilder innerHits = null;
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
String currentFieldName = null;
XContentParser.Token token;
@ -264,6 +298,8 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
}
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_FIELD)) {
score = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
ignoreUnmapped = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
boost = parser.floatValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
@ -273,7 +309,8 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
}
}
}
return new HasParentQueryBuilder(parentType, iqb, score, innerHits).queryName(queryName).boost(boost);
return new HasParentQueryBuilder(parentType, iqb, score, innerHits).ignoreUnmapped(ignoreUnmapped).queryName(queryName)
.boost(boost);
}
@Override
@ -286,12 +323,13 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
return Objects.equals(query, that.query)
&& Objects.equals(type, that.type)
&& Objects.equals(score, that.score)
&& Objects.equals(innerHit, that.innerHit);
&& Objects.equals(innerHit, that.innerHit)
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
}
@Override
protected int doHashCode() {
return Objects.hash(query, type, score, innerHit);
return Objects.hash(query, type, score, innerHit, ignoreUnmapped);
}
@Override

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.BitSetProducer;
import org.apache.lucene.search.join.ScoreMode;
@ -45,14 +46,20 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
public static final ParseField QUERY_NAME_FIELD = new ParseField(NAME);
/**
* The default score move for nested queries.
* The default score mode for nested queries.
*/
public static final ScoreMode DEFAULT_SCORE_MODE = ScoreMode.Avg;
/**
* The default value for ignore_unmapped.
*/
public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
private static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode");
private static final ParseField PATH_FIELD = new ParseField("path");
private static final ParseField QUERY_FIELD = new ParseField("query");
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
private final QueryBuilder<?> query;
@ -62,6 +69,8 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
private InnerHitBuilder innerHitBuilder;
private boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
public NestedQueryBuilder(String path, QueryBuilder<?> query) {
if (path == null) {
throw new IllegalArgumentException("[" + NAME + "] requires 'path' field");
@ -92,6 +101,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
scoreMode = ScoreMode.values()[in.readVInt()];
query = in.readQuery();
innerHitBuilder = in.readOptionalWriteable(InnerHitBuilder::new);
ignoreUnmapped = in.readBoolean();
}
@Override
@ -100,6 +110,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
out.writeVInt(scoreMode.ordinal());
out.writeQuery(query);
out.writeOptionalWriteable(innerHitBuilder);
out.writeBoolean(ignoreUnmapped);
}
/**
@ -123,6 +134,25 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
return this;
}
/**
* Sets whether the query builder should ignore unmapped paths (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the path is unmapped.
*/
public NestedQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
this.ignoreUnmapped = ignoreUnmapped;
return this;
}
/**
* Gets whether the query builder will ignore unmapped fields (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the path is unmapped.
*/
public boolean ignoreUnmapped() {
return ignoreUnmapped;
}
/**
* Returns the nested query to execute.
*/
@ -150,6 +180,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
builder.field(QUERY_FIELD.getPreferredName());
query.toXContent(builder, params);
builder.field(PATH_FIELD.getPreferredName(), path);
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
if (scoreMode != null) {
builder.field(SCORE_MODE_FIELD.getPreferredName(), HasChildQueryBuilder.scoreModeAsString(scoreMode));
}
@ -169,6 +200,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
String path = null;
String currentFieldName = null;
InnerHitBuilder innerHitBuilder = null;
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
@ -186,6 +218,8 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
path = parser.text();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
boost = parser.floatValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
ignoreUnmapped = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_MODE_FIELD)) {
scoreMode = HasChildQueryBuilder.parseScoreMode(parser.text());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
@ -195,7 +229,8 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
}
}
}
return new NestedQueryBuilder(path, query, scoreMode, innerHitBuilder).queryName(queryName).boost(boost);
return new NestedQueryBuilder(path, query, scoreMode, innerHitBuilder).ignoreUnmapped(ignoreUnmapped).queryName(queryName)
.boost(boost);
}
@Override
@ -208,19 +243,24 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
return Objects.equals(query, that.query)
&& Objects.equals(path, that.path)
&& Objects.equals(scoreMode, that.scoreMode)
&& Objects.equals(innerHitBuilder, that.innerHitBuilder);
&& Objects.equals(innerHitBuilder, that.innerHitBuilder)
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
}
@Override
protected int doHashCode() {
return Objects.hash(query, path, scoreMode, innerHitBuilder);
return Objects.hash(query, path, scoreMode, innerHitBuilder, ignoreUnmapped);
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
ObjectMapper nestedObjectMapper = context.getObjectMapper(path);
if (nestedObjectMapper == null) {
throw new IllegalStateException("[" + NAME + "] failed to find nested object under path [" + path + "]");
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
throw new IllegalStateException("[" + NAME + "] failed to find nested object under path [" + path + "]");
}
}
if (!nestedObjectMapper.nested().isNested()) {
throw new IllegalStateException("[" + NAME + "] nested object under path [" + path + "] is not of nested type");

View File

@ -23,6 +23,7 @@ import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.DocValuesTermsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.elasticsearch.common.ParseField;
@ -43,12 +44,20 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
public static final String NAME = "parent_id";
public static final ParseField QUERY_NAME_FIELD = new ParseField(NAME);
/**
* The default value for ignore_unmapped.
*/
public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
private static final ParseField ID_FIELD = new ParseField("id");
private static final ParseField TYPE_FIELD = new ParseField("type", "child_type");
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
private final String type;
private final String id;
private boolean ignoreUnmapped = false;
public ParentIdQueryBuilder(String type, String id) {
this.type = type;
this.id = id;
@ -61,12 +70,14 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
super(in);
type = in.readString();
id = in.readString();
ignoreUnmapped = in.readBoolean();
}
@Override
protected void doWriteTo(StreamOutput out) throws IOException {
out.writeString(type);
out.writeString(id);
out.writeBoolean(ignoreUnmapped);
}
public String getType() {
@ -77,11 +88,31 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
return id;
}
/**
* Sets whether the query builder should ignore unmapped types (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the type is unmapped.
*/
public ParentIdQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
this.ignoreUnmapped = ignoreUnmapped;
return this;
}
/**
* Gets whether the query builder will ignore unmapped types (and run a
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
* the type is unmapped.
*/
public boolean ignoreUnmapped() {
return ignoreUnmapped;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
builder.field(TYPE_FIELD.getPreferredName(), type);
builder.field(ID_FIELD.getPreferredName(), id);
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
printBoostAndQueryName(builder);
builder.endObject();
}
@ -93,6 +124,7 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
String id = null;
String queryName = null;
String currentFieldName = null;
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
@ -102,6 +134,8 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
type = parser.text();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ID_FIELD)) {
id = parser.text();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
ignoreUnmapped = parser.booleanValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
boost = parser.floatValue();
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
@ -116,6 +150,7 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
ParentIdQueryBuilder queryBuilder = new ParentIdQueryBuilder(type, id);
queryBuilder.queryName(queryName);
queryBuilder.boost(boost);
queryBuilder.ignoreUnmapped(ignoreUnmapped);
return queryBuilder;
}
@ -124,7 +159,11 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
protected Query doToQuery(QueryShardContext context) throws IOException {
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
if (childDocMapper == null) {
throw new QueryShardException(context, "[" + NAME + "] no mapping found for type [" + type + "]");
if (ignoreUnmapped) {
return new MatchNoDocsQuery();
} else {
throw new QueryShardException(context, "[" + NAME + "] no mapping found for type [" + type + "]");
}
}
ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
if (parentFieldMapper.active() == false) {
@ -141,12 +180,14 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
@Override
protected boolean doEquals(ParentIdQueryBuilder that) {
return Objects.equals(type, that.type) && Objects.equals(id, that.id);
return Objects.equals(type, that.type)
&& Objects.equals(id, that.id)
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
}
@Override
protected int doHashCode() {
return Objects.hash(type, id);
return Objects.hash(type, id, ignoreUnmapped);
}
@Override

View File

@ -26,6 +26,7 @@ import org.apache.lucene.queries.TermsQuery;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.join.ScoreMode;
@ -55,9 +56,10 @@ import org.junit.BeforeClass;
import java.io.IOException;
import java.util.Collections;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.Matchers.is;
public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQueryBuilder> {
@ -124,7 +126,7 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
randomBoolean() ? null : new InnerHitBuilder()
.setName(randomAsciiOfLengthBetween(1, 10))
.setSize(randomIntBetween(0, 100))
.addSort(new FieldSortBuilder(STRING_FIELD_NAME_2).order(SortOrder.ASC)));
.addSort(new FieldSortBuilder(STRING_FIELD_NAME_2).order(SortOrder.ASC))).ignoreUnmapped(randomBoolean());
}
@Override
@ -217,6 +219,7 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
" \"score_mode\" : \"avg\",\n" +
" \"min_children\" : 883170873,\n" +
" \"max_children\" : 1217235442,\n" +
" \"ignore_unmapped\" : false,\n" +
" \"boost\" : 2.0,\n" +
" \"_name\" : \"WNzYMJKRwePuRBh\",\n" +
" \"inner_hits\" : {\n" +
@ -386,4 +389,17 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
assertThat(e.getMessage(), is("No score mode for child query [unrecognized value] found"));
}
}
public void testIgnoreUnmapped() throws IOException {
final HasChildQueryBuilder queryBuilder = new HasChildQueryBuilder("unmapped", new MatchAllQueryBuilder());
queryBuilder.ignoreUnmapped(true);
Query query = queryBuilder.toQuery(queryShardContext());
assertThat(query, notNullValue());
assertThat(query, instanceOf(MatchNoDocsQuery.class));
final HasChildQueryBuilder failingQueryBuilder = new HasChildQueryBuilder("unmapped", new MatchAllQueryBuilder());
failingQueryBuilder.ignoreUnmapped(false);
QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext()));
assertThat(e.getMessage(), containsString("[" + HasChildQueryBuilder.NAME + "] no mapping found for type [unmapped]"));
}
}

View File

@ -21,6 +21,8 @@ package org.elasticsearch.index.query;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import com.fasterxml.jackson.core.JsonParseException;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.ElasticsearchParseException;
@ -45,9 +47,10 @@ import org.junit.BeforeClass;
import java.io.IOException;
import java.util.Arrays;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.CoreMatchers.notNullValue;
public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQueryBuilder> {
protected static final String PARENT_TYPE = "parent";
@ -109,7 +112,7 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
randomBoolean() ? null : new InnerHitBuilder()
.setName(randomAsciiOfLengthBetween(1, 10))
.setSize(randomIntBetween(0, 100))
.addSort(new FieldSortBuilder(STRING_FIELD_NAME_2).order(SortOrder.ASC)));
.addSort(new FieldSortBuilder(STRING_FIELD_NAME_2).order(SortOrder.ASC))).ignoreUnmapped(randomBoolean());
}
@Override
@ -257,6 +260,7 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
" },\n" +
" \"parent_type\" : \"blog\",\n" +
" \"score\" : true,\n" +
" \"ignore_unmapped\" : false,\n" +
" \"boost\" : 1.0\n" +
" }\n" +
"}";
@ -265,4 +269,18 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
assertEquals(json, "blog", parsed.type());
assertEquals(json, "something", ((TermQueryBuilder) parsed.query()).value());
}
public void testIgnoreUnmapped() throws IOException {
final HasParentQueryBuilder queryBuilder = new HasParentQueryBuilder("unmapped", new MatchAllQueryBuilder());
queryBuilder.ignoreUnmapped(true);
Query query = queryBuilder.toQuery(queryShardContext());
assertThat(query, notNullValue());
assertThat(query, instanceOf(MatchNoDocsQuery.class));
final HasParentQueryBuilder failingQueryBuilder = new HasParentQueryBuilder("unmapped", new MatchAllQueryBuilder());
failingQueryBuilder.ignoreUnmapped(false);
QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext()));
assertThat(e.getMessage(),
containsString("[" + HasParentQueryBuilder.NAME + "] query configured 'parent_type' [unmapped] is not a valid type"));
}
}

View File

@ -20,6 +20,8 @@
package org.elasticsearch.index.query;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
@ -38,6 +40,8 @@ import java.io.IOException;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.containsString;
public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBuilder> {
@ -87,7 +91,7 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
SearchContext.current() == null ? null : new InnerHitBuilder()
.setName(randomAsciiOfLengthBetween(1, 10))
.setSize(randomIntBetween(0, 100))
.addSort(new FieldSortBuilder(STRING_FIELD_NAME).order(SortOrder.ASC)));
.addSort(new FieldSortBuilder(STRING_FIELD_NAME).order(SortOrder.ASC))).ignoreUnmapped(randomBoolean());
}
@Override
@ -176,6 +180,7 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
" }\n" +
" },\n" +
" \"path\" : \"obj1\",\n" +
" \"ignore_unmapped\" : false,\n" +
" \"score_mode\" : \"avg\",\n" +
" \"boost\" : 1.0\n" +
" }\n" +
@ -186,4 +191,17 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
assertEquals(json, ScoreMode.Avg, parsed.scoreMode());
}
public void testIgnoreUnmapped() throws IOException {
final NestedQueryBuilder queryBuilder = new NestedQueryBuilder("unmapped", new MatchAllQueryBuilder());
queryBuilder.ignoreUnmapped(true);
Query query = queryBuilder.toQuery(queryShardContext());
assertThat(query, notNullValue());
assertThat(query, instanceOf(MatchNoDocsQuery.class));
final NestedQueryBuilder failingQueryBuilder = new NestedQueryBuilder("unmapped", new MatchAllQueryBuilder());
failingQueryBuilder.ignoreUnmapped(false);
IllegalStateException e = expectThrows(IllegalStateException.class, () -> failingQueryBuilder.toQuery(queryShardContext()));
assertThat(e.getMessage(), containsString("[" + NestedQueryBuilder.NAME + "] failed to find nested object under path [unmapped]"));
}
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.DocValuesTermsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
@ -34,6 +35,10 @@ import org.hamcrest.Matchers;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.notNullValue;
public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQueryBuilder> {
protected static final String PARENT_TYPE = "parent";
@ -84,7 +89,7 @@ public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQue
@Override
protected ParentIdQueryBuilder doCreateTestQueryBuilder() {
return new ParentIdQueryBuilder(CHILD_TYPE, randomAsciiOfLength(4));
return new ParentIdQueryBuilder(CHILD_TYPE, randomAsciiOfLength(4)).ignoreUnmapped(randomBoolean());
}
@Override
@ -108,6 +113,7 @@ public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQue
" \"parent_id\" : {\n" +
" \"type\" : \"child\",\n" +
" \"id\" : \"123\",\n" +
" \"ignore_unmapped\" : false,\n" +
" \"boost\" : 3.0,\n" +
" \"_name\" : \"name\"" +
" }\n" +
@ -120,4 +126,17 @@ public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQue
assertThat(queryBuilder.queryName(), Matchers.equalTo("name"));
}
public void testIgnoreUnmapped() throws IOException {
final ParentIdQueryBuilder queryBuilder = new ParentIdQueryBuilder("unmapped", "foo");
queryBuilder.ignoreUnmapped(true);
Query query = queryBuilder.toQuery(queryShardContext());
assertThat(query, notNullValue());
assertThat(query, instanceOf(MatchNoDocsQuery.class));
final ParentIdQueryBuilder failingQueryBuilder = new ParentIdQueryBuilder("unmapped", "foo");
failingQueryBuilder.ignoreUnmapped(false);
QueryShardException e = expectThrows(QueryShardException.class, () -> failingQueryBuilder.toQuery(queryShardContext()));
assertThat(e.getMessage(), containsString("[" + ParentIdQueryBuilder.NAME + "] no mapping found for type [unmapped]"));
}
}

View File

@ -72,3 +72,12 @@ a match:
The `min_children` and `max_children` parameters can be combined with
the `score_mode` parameter.
[float]
==== Ignore Unmapped
When set to `true` the `ignore_unmapped` option will ignore an unmapped `type`
and will not match any documents for this query. This can be useful when
querying multiple indexes which might have different mappings. When set to
`false` (the default value) the query will throw an exception if the `type`
is not mapped.

View File

@ -46,3 +46,12 @@ matching parent document. The score mode can be specified with the
}
}
--------------------------------------------------
[float]
==== Ignore Unmapped
When set to `true` the `ignore_unmapped` option will ignore an unmapped `type`
and will not match any documents for this query. This can be useful when
querying multiple indexes which might have different mappings. When set to
`false` (the default value) the query will throw an exception if the `type`
is not mapped.

View File

@ -55,6 +55,12 @@ The `score_mode` allows to set how inner children matching affects
scoring of parent. It defaults to `avg`, but can be `sum`, `min`,
`max` and `none`.
There is also an `ignore_unmapped` option which, when set to `true` will
ignore an unmapped `path` and will not match any documents for this query.
This can be useful when querying multiple indexes which might have different
mappings. When set to `false` (the default value) the query will throw an
exception if the `path` is not mapped.
Multi level nesting is automatically supported, and detected, resulting
in an inner nested query to automatically match the relevant nesting
level (and not root) if it exists within another nested query.

View File

@ -8,10 +8,10 @@ The `parent_id` query can be used to find child documents which belong to a part
[source,js]
--------------------------------------------------
{
"parent_id" : {
"type" : "blog_tag",
"id" : "1"
}
"parent_id" : {
"type" : "blog_tag",
"id" : "1"
}
}
--------------------------------------------------
@ -40,4 +40,9 @@ This query has two required parameters:
[horizontal]
`type`:: The **child** type. This must be a type with `_parent` field.
`id`:: The required parent id select documents must referrer to.
`id`:: The required parent id select documents must referrer to.
`ignore_unmapped`:: When set to `true` this will ignore an unmapped `type` and will not match any
documents for this query. This can be useful when querying multiple indexes
which might have different mappings. When set to `false` (the default value)
the query will throw an exception if the `type` is not mapped.