Fix unmapped field handling in the composite aggregation (#41280)

The `composite` aggregation maps unknown fields as numerics, this means that
any `after` value that is set on a query with an unmapped field on some indices
will fail if the provided value is not numeric. This commit changes the default
value source to use keyword instead in order to be able to parse any type of after
values.
This commit is contained in:
Jim Ferenczi 2019-04-18 22:48:22 +02:00 committed by jimczi
parent 754037b71e
commit 8f73e1e883
2 changed files with 107 additions and 2 deletions

View File

@ -18,6 +18,22 @@ setup:
nested_long: nested_long:
type: long type: long
- do:
indices.create:
index: other
body:
mappings:
properties:
date:
type: date
long:
type: long
nested:
type: nested
properties:
nested_long:
type: long
- do: - do:
index: index:
index: test index: test
@ -54,9 +70,15 @@ setup:
id: 6 id: 6
body: { "date": "2017-10-21T07:00:00" } body: { "date": "2017-10-21T07:00:00" }
- do:
index:
index: other
id: 0
body: { "date": "2017-10-20T03:08:45" }
- do: - do:
indices.refresh: indices.refresh:
index: [test] index: [test, other]
--- ---
"Simple Composite aggregation": "Simple Composite aggregation":
@ -419,3 +441,84 @@ setup:
- match: { aggregations.1.2.buckets.0.doc_count: 2 } - match: { aggregations.1.2.buckets.0.doc_count: 2 }
- match: { aggregations.1.2.buckets.1.key.nested: 1000 } - match: { aggregations.1.2.buckets.1.key.nested: 1000 }
- match: { aggregations.1.2.buckets.1.doc_count: 1 } - match: { aggregations.1.2.buckets.1.doc_count: 1 }
---
"Composite aggregation with unmapped field":
- skip:
version: " - 7.0.99"
reason: starting in 7.1 the composite aggregation handles unmapped fields as keywords
- do:
search:
rest_total_hits_as_int: true
index: [test, other]
body:
aggregations:
test:
composite:
sources: [
{
"long": {
"terms": {
"field": "long"
}
}
},
{
"kw": {
"terms": {
"field": "keyword"
}
}
}
]
- match: {hits.total: 7}
- length: { aggregations.test.buckets: 5 }
- match: { aggregations.test.buckets.0.key.long: 0}
- match: { aggregations.test.buckets.0.key.kw: "bar" }
- match: { aggregations.test.buckets.0.doc_count: 2 }
- match: { aggregations.test.buckets.1.key.long: 10 }
- match: { aggregations.test.buckets.1.key.kw: "foo"}
- match: { aggregations.test.buckets.1.doc_count: 1 }
- match: { aggregations.test.buckets.2.key.long: 20 }
- match: { aggregations.test.buckets.2.key.kw: "foo" }
- match: { aggregations.test.buckets.2.doc_count: 1 }
- match: { aggregations.test.buckets.3.key.long: 100}
- match: { aggregations.test.buckets.3.key.kw: "bar" }
- match: { aggregations.test.buckets.3.doc_count: 1 }
- match: { aggregations.test.buckets.4.key.long: 1000 }
- match: { aggregations.test.buckets.4.key.kw: "bar" }
- match: { aggregations.test.buckets.4.doc_count: 1 }
- do:
search:
rest_total_hits_as_int: true
index: [test, other]
body:
aggregations:
test:
composite:
after: { "long": 100, "kw": "bar" }
sources: [
{
"long": {
"terms": {
"field": "long"
}
}
},
{
"kw": {
"terms": {
"field": "keyword"
}
}
}
]
- match: {hits.total: 7}
- length: { aggregations.test.buckets: 1 }
- match: { aggregations.test.buckets.0.key.long: 1000 }
- match: { aggregations.test.buckets.0.key.kw: "bar" }
- match: { aggregations.test.buckets.0.doc_count: 1 }

View File

@ -83,7 +83,9 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
protected CompositeValuesSourceConfig innerBuild(SearchContext context, ValuesSourceConfig<?> config) throws IOException { protected CompositeValuesSourceConfig innerBuild(SearchContext context, ValuesSourceConfig<?> config) throws IOException {
ValuesSource vs = config.toValuesSource(context.getQueryShardContext()); ValuesSource vs = config.toValuesSource(context.getQueryShardContext());
if (vs == null) { if (vs == null) {
vs = ValuesSource.Numeric.EMPTY; // The field is unmapped so we use a value source that can parse any type of values.
// This is needed because the after values are parsed even when there are no values to process.
vs = ValuesSource.Bytes.WithOrdinals.EMPTY;
} }
final MappedFieldType fieldType = config.fieldContext() != null ? config.fieldContext().fieldType() : null; final MappedFieldType fieldType = config.fieldContext() != null ? config.fieldContext().fieldType() : null;
final DocValueFormat format; final DocValueFormat format;