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:
parent
acec464eb8
commit
686aff1545
|
@ -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.
|
* 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;
|
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.
|
* 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 MIN_CHILDREN_FIELD = new ParseField("min_children");
|
||||||
private static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode");
|
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 INNER_HITS_FIELD = new ParseField("inner_hits");
|
||||||
|
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
|
||||||
|
|
||||||
private final QueryBuilder<?> query;
|
private final QueryBuilder<?> query;
|
||||||
|
|
||||||
|
@ -87,6 +93,8 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
|
|
||||||
private InnerHitBuilder innerHitBuilder;
|
private InnerHitBuilder innerHitBuilder;
|
||||||
|
|
||||||
|
private boolean ignoreUnmapped = false;
|
||||||
|
|
||||||
|
|
||||||
public HasChildQueryBuilder(String type, QueryBuilder<?> query, int maxChildren, int minChildren, ScoreMode scoreMode,
|
public HasChildQueryBuilder(String type, QueryBuilder<?> query, int maxChildren, int minChildren, ScoreMode scoreMode,
|
||||||
InnerHitBuilder innerHitBuilder) {
|
InnerHitBuilder innerHitBuilder) {
|
||||||
|
@ -123,6 +131,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
scoreMode = ScoreMode.values()[in.readVInt()];
|
scoreMode = ScoreMode.values()[in.readVInt()];
|
||||||
query = in.readQuery();
|
query = in.readQuery();
|
||||||
innerHitBuilder = in.readOptionalWriteable(InnerHitBuilder::new);
|
innerHitBuilder = in.readOptionalWriteable(InnerHitBuilder::new);
|
||||||
|
ignoreUnmapped = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -133,6 +142,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
out.writeVInt(scoreMode.ordinal());
|
out.writeVInt(scoreMode.ordinal());
|
||||||
out.writeQuery(query);
|
out.writeQuery(query);
|
||||||
out.writeOptionalWriteable(innerHitBuilder);
|
out.writeOptionalWriteable(innerHitBuilder);
|
||||||
|
out.writeBoolean(ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -220,6 +230,25 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
*/
|
*/
|
||||||
public int maxChildren() { return maxChildren; }
|
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
|
@Override
|
||||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject(NAME);
|
builder.startObject(NAME);
|
||||||
|
@ -229,6 +258,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
builder.field(SCORE_MODE_FIELD.getPreferredName(), scoreModeAsString(scoreMode));
|
builder.field(SCORE_MODE_FIELD.getPreferredName(), scoreModeAsString(scoreMode));
|
||||||
builder.field(MIN_CHILDREN_FIELD.getPreferredName(), minChildren);
|
builder.field(MIN_CHILDREN_FIELD.getPreferredName(), minChildren);
|
||||||
builder.field(MAX_CHILDREN_FIELD.getPreferredName(), maxChildren);
|
builder.field(MAX_CHILDREN_FIELD.getPreferredName(), maxChildren);
|
||||||
|
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
|
||||||
printBoostAndQueryName(builder);
|
printBoostAndQueryName(builder);
|
||||||
if (innerHitBuilder != null) {
|
if (innerHitBuilder != null) {
|
||||||
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHitBuilder, params);
|
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHitBuilder, params);
|
||||||
|
@ -243,6 +273,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
ScoreMode scoreMode = HasChildQueryBuilder.DEFAULT_SCORE_MODE;
|
ScoreMode scoreMode = HasChildQueryBuilder.DEFAULT_SCORE_MODE;
|
||||||
int minChildren = HasChildQueryBuilder.DEFAULT_MIN_CHILDREN;
|
int minChildren = HasChildQueryBuilder.DEFAULT_MIN_CHILDREN;
|
||||||
int maxChildren = HasChildQueryBuilder.DEFAULT_MAX_CHILDREN;
|
int maxChildren = HasChildQueryBuilder.DEFAULT_MAX_CHILDREN;
|
||||||
|
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
|
||||||
String queryName = null;
|
String queryName = null;
|
||||||
InnerHitBuilder innerHitBuilder = null;
|
InnerHitBuilder innerHitBuilder = null;
|
||||||
String currentFieldName = null;
|
String currentFieldName = null;
|
||||||
|
@ -272,6 +303,8 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
minChildren = parser.intValue(true);
|
minChildren = parser.intValue(true);
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_CHILDREN_FIELD)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_CHILDREN_FIELD)) {
|
||||||
maxChildren = parser.intValue(true);
|
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)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
||||||
queryName = parser.text();
|
queryName = parser.text();
|
||||||
} else {
|
} else {
|
||||||
|
@ -283,6 +316,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
scoreMode, innerHitBuilder);
|
scoreMode, innerHitBuilder);
|
||||||
hasChildQueryBuilder.queryName(queryName);
|
hasChildQueryBuilder.queryName(queryName);
|
||||||
hasChildQueryBuilder.boost(boost);
|
hasChildQueryBuilder.boost(boost);
|
||||||
|
hasChildQueryBuilder.ignoreUnmapped(ignoreUnmapped);
|
||||||
return hasChildQueryBuilder;
|
return hasChildQueryBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +365,11 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
}
|
}
|
||||||
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
|
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
|
||||||
if (childDocMapper == null) {
|
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();
|
ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
|
||||||
if (parentFieldMapper.active() == false) {
|
if (parentFieldMapper.active() == false) {
|
||||||
|
@ -344,8 +382,8 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
String parentType = parentFieldMapper.type();
|
String parentType = parentFieldMapper.type();
|
||||||
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType);
|
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType);
|
||||||
if (parentDocMapper == null) {
|
if (parentDocMapper == null) {
|
||||||
throw new QueryShardException(context, "[" + NAME + "] Type [" + type + "] points to a non existent parent type ["
|
throw new QueryShardException(context,
|
||||||
+ parentType + "]");
|
"[" + NAME + "] Type [" + type + "] points to a non existent parent type [" + parentType + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxChildren > 0 && maxChildren < minChildren) {
|
if (maxChildren > 0 && maxChildren < minChildren) {
|
||||||
|
@ -464,12 +502,13 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
|
||||||
&& Objects.equals(scoreMode, that.scoreMode)
|
&& Objects.equals(scoreMode, that.scoreMode)
|
||||||
&& Objects.equals(minChildren, that.minChildren)
|
&& Objects.equals(minChildren, that.minChildren)
|
||||||
&& Objects.equals(maxChildren, that.maxChildren)
|
&& Objects.equals(maxChildren, that.maxChildren)
|
||||||
&& Objects.equals(innerHitBuilder, that.innerHitBuilder);
|
&& Objects.equals(innerHitBuilder, that.innerHitBuilder)
|
||||||
|
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int doHashCode() {
|
protected int doHashCode() {
|
||||||
return Objects.hash(query, type, scoreMode, minChildren, maxChildren, innerHitBuilder);
|
return Objects.hash(query, type, scoreMode, minChildren, maxChildren, innerHitBuilder, ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import org.apache.lucene.search.BooleanClause;
|
import org.apache.lucene.search.BooleanClause;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
|
@ -49,16 +50,23 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
|
|
||||||
public static final boolean DEFAULT_SCORE = false;
|
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 QUERY_FIELD = new ParseField("query", "filter");
|
||||||
private static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode").withAllDeprecated("score");
|
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 TYPE_FIELD = new ParseField("parent_type", "type");
|
||||||
private static final ParseField SCORE_FIELD = new ParseField("score");
|
private static final ParseField SCORE_FIELD = new ParseField("score");
|
||||||
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
|
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 QueryBuilder<?> query;
|
||||||
private final String type;
|
private final String type;
|
||||||
private boolean score = DEFAULT_SCORE;
|
private boolean score = DEFAULT_SCORE;
|
||||||
private InnerHitBuilder innerHit;
|
private InnerHitBuilder innerHit;
|
||||||
|
private boolean ignoreUnmapped = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param type The parent type
|
* @param type The parent type
|
||||||
|
@ -94,6 +102,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
score = in.readBoolean();
|
score = in.readBoolean();
|
||||||
query = in.readQuery();
|
query = in.readQuery();
|
||||||
innerHit = in.readOptionalWriteable(InnerHitBuilder::new);
|
innerHit = in.readOptionalWriteable(InnerHitBuilder::new);
|
||||||
|
ignoreUnmapped = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -102,6 +111,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
out.writeBoolean(score);
|
out.writeBoolean(score);
|
||||||
out.writeQuery(query);
|
out.writeQuery(query);
|
||||||
out.writeOptionalWriteable(innerHit);
|
out.writeOptionalWriteable(innerHit);
|
||||||
|
out.writeBoolean(ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,6 +160,25 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
return innerHit;
|
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
|
@Override
|
||||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||||
Query innerQuery;
|
Query innerQuery;
|
||||||
|
@ -166,8 +195,11 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
}
|
}
|
||||||
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(type);
|
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(type);
|
||||||
if (parentDocMapper == null) {
|
if (parentDocMapper == null) {
|
||||||
throw new QueryShardException(context, "[" + NAME + "] query configured 'parent_type' [" + type
|
if (ignoreUnmapped) {
|
||||||
+ "] is not a valid type");
|
return new MatchNoDocsQuery();
|
||||||
|
} else {
|
||||||
|
throw new QueryShardException(context, "[" + NAME + "] query configured 'parent_type' [" + type + "] is not a valid type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (innerHit != null) {
|
if (innerHit != null) {
|
||||||
|
@ -220,6 +252,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
query.toXContent(builder, params);
|
query.toXContent(builder, params);
|
||||||
builder.field(TYPE_FIELD.getPreferredName(), type);
|
builder.field(TYPE_FIELD.getPreferredName(), type);
|
||||||
builder.field(SCORE_FIELD.getPreferredName(), score);
|
builder.field(SCORE_FIELD.getPreferredName(), score);
|
||||||
|
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
|
||||||
printBoostAndQueryName(builder);
|
printBoostAndQueryName(builder);
|
||||||
if (innerHit != null) {
|
if (innerHit != null) {
|
||||||
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHit, params);
|
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHit, params);
|
||||||
|
@ -234,6 +267,7 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
boolean score = HasParentQueryBuilder.DEFAULT_SCORE;
|
boolean score = HasParentQueryBuilder.DEFAULT_SCORE;
|
||||||
String queryName = null;
|
String queryName = null;
|
||||||
InnerHitBuilder innerHits = null;
|
InnerHitBuilder innerHits = null;
|
||||||
|
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
|
||||||
|
|
||||||
String currentFieldName = null;
|
String currentFieldName = null;
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
|
@ -264,6 +298,8 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
}
|
}
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_FIELD)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_FIELD)) {
|
||||||
score = parser.booleanValue();
|
score = parser.booleanValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
|
||||||
|
ignoreUnmapped = parser.booleanValue();
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
||||||
boost = parser.floatValue();
|
boost = parser.floatValue();
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
} 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
|
@Override
|
||||||
|
@ -286,12 +323,13 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
|
||||||
return Objects.equals(query, that.query)
|
return Objects.equals(query, that.query)
|
||||||
&& Objects.equals(type, that.type)
|
&& Objects.equals(type, that.type)
|
||||||
&& Objects.equals(score, that.score)
|
&& Objects.equals(score, that.score)
|
||||||
&& Objects.equals(innerHit, that.innerHit);
|
&& Objects.equals(innerHit, that.innerHit)
|
||||||
|
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int doHashCode() {
|
protected int doHashCode() {
|
||||||
return Objects.hash(query, type, score, innerHit);
|
return Objects.hash(query, type, score, innerHit, ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.index.query;
|
package org.elasticsearch.index.query;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.join.BitSetProducer;
|
import org.apache.lucene.search.join.BitSetProducer;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
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);
|
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;
|
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 SCORE_MODE_FIELD = new ParseField("score_mode");
|
||||||
private static final ParseField PATH_FIELD = new ParseField("path");
|
private static final ParseField PATH_FIELD = new ParseField("path");
|
||||||
private static final ParseField QUERY_FIELD = new ParseField("query");
|
private static final ParseField QUERY_FIELD = new ParseField("query");
|
||||||
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
|
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 QueryBuilder<?> query;
|
||||||
|
|
||||||
|
@ -62,6 +69,8 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
|
|
||||||
private InnerHitBuilder innerHitBuilder;
|
private InnerHitBuilder innerHitBuilder;
|
||||||
|
|
||||||
|
private boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
|
||||||
|
|
||||||
public NestedQueryBuilder(String path, QueryBuilder<?> query) {
|
public NestedQueryBuilder(String path, QueryBuilder<?> query) {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
throw new IllegalArgumentException("[" + NAME + "] requires 'path' field");
|
throw new IllegalArgumentException("[" + NAME + "] requires 'path' field");
|
||||||
|
@ -92,6 +101,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
scoreMode = ScoreMode.values()[in.readVInt()];
|
scoreMode = ScoreMode.values()[in.readVInt()];
|
||||||
query = in.readQuery();
|
query = in.readQuery();
|
||||||
innerHitBuilder = in.readOptionalWriteable(InnerHitBuilder::new);
|
innerHitBuilder = in.readOptionalWriteable(InnerHitBuilder::new);
|
||||||
|
ignoreUnmapped = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -100,6 +110,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
out.writeVInt(scoreMode.ordinal());
|
out.writeVInt(scoreMode.ordinal());
|
||||||
out.writeQuery(query);
|
out.writeQuery(query);
|
||||||
out.writeOptionalWriteable(innerHitBuilder);
|
out.writeOptionalWriteable(innerHitBuilder);
|
||||||
|
out.writeBoolean(ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,6 +134,25 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
return this;
|
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.
|
* Returns the nested query to execute.
|
||||||
*/
|
*/
|
||||||
|
@ -150,6 +180,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
builder.field(QUERY_FIELD.getPreferredName());
|
builder.field(QUERY_FIELD.getPreferredName());
|
||||||
query.toXContent(builder, params);
|
query.toXContent(builder, params);
|
||||||
builder.field(PATH_FIELD.getPreferredName(), path);
|
builder.field(PATH_FIELD.getPreferredName(), path);
|
||||||
|
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
|
||||||
if (scoreMode != null) {
|
if (scoreMode != null) {
|
||||||
builder.field(SCORE_MODE_FIELD.getPreferredName(), HasChildQueryBuilder.scoreModeAsString(scoreMode));
|
builder.field(SCORE_MODE_FIELD.getPreferredName(), HasChildQueryBuilder.scoreModeAsString(scoreMode));
|
||||||
}
|
}
|
||||||
|
@ -169,6 +200,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
String path = null;
|
String path = null;
|
||||||
String currentFieldName = null;
|
String currentFieldName = null;
|
||||||
InnerHitBuilder innerHitBuilder = null;
|
InnerHitBuilder innerHitBuilder = null;
|
||||||
|
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
@ -186,6 +218,8 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
path = parser.text();
|
path = parser.text();
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
||||||
boost = parser.floatValue();
|
boost = parser.floatValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
|
||||||
|
ignoreUnmapped = parser.booleanValue();
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_MODE_FIELD)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_MODE_FIELD)) {
|
||||||
scoreMode = HasChildQueryBuilder.parseScoreMode(parser.text());
|
scoreMode = HasChildQueryBuilder.parseScoreMode(parser.text());
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
} 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
|
@Override
|
||||||
|
@ -208,19 +243,24 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
|
||||||
return Objects.equals(query, that.query)
|
return Objects.equals(query, that.query)
|
||||||
&& Objects.equals(path, that.path)
|
&& Objects.equals(path, that.path)
|
||||||
&& Objects.equals(scoreMode, that.scoreMode)
|
&& Objects.equals(scoreMode, that.scoreMode)
|
||||||
&& Objects.equals(innerHitBuilder, that.innerHitBuilder);
|
&& Objects.equals(innerHitBuilder, that.innerHitBuilder)
|
||||||
|
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int doHashCode() {
|
protected int doHashCode() {
|
||||||
return Objects.hash(query, path, scoreMode, innerHitBuilder);
|
return Objects.hash(query, path, scoreMode, innerHitBuilder, ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||||
ObjectMapper nestedObjectMapper = context.getObjectMapper(path);
|
ObjectMapper nestedObjectMapper = context.getObjectMapper(path);
|
||||||
if (nestedObjectMapper == null) {
|
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()) {
|
if (!nestedObjectMapper.nested().isNested()) {
|
||||||
throw new IllegalStateException("[" + NAME + "] nested object under path [" + path + "] is not of nested type");
|
throw new IllegalStateException("[" + NAME + "] nested object under path [" + path + "] is not of nested type");
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.search.BooleanClause;
|
import org.apache.lucene.search.BooleanClause;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.DocValuesTermsQuery;
|
import org.apache.lucene.search.DocValuesTermsQuery;
|
||||||
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.TermQuery;
|
import org.apache.lucene.search.TermQuery;
|
||||||
import org.elasticsearch.common.ParseField;
|
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 String NAME = "parent_id";
|
||||||
public static final ParseField QUERY_NAME_FIELD = new ParseField(NAME);
|
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 ID_FIELD = new ParseField("id");
|
||||||
private static final ParseField TYPE_FIELD = new ParseField("type", "child_type");
|
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 type;
|
||||||
private final String id;
|
private final String id;
|
||||||
|
|
||||||
|
private boolean ignoreUnmapped = false;
|
||||||
|
|
||||||
public ParentIdQueryBuilder(String type, String id) {
|
public ParentIdQueryBuilder(String type, String id) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -61,12 +70,14 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
|
||||||
super(in);
|
super(in);
|
||||||
type = in.readString();
|
type = in.readString();
|
||||||
id = in.readString();
|
id = in.readString();
|
||||||
|
ignoreUnmapped = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doWriteTo(StreamOutput out) throws IOException {
|
protected void doWriteTo(StreamOutput out) throws IOException {
|
||||||
out.writeString(type);
|
out.writeString(type);
|
||||||
out.writeString(id);
|
out.writeString(id);
|
||||||
|
out.writeBoolean(ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
|
@ -77,11 +88,31 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
|
||||||
return id;
|
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
|
@Override
|
||||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject(NAME);
|
builder.startObject(NAME);
|
||||||
builder.field(TYPE_FIELD.getPreferredName(), type);
|
builder.field(TYPE_FIELD.getPreferredName(), type);
|
||||||
builder.field(ID_FIELD.getPreferredName(), id);
|
builder.field(ID_FIELD.getPreferredName(), id);
|
||||||
|
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
|
||||||
printBoostAndQueryName(builder);
|
printBoostAndQueryName(builder);
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
}
|
}
|
||||||
|
@ -93,6 +124,7 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
|
||||||
String id = null;
|
String id = null;
|
||||||
String queryName = null;
|
String queryName = null;
|
||||||
String currentFieldName = null;
|
String currentFieldName = null;
|
||||||
|
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
@ -102,6 +134,8 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
|
||||||
type = parser.text();
|
type = parser.text();
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ID_FIELD)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ID_FIELD)) {
|
||||||
id = parser.text();
|
id = parser.text();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
|
||||||
|
ignoreUnmapped = parser.booleanValue();
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
||||||
boost = parser.floatValue();
|
boost = parser.floatValue();
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
} 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);
|
ParentIdQueryBuilder queryBuilder = new ParentIdQueryBuilder(type, id);
|
||||||
queryBuilder.queryName(queryName);
|
queryBuilder.queryName(queryName);
|
||||||
queryBuilder.boost(boost);
|
queryBuilder.boost(boost);
|
||||||
|
queryBuilder.ignoreUnmapped(ignoreUnmapped);
|
||||||
return queryBuilder;
|
return queryBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +159,11 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
|
||||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||||
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
|
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
|
||||||
if (childDocMapper == null) {
|
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();
|
ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
|
||||||
if (parentFieldMapper.active() == false) {
|
if (parentFieldMapper.active() == false) {
|
||||||
|
@ -141,12 +180,14 @@ public final class ParentIdQueryBuilder extends AbstractQueryBuilder<ParentIdQue
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doEquals(ParentIdQueryBuilder that) {
|
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
|
@Override
|
||||||
protected int doHashCode() {
|
protected int doHashCode() {
|
||||||
return Objects.hash(type, id);
|
return Objects.hash(type, id, ignoreUnmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.lucene.queries.TermsQuery;
|
||||||
import org.apache.lucene.search.BooleanClause;
|
import org.apache.lucene.search.BooleanClause;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.ConstantScoreQuery;
|
import org.apache.lucene.search.ConstantScoreQuery;
|
||||||
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.TermQuery;
|
import org.apache.lucene.search.TermQuery;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
|
@ -55,9 +56,10 @@ import org.junit.BeforeClass;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQueryBuilder> {
|
public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQueryBuilder> {
|
||||||
|
@ -124,7 +126,7 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
|
||||||
randomBoolean() ? null : new InnerHitBuilder()
|
randomBoolean() ? null : new InnerHitBuilder()
|
||||||
.setName(randomAsciiOfLengthBetween(1, 10))
|
.setName(randomAsciiOfLengthBetween(1, 10))
|
||||||
.setSize(randomIntBetween(0, 100))
|
.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
|
@Override
|
||||||
|
@ -217,6 +219,7 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
|
||||||
" \"score_mode\" : \"avg\",\n" +
|
" \"score_mode\" : \"avg\",\n" +
|
||||||
" \"min_children\" : 883170873,\n" +
|
" \"min_children\" : 883170873,\n" +
|
||||||
" \"max_children\" : 1217235442,\n" +
|
" \"max_children\" : 1217235442,\n" +
|
||||||
|
" \"ignore_unmapped\" : false,\n" +
|
||||||
" \"boost\" : 2.0,\n" +
|
" \"boost\" : 2.0,\n" +
|
||||||
" \"_name\" : \"WNzYMJKRwePuRBh\",\n" +
|
" \"_name\" : \"WNzYMJKRwePuRBh\",\n" +
|
||||||
" \"inner_hits\" : {\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"));
|
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]"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||||
import com.fasterxml.jackson.core.JsonParseException;
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
|
@ -45,9 +47,10 @@ import org.junit.BeforeClass;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
|
||||||
public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQueryBuilder> {
|
public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQueryBuilder> {
|
||||||
protected static final String PARENT_TYPE = "parent";
|
protected static final String PARENT_TYPE = "parent";
|
||||||
|
@ -109,7 +112,7 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
|
||||||
randomBoolean() ? null : new InnerHitBuilder()
|
randomBoolean() ? null : new InnerHitBuilder()
|
||||||
.setName(randomAsciiOfLengthBetween(1, 10))
|
.setName(randomAsciiOfLengthBetween(1, 10))
|
||||||
.setSize(randomIntBetween(0, 100))
|
.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
|
@Override
|
||||||
|
@ -257,6 +260,7 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
|
||||||
" },\n" +
|
" },\n" +
|
||||||
" \"parent_type\" : \"blog\",\n" +
|
" \"parent_type\" : \"blog\",\n" +
|
||||||
" \"score\" : true,\n" +
|
" \"score\" : true,\n" +
|
||||||
|
" \"ignore_unmapped\" : false,\n" +
|
||||||
" \"boost\" : 1.0\n" +
|
" \"boost\" : 1.0\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
"}";
|
"}";
|
||||||
|
@ -265,4 +269,18 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
|
||||||
assertEquals(json, "blog", parsed.type());
|
assertEquals(json, "blog", parsed.type());
|
||||||
assertEquals(json, "something", ((TermQueryBuilder) parsed.query()).value());
|
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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
package org.elasticsearch.index.query;
|
package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
|
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.instanceOf;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
|
||||||
public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBuilder> {
|
public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBuilder> {
|
||||||
|
|
||||||
|
@ -87,7 +91,7 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
|
||||||
SearchContext.current() == null ? null : new InnerHitBuilder()
|
SearchContext.current() == null ? null : new InnerHitBuilder()
|
||||||
.setName(randomAsciiOfLengthBetween(1, 10))
|
.setName(randomAsciiOfLengthBetween(1, 10))
|
||||||
.setSize(randomIntBetween(0, 100))
|
.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
|
@Override
|
||||||
|
@ -176,6 +180,7 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
|
||||||
" }\n" +
|
" }\n" +
|
||||||
" },\n" +
|
" },\n" +
|
||||||
" \"path\" : \"obj1\",\n" +
|
" \"path\" : \"obj1\",\n" +
|
||||||
|
" \"ignore_unmapped\" : false,\n" +
|
||||||
" \"score_mode\" : \"avg\",\n" +
|
" \"score_mode\" : \"avg\",\n" +
|
||||||
" \"boost\" : 1.0\n" +
|
" \"boost\" : 1.0\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
|
@ -186,4 +191,17 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
|
||||||
|
|
||||||
assertEquals(json, ScoreMode.Avg, parsed.scoreMode());
|
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]"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.index.query;
|
||||||
|
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.DocValuesTermsQuery;
|
import org.apache.lucene.search.DocValuesTermsQuery;
|
||||||
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.TermQuery;
|
import org.apache.lucene.search.TermQuery;
|
||||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||||
|
@ -34,6 +35,10 @@ import org.hamcrest.Matchers;
|
||||||
|
|
||||||
import java.io.IOException;
|
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> {
|
public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQueryBuilder> {
|
||||||
|
|
||||||
protected static final String PARENT_TYPE = "parent";
|
protected static final String PARENT_TYPE = "parent";
|
||||||
|
@ -84,7 +89,7 @@ public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQue
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ParentIdQueryBuilder doCreateTestQueryBuilder() {
|
protected ParentIdQueryBuilder doCreateTestQueryBuilder() {
|
||||||
return new ParentIdQueryBuilder(CHILD_TYPE, randomAsciiOfLength(4));
|
return new ParentIdQueryBuilder(CHILD_TYPE, randomAsciiOfLength(4)).ignoreUnmapped(randomBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -108,6 +113,7 @@ public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQue
|
||||||
" \"parent_id\" : {\n" +
|
" \"parent_id\" : {\n" +
|
||||||
" \"type\" : \"child\",\n" +
|
" \"type\" : \"child\",\n" +
|
||||||
" \"id\" : \"123\",\n" +
|
" \"id\" : \"123\",\n" +
|
||||||
|
" \"ignore_unmapped\" : false,\n" +
|
||||||
" \"boost\" : 3.0,\n" +
|
" \"boost\" : 3.0,\n" +
|
||||||
" \"_name\" : \"name\"" +
|
" \"_name\" : \"name\"" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
|
@ -120,4 +126,17 @@ public class ParentIdQueryBuilderTests extends AbstractQueryTestCase<ParentIdQue
|
||||||
assertThat(queryBuilder.queryName(), Matchers.equalTo("name"));
|
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]"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,3 +72,12 @@ a match:
|
||||||
|
|
||||||
The `min_children` and `max_children` parameters can be combined with
|
The `min_children` and `max_children` parameters can be combined with
|
||||||
the `score_mode` parameter.
|
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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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`,
|
scoring of parent. It defaults to `avg`, but can be `sum`, `min`,
|
||||||
`max` and `none`.
|
`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
|
Multi level nesting is automatically supported, and detected, resulting
|
||||||
in an inner nested query to automatically match the relevant nesting
|
in an inner nested query to automatically match the relevant nesting
|
||||||
level (and not root) if it exists within another nested query.
|
level (and not root) if it exists within another nested query.
|
||||||
|
|
|
@ -8,10 +8,10 @@ The `parent_id` query can be used to find child documents which belong to a part
|
||||||
[source,js]
|
[source,js]
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
{
|
{
|
||||||
"parent_id" : {
|
"parent_id" : {
|
||||||
"type" : "blog_tag",
|
"type" : "blog_tag",
|
||||||
"id" : "1"
|
"id" : "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
|
@ -40,4 +40,9 @@ This query has two required parameters:
|
||||||
[horizontal]
|
[horizontal]
|
||||||
`type`:: The **child** type. This must be a type with `_parent` field.
|
`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.
|
||||||
|
|
Loading…
Reference in New Issue