Skip to content

Commit 686aff1

Browse files
committed
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)
1 parent acec464 commit 686aff1

File tree

12 files changed

+287
-29
lines changed

12 files changed

+287
-29
lines changed

core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
6363
* The default minimum number of children that are required to match for the parent to be considered a match.
6464
*/
6565
public static final int DEFAULT_MIN_CHILDREN = 0;
66+
67+
/**
68+
* The default value for ignore_unmapped.
69+
*/
70+
public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
6671
/*
6772
* The default score mode that is used to combine score coming from multiple parent documents.
6873
*/
@@ -74,6 +79,7 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
7479
private static final ParseField MIN_CHILDREN_FIELD = new ParseField("min_children");
7580
private static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode");
7681
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
82+
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
7783

7884
private final QueryBuilder<?> query;
7985

@@ -87,6 +93,8 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
8793

8894
private InnerHitBuilder innerHitBuilder;
8995

96+
private boolean ignoreUnmapped = false;
97+
9098

9199
public HasChildQueryBuilder(String type, QueryBuilder<?> query, int maxChildren, int minChildren, ScoreMode scoreMode,
92100
InnerHitBuilder innerHitBuilder) {
@@ -123,6 +131,7 @@ public HasChildQueryBuilder(StreamInput in) throws IOException {
123131
scoreMode = ScoreMode.values()[in.readVInt()];
124132
query = in.readQuery();
125133
innerHitBuilder = in.readOptionalWriteable(InnerHitBuilder::new);
134+
ignoreUnmapped = in.readBoolean();
126135
}
127136

128137
@Override
@@ -133,6 +142,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
133142
out.writeVInt(scoreMode.ordinal());
134143
out.writeQuery(query);
135144
out.writeOptionalWriteable(innerHitBuilder);
145+
out.writeBoolean(ignoreUnmapped);
136146
}
137147

138148
/**
@@ -220,6 +230,25 @@ public int minChildren() {
220230
*/
221231
public int maxChildren() { return maxChildren; }
222232

233+
/**
234+
* Sets whether the query builder should ignore unmapped types (and run a
235+
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
236+
* the type is unmapped.
237+
*/
238+
public HasChildQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
239+
this.ignoreUnmapped = ignoreUnmapped;
240+
return this;
241+
}
242+
243+
/**
244+
* Gets whether the query builder will ignore unmapped types (and run a
245+
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
246+
* the type is unmapped.
247+
*/
248+
public boolean ignoreUnmapped() {
249+
return ignoreUnmapped;
250+
}
251+
223252
@Override
224253
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
225254
builder.startObject(NAME);
@@ -229,6 +258,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
229258
builder.field(SCORE_MODE_FIELD.getPreferredName(), scoreModeAsString(scoreMode));
230259
builder.field(MIN_CHILDREN_FIELD.getPreferredName(), minChildren);
231260
builder.field(MAX_CHILDREN_FIELD.getPreferredName(), maxChildren);
261+
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
232262
printBoostAndQueryName(builder);
233263
if (innerHitBuilder != null) {
234264
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHitBuilder, params);
@@ -243,6 +273,7 @@ public static HasChildQueryBuilder fromXContent(QueryParseContext parseContext)
243273
ScoreMode scoreMode = HasChildQueryBuilder.DEFAULT_SCORE_MODE;
244274
int minChildren = HasChildQueryBuilder.DEFAULT_MIN_CHILDREN;
245275
int maxChildren = HasChildQueryBuilder.DEFAULT_MAX_CHILDREN;
276+
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
246277
String queryName = null;
247278
InnerHitBuilder innerHitBuilder = null;
248279
String currentFieldName = null;
@@ -272,6 +303,8 @@ public static HasChildQueryBuilder fromXContent(QueryParseContext parseContext)
272303
minChildren = parser.intValue(true);
273304
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_CHILDREN_FIELD)) {
274305
maxChildren = parser.intValue(true);
306+
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
307+
ignoreUnmapped = parser.booleanValue();
275308
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
276309
queryName = parser.text();
277310
} else {
@@ -283,6 +316,7 @@ public static HasChildQueryBuilder fromXContent(QueryParseContext parseContext)
283316
scoreMode, innerHitBuilder);
284317
hasChildQueryBuilder.queryName(queryName);
285318
hasChildQueryBuilder.boost(boost);
319+
hasChildQueryBuilder.ignoreUnmapped(ignoreUnmapped);
286320
return hasChildQueryBuilder;
287321
}
288322

@@ -331,7 +365,11 @@ protected Query doToQuery(QueryShardContext context) throws IOException {
331365
}
332366
DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
333367
if (childDocMapper == null) {
334-
throw new QueryShardException(context, "[" + NAME + "] no mapping found for type [" + type + "]");
368+
if (ignoreUnmapped) {
369+
return new MatchNoDocsQuery();
370+
} else {
371+
throw new QueryShardException(context, "[" + NAME + "] no mapping found for type [" + type + "]");
372+
}
335373
}
336374
ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
337375
if (parentFieldMapper.active() == false) {
@@ -344,8 +382,8 @@ protected Query doToQuery(QueryShardContext context) throws IOException {
344382
String parentType = parentFieldMapper.type();
345383
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(parentType);
346384
if (parentDocMapper == null) {
347-
throw new QueryShardException(context, "[" + NAME + "] Type [" + type + "] points to a non existent parent type ["
348-
+ parentType + "]");
385+
throw new QueryShardException(context,
386+
"[" + NAME + "] Type [" + type + "] points to a non existent parent type [" + parentType + "]");
349387
}
350388

351389
if (maxChildren > 0 && maxChildren < minChildren) {
@@ -464,12 +502,13 @@ protected boolean doEquals(HasChildQueryBuilder that) {
464502
&& Objects.equals(scoreMode, that.scoreMode)
465503
&& Objects.equals(minChildren, that.minChildren)
466504
&& Objects.equals(maxChildren, that.maxChildren)
467-
&& Objects.equals(innerHitBuilder, that.innerHitBuilder);
505+
&& Objects.equals(innerHitBuilder, that.innerHitBuilder)
506+
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
468507
}
469508

470509
@Override
471510
protected int doHashCode() {
472-
return Objects.hash(query, type, scoreMode, minChildren, maxChildren, innerHitBuilder);
511+
return Objects.hash(query, type, scoreMode, minChildren, maxChildren, innerHitBuilder, ignoreUnmapped);
473512
}
474513

475514
@Override

core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.apache.lucene.search.BooleanClause;
2222
import org.apache.lucene.search.BooleanQuery;
23+
import org.apache.lucene.search.MatchNoDocsQuery;
2324
import org.apache.lucene.search.Query;
2425
import org.apache.lucene.search.join.ScoreMode;
2526
import org.elasticsearch.common.ParseField;
@@ -49,16 +50,23 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
4950

5051
public static final boolean DEFAULT_SCORE = false;
5152

53+
/**
54+
* The default value for ignore_unmapped.
55+
*/
56+
public static final boolean DEFAULT_IGNORE_UNMAPPED = false;
57+
5258
private static final ParseField QUERY_FIELD = new ParseField("query", "filter");
5359
private static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode").withAllDeprecated("score");
5460
private static final ParseField TYPE_FIELD = new ParseField("parent_type", "type");
5561
private static final ParseField SCORE_FIELD = new ParseField("score");
5662
private static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
63+
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
5764

5865
private final QueryBuilder<?> query;
5966
private final String type;
6067
private boolean score = DEFAULT_SCORE;
6168
private InnerHitBuilder innerHit;
69+
private boolean ignoreUnmapped = false;
6270

6371
/**
6472
* @param type The parent type
@@ -94,6 +102,7 @@ public HasParentQueryBuilder(StreamInput in) throws IOException {
94102
score = in.readBoolean();
95103
query = in.readQuery();
96104
innerHit = in.readOptionalWriteable(InnerHitBuilder::new);
105+
ignoreUnmapped = in.readBoolean();
97106
}
98107

99108
@Override
@@ -102,6 +111,7 @@ protected void doWriteTo(StreamOutput out) throws IOException {
102111
out.writeBoolean(score);
103112
out.writeQuery(query);
104113
out.writeOptionalWriteable(innerHit);
114+
out.writeBoolean(ignoreUnmapped);
105115
}
106116

107117
/**
@@ -150,6 +160,25 @@ public InnerHitBuilder innerHit() {
150160
return innerHit;
151161
}
152162

163+
/**
164+
* Sets whether the query builder should ignore unmapped types (and run a
165+
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
166+
* the type is unmapped.
167+
*/
168+
public HasParentQueryBuilder ignoreUnmapped(boolean ignoreUnmapped) {
169+
this.ignoreUnmapped = ignoreUnmapped;
170+
return this;
171+
}
172+
173+
/**
174+
* Gets whether the query builder will ignore unmapped types (and run a
175+
* {@link MatchNoDocsQuery} in place of this query) or throw an exception if
176+
* the type is unmapped.
177+
*/
178+
public boolean ignoreUnmapped() {
179+
return ignoreUnmapped;
180+
}
181+
153182
@Override
154183
protected Query doToQuery(QueryShardContext context) throws IOException {
155184
Query innerQuery;
@@ -166,8 +195,11 @@ protected Query doToQuery(QueryShardContext context) throws IOException {
166195
}
167196
DocumentMapper parentDocMapper = context.getMapperService().documentMapper(type);
168197
if (parentDocMapper == null) {
169-
throw new QueryShardException(context, "[" + NAME + "] query configured 'parent_type' [" + type
170-
+ "] is not a valid type");
198+
if (ignoreUnmapped) {
199+
return new MatchNoDocsQuery();
200+
} else {
201+
throw new QueryShardException(context, "[" + NAME + "] query configured 'parent_type' [" + type + "] is not a valid type");
202+
}
171203
}
172204

173205
if (innerHit != null) {
@@ -220,6 +252,7 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
220252
query.toXContent(builder, params);
221253
builder.field(TYPE_FIELD.getPreferredName(), type);
222254
builder.field(SCORE_FIELD.getPreferredName(), score);
255+
builder.field(IGNORE_UNMAPPED_FIELD.getPreferredName(), ignoreUnmapped);
223256
printBoostAndQueryName(builder);
224257
if (innerHit != null) {
225258
builder.field(INNER_HITS_FIELD.getPreferredName(), innerHit, params);
@@ -234,6 +267,7 @@ public static HasParentQueryBuilder fromXContent(QueryParseContext parseContext)
234267
boolean score = HasParentQueryBuilder.DEFAULT_SCORE;
235268
String queryName = null;
236269
InnerHitBuilder innerHits = null;
270+
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
237271

238272
String currentFieldName = null;
239273
XContentParser.Token token;
@@ -264,6 +298,8 @@ public static HasParentQueryBuilder fromXContent(QueryParseContext parseContext)
264298
}
265299
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_FIELD)) {
266300
score = parser.booleanValue();
301+
} else if (parseContext.parseFieldMatcher().match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
302+
ignoreUnmapped = parser.booleanValue();
267303
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
268304
boost = parser.floatValue();
269305
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
@@ -273,7 +309,8 @@ public static HasParentQueryBuilder fromXContent(QueryParseContext parseContext)
273309
}
274310
}
275311
}
276-
return new HasParentQueryBuilder(parentType, iqb, score, innerHits).queryName(queryName).boost(boost);
312+
return new HasParentQueryBuilder(parentType, iqb, score, innerHits).ignoreUnmapped(ignoreUnmapped).queryName(queryName)
313+
.boost(boost);
277314
}
278315

279316
@Override
@@ -286,12 +323,13 @@ protected boolean doEquals(HasParentQueryBuilder that) {
286323
return Objects.equals(query, that.query)
287324
&& Objects.equals(type, that.type)
288325
&& Objects.equals(score, that.score)
289-
&& Objects.equals(innerHit, that.innerHit);
326+
&& Objects.equals(innerHit, that.innerHit)
327+
&& Objects.equals(ignoreUnmapped, that.ignoreUnmapped);
290328
}
291329

292330
@Override
293331
protected int doHashCode() {
294-
return Objects.hash(query, type, score, innerHit);
332+
return Objects.hash(query, type, score, innerHit, ignoreUnmapped);
295333
}
296334

297335
@Override

0 commit comments

Comments
 (0)