Fix `missing` on aggs on `boolean` fields. (#22135)

The creation of the `ValuesSource` used to pass `DateTimeZone.UTC` as a time
zone all the time in case of empty fields in spite of the fact that all doc
value formats but the date one reject this parameter.

This commit centralizes the creation of the `ValuesSource` and adds unit tests
to it.

Closes #22009
This commit is contained in:
Adrien Grand 2016-12-14 10:03:09 +01:00 committed by GitHub
parent 7e5058037b
commit 149ef74b26
8 changed files with 433 additions and 116 deletions

View File

@ -262,7 +262,7 @@ public interface DocValueFormat extends NamedWriteable {
@Override
public double parseDouble(String value, boolean roundUp, LongSupplier now) {
throw new UnsupportedOperationException();
return parseLong(value, roundUp, now);
}
@Override

View File

@ -68,6 +68,7 @@ public enum ValueType implements Writeable {
}
},
IP((byte) 6, "ip", "ip", ValuesSourceType.BYTES, IndexFieldData.class, DocValueFormat.IP),
// TODO: what is the difference between "number" and "numeric"?
NUMERIC((byte) 7, "numeric", "numeric", ValuesSourceType.NUMERIC, IndexNumericFieldData.class, DocValueFormat.RAW) {
@Override
public boolean isNumeric() {
@ -79,6 +80,12 @@ public enum ValueType implements Writeable {
public boolean isGeoPoint() {
return true;
}
},
BOOLEAN((byte) 9, "boolean", "boolean", ValuesSourceType.NUMERIC, IndexNumericFieldData.class, DocValueFormat.BOOLEAN) {
@Override
public boolean isNumeric() {
return super.isNumeric();
}
};
final String description;
@ -150,7 +157,9 @@ public enum ValueType implements Writeable {
case "byte": return LONG;
case "date": return DATE;
case "ip": return IP;
case "boolean": return BOOLEAN;
default:
// TODO: do not be lenient here
return null;
}
}

View File

@ -18,20 +18,12 @@
*/
package org.elasticsearch.search.aggregations.support;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.aggregations.AggregationInitializationException;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationInitializationException;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
import org.elasticsearch.search.aggregations.AggregatorFactory;
@ -298,98 +290,14 @@ public abstract class ValuesSourceAggregationBuilder<VS extends ValuesSource, AB
}
protected ValuesSourceConfig<VS> resolveConfig(SearchContext context) {
ValuesSourceConfig<VS> config = config(context);
return config;
ValueType valueType = this.valueType != null ? this.valueType : targetValueType;
return ValuesSourceConfig.resolve(context.getQueryShardContext(),
valueType, field, script, missing, timeZone, format);
}
protected abstract ValuesSourceAggregatorFactory<VS, ?> innerBuild(SearchContext context, ValuesSourceConfig<VS> config,
AggregatorFactory<?> parent, AggregatorFactories.Builder subFactoriesBuilder) throws IOException;
public ValuesSourceConfig<VS> config(SearchContext context) {
ValueType valueType = this.valueType != null ? this.valueType : targetValueType;
if (field == null) {
if (script == null) {
@SuppressWarnings("unchecked")
ValuesSourceConfig<VS> config = new ValuesSourceConfig(ValuesSourceType.ANY);
config.format(resolveFormat(null, valueType));
return config;
}
ValuesSourceType valuesSourceType = valueType != null ? valueType.getValuesSourceType() : this.valuesSourceType;
if (valuesSourceType == null || valuesSourceType == ValuesSourceType.ANY) {
// the specific value source type is undefined, but for scripts,
// we need to have a specific value source
// type to know how to handle the script values, so we fallback
// on Bytes
valuesSourceType = ValuesSourceType.BYTES;
}
ValuesSourceConfig<VS> config = new ValuesSourceConfig<VS>(valuesSourceType);
config.missing(missing);
config.timezone(timeZone);
config.format(resolveFormat(format, valueType));
config.script(createScript(script, context));
config.scriptValueType(valueType);
return config;
}
MappedFieldType fieldType = context.smartNameFieldType(field);
if (fieldType == null) {
ValuesSourceType valuesSourceType = valueType != null ? valueType.getValuesSourceType() : this.valuesSourceType;
ValuesSourceConfig<VS> config = new ValuesSourceConfig<>(valuesSourceType);
config.missing(missing);
config.timezone(timeZone);
config.format(resolveFormat(format, valueType));
config.unmapped(true);
if (valueType != null) {
// todo do we really need this for unmapped?
config.scriptValueType(valueType);
}
return config;
}
IndexFieldData<?> indexFieldData = context.fieldData().getForField(fieldType);
ValuesSourceConfig<VS> config;
if (valuesSourceType == ValuesSourceType.ANY) {
if (indexFieldData instanceof IndexNumericFieldData) {
config = new ValuesSourceConfig<>(ValuesSourceType.NUMERIC);
} else if (indexFieldData instanceof IndexGeoPointFieldData) {
config = new ValuesSourceConfig<>(ValuesSourceType.GEOPOINT);
} else {
config = new ValuesSourceConfig<>(ValuesSourceType.BYTES);
}
} else {
config = new ValuesSourceConfig(valuesSourceType);
}
config.fieldContext(new FieldContext(field, indexFieldData, fieldType));
config.missing(missing);
config.timezone(timeZone);
config.script(createScript(script, context));
config.format(fieldType.docValueFormat(format, timeZone));
return config;
}
private SearchScript createScript(Script script, SearchContext context) {
if (script == null) {
return null;
} else {
return context.getQueryShardContext().getSearchScript(script, ScriptContext.Standard.AGGS);
}
}
private static DocValueFormat resolveFormat(@Nullable String format, @Nullable ValueType valueType) {
if (valueType == null) {
return DocValueFormat.RAW; // we can't figure it out
}
DocValueFormat valueFormat = valueType.defaultFormat;
if (valueFormat instanceof DocValueFormat.Decimal && format != null) {
valueFormat = new DocValueFormat.Decimal(format);
}
return valueFormat;
}
@Override
public final XContentBuilder internalXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();

View File

@ -49,7 +49,7 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource, AF
@Override
public Aggregator createInternal(Aggregator parent, boolean collectsFromSingleBucket,
List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) throws IOException {
VS vs = config.toValuesSource(context);
VS vs = config.toValuesSource(context.getQueryShardContext());
if (vs == null) {
return createUnmapped(parent, pipelineAggregators, metaData);
}

View File

@ -27,17 +27,115 @@ import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import org.elasticsearch.index.fielddata.IndexOrdinalsFieldData;
import org.elasticsearch.index.fielddata.plain.ParentChildIndexFieldData;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.AggregationExecutionException;
import org.elasticsearch.search.internal.SearchContext;
import org.joda.time.DateTimeZone;
import java.io.IOException;
/**
* A configuration that tells aggregations how to retrieve data from the index
* in order to run a specific aggregation.
*/
public class ValuesSourceConfig<VS extends ValuesSource> {
/**
* Resolve a {@link ValuesSourceConfig} given configuration parameters.
*/
public static <VS extends ValuesSource> ValuesSourceConfig<VS> resolve(
QueryShardContext context,
ValueType valueType,
String field, Script script,
Object missing,
DateTimeZone timeZone,
String format) {
if (field == null) {
if (script == null) {
@SuppressWarnings("unchecked")
ValuesSourceConfig<VS> config = new ValuesSourceConfig<>(ValuesSourceType.ANY);
config.format(resolveFormat(null, valueType));
return config;
}
ValuesSourceType valuesSourceType = valueType != null ? valueType.getValuesSourceType() : ValuesSourceType.ANY;
if (valuesSourceType == ValuesSourceType.ANY) {
// the specific value source type is undefined, but for scripts,
// we need to have a specific value source
// type to know how to handle the script values, so we fallback
// on Bytes
valuesSourceType = ValuesSourceType.BYTES;
}
ValuesSourceConfig<VS> config = new ValuesSourceConfig<VS>(valuesSourceType);
config.missing(missing);
config.timezone(timeZone);
config.format(resolveFormat(format, valueType));
config.script(createScript(script, context));
config.scriptValueType(valueType);
return config;
}
MappedFieldType fieldType = context.fieldMapper(field);
if (fieldType == null) {
ValuesSourceType valuesSourceType = valueType != null ? valueType.getValuesSourceType() : ValuesSourceType.ANY;
ValuesSourceConfig<VS> config = new ValuesSourceConfig<>(valuesSourceType);
config.missing(missing);
config.timezone(timeZone);
config.format(resolveFormat(format, valueType));
config.unmapped(true);
if (valueType != null) {
// todo do we really need this for unmapped?
config.scriptValueType(valueType);
}
return config;
}
IndexFieldData<?> indexFieldData = context.getForField(fieldType);
ValuesSourceConfig<VS> config;
if (valueType == null) {
if (indexFieldData instanceof IndexNumericFieldData) {
config = new ValuesSourceConfig<>(ValuesSourceType.NUMERIC);
} else if (indexFieldData instanceof IndexGeoPointFieldData) {
config = new ValuesSourceConfig<>(ValuesSourceType.GEOPOINT);
} else {
config = new ValuesSourceConfig<>(ValuesSourceType.BYTES);
}
} else {
config = new ValuesSourceConfig<>(valueType.getValuesSourceType());
}
config.fieldContext(new FieldContext(field, indexFieldData, fieldType));
config.missing(missing);
config.timezone(timeZone);
config.script(createScript(script, context));
config.format(fieldType.docValueFormat(format, timeZone));
return config;
}
private static SearchScript createScript(Script script, QueryShardContext context) {
if (script == null) {
return null;
} else {
return context.getSearchScript(script, ScriptContext.Standard.AGGS);
}
}
private static DocValueFormat resolveFormat(@Nullable String format, @Nullable ValueType valueType) {
if (valueType == null) {
return DocValueFormat.RAW; // we can't figure it out
}
DocValueFormat valueFormat = valueType.defaultFormat;
if (valueFormat instanceof DocValueFormat.Decimal && format != null) {
valueFormat = new DocValueFormat.Decimal(format);
}
return valueFormat;
}
private final ValuesSourceType valueSourceType;
private FieldContext fieldContext;
private SearchScript script;
@ -125,7 +223,7 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
/** Get a value source given its configuration. A return value of null indicates that
* no value source could be built. */
@Nullable
public VS toValuesSource(SearchContext context) throws IOException {
public VS toValuesSource(QueryShardContext context) throws IOException {
if (!valid()) {
throw new IllegalStateException(
"value source config is invalid; must have either a field context or a script or marked as unwrapped");
@ -143,8 +241,7 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
} else if (valueSourceType() == ValuesSourceType.ANY || valueSourceType() == ValuesSourceType.BYTES) {
vs = (VS) ValuesSource.Bytes.WithOrdinals.EMPTY;
} else {
throw new SearchParseException(context, "Can't deal with unmapped ValuesSource type "
+ valueSourceType(), null);
throw new IllegalArgumentException("Can't deal with unmapped ValuesSource type " + valueSourceType());
}
} else {
vs = originalValuesSource();
@ -162,17 +259,7 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
return (VS) MissingValues.replaceMissing((ValuesSource.Bytes) vs, missing);
}
} else if (vs instanceof ValuesSource.Numeric) {
Number missing = null;
if (missing() instanceof Number) {
missing = (Number) missing();
} else {
if (fieldContext() != null && fieldContext().fieldType() != null) {
missing = fieldContext().fieldType().docValueFormat(null, DateTimeZone.UTC)
.parseDouble(missing().toString(), false, context.getQueryShardContext()::nowInMillis);
} else {
missing = Double.parseDouble(missing().toString());
}
}
Number missing = format.parseDouble(missing().toString(), false, context::nowInMillis);
return (VS) MissingValues.replaceMissing((ValuesSource.Numeric) vs, missing);
} else if (vs instanceof ValuesSource.GeoPoint) {
// TODO: also support the structured formats of geo points
@ -180,7 +267,7 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
return (VS) MissingValues.replaceMissing((ValuesSource.GeoPoint) vs, missing);
} else {
// Should not happen
throw new SearchParseException(context, "Can't apply missing values on a " + vs.getClass(), null);
throw new IllegalArgumentException("Can't apply missing values on a " + vs.getClass());
}
}

View File

@ -0,0 +1,49 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.support;
import org.elasticsearch.test.ESTestCase;
public class ValueTypeTests extends ESTestCase {
public void testResolve() {
assertEquals(ValueType.STRING, ValueType.resolveForScript("string"));
assertEquals(ValueType.DOUBLE, ValueType.resolveForScript("float"));
assertEquals(ValueType.DOUBLE, ValueType.resolveForScript("double"));
assertEquals(ValueType.LONG, ValueType.resolveForScript("byte"));
assertEquals(ValueType.LONG, ValueType.resolveForScript("short"));
assertEquals(ValueType.LONG, ValueType.resolveForScript("integer"));
assertEquals(ValueType.LONG, ValueType.resolveForScript("long"));
assertEquals(ValueType.DATE, ValueType.resolveForScript("date"));
assertEquals(ValueType.IP, ValueType.resolveForScript("ip"));
assertEquals(ValueType.BOOLEAN, ValueType.resolveForScript("boolean"));
}
public void testCompatibility() {
assertTrue(ValueType.DOUBLE.isA(ValueType.NUMERIC));
assertTrue(ValueType.LONG.isA(ValueType.NUMERIC));
assertTrue(ValueType.DATE.isA(ValueType.LONG));
assertTrue(ValueType.NUMERIC.isA(ValueType.NUMBER));
assertTrue(ValueType.BOOLEAN.isA(ValueType.NUMBER));
assertFalse(ValueType.STRING.isA(ValueType.NUMBER));
assertFalse(ValueType.DATE.isA(ValueType.IP));
}
}

View File

@ -0,0 +1,264 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.support;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.engine.Engine.Searcher;
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.test.ESSingleNodeTestCase;
public class ValuesSourceConfigTests extends ESSingleNodeTestCase {
public void testKeyword() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type",
"bytes", "type=keyword");
client().prepareIndex("index", "type", "1")
.setSource("bytes", "abc")
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Bytes> config = ValuesSourceConfig.resolve(
context, null, "bytes", null, null, null, null);
ValuesSource.Bytes valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedBinaryDocValues values = valuesSource.bytesValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(new BytesRef("abc"), values.valueAt(0));
}
}
public void testEmptyKeyword() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type",
"bytes", "type=keyword");
client().prepareIndex("index", "type", "1")
.setSource()
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Bytes> config = ValuesSourceConfig.resolve(
context, null, "bytes", null, null, null, null);
ValuesSource.Bytes valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedBinaryDocValues values = valuesSource.bytesValues(ctx);
values.setDocument(0);
assertEquals(0, values.count());
config = ValuesSourceConfig.resolve(
context, null, "bytes", null, "abc", null, null);
valuesSource = config.toValuesSource(context);
values = valuesSource.bytesValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(new BytesRef("abc"), values.valueAt(0));
}
}
public void testUnmappedKeyword() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type");
client().prepareIndex("index", "type", "1")
.setSource()
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Bytes> config = ValuesSourceConfig.resolve(
context, ValueType.STRING, "bytes", null, null, null, null);
ValuesSource.Bytes valuesSource = config.toValuesSource(context);
assertNull(valuesSource);
config = ValuesSourceConfig.resolve(
context, ValueType.STRING, "bytes", null, "abc", null, null);
valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedBinaryDocValues values = valuesSource.bytesValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(new BytesRef("abc"), values.valueAt(0));
}
}
public void testLong() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type",
"long", "type=long");
client().prepareIndex("index", "type", "1")
.setSource("long", 42)
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Numeric> config = ValuesSourceConfig.resolve(
context, null, "long", null, null, null, null);
ValuesSource.Numeric valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedNumericDocValues values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(42, values.valueAt(0));
}
}
public void testEmptyLong() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type",
"long", "type=long");
client().prepareIndex("index", "type", "1")
.setSource()
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Numeric> config = ValuesSourceConfig.resolve(
context, null, "long", null, null, null, null);
ValuesSource.Numeric valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedNumericDocValues values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(0, values.count());
config = ValuesSourceConfig.resolve(
context, null, "long", null, 42, null, null);
valuesSource = config.toValuesSource(context);
values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(42, values.valueAt(0));
}
}
public void testUnmappedLong() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type");
client().prepareIndex("index", "type", "1")
.setSource()
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Numeric> config = ValuesSourceConfig.resolve(
context, ValueType.NUMBER, "long", null, null, null, null);
ValuesSource.Numeric valuesSource = config.toValuesSource(context);
assertNull(valuesSource);
config = ValuesSourceConfig.resolve(
context, ValueType.NUMBER, "long", null, 42, null, null);
valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedNumericDocValues values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(42, values.valueAt(0));
}
}
public void testBoolean() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type",
"bool", "type=boolean");
client().prepareIndex("index", "type", "1")
.setSource("bool", true)
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Numeric> config = ValuesSourceConfig.resolve(
context, null, "bool", null, null, null, null);
ValuesSource.Numeric valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedNumericDocValues values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(1, values.valueAt(0));
}
}
public void testEmptyBoolean() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type",
"bool", "type=boolean");
client().prepareIndex("index", "type", "1")
.setSource()
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Numeric> config = ValuesSourceConfig.resolve(
context, null, "bool", null, null, null, null);
ValuesSource.Numeric valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedNumericDocValues values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(0, values.count());
config = ValuesSourceConfig.resolve(
context, null, "bool", null, true, null, null);
valuesSource = config.toValuesSource(context);
values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(1, values.valueAt(0));
}
}
public void testUnmappedBoolean() throws Exception {
IndexService indexService = createIndex("index", Settings.EMPTY, "type");
client().prepareIndex("index", "type", "1")
.setSource()
.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
try (Searcher searcher = indexService.getShard(0).acquireSearcher("test")) {
QueryShardContext context = indexService.newQueryShardContext(0, searcher.reader(), () -> 42L);
ValuesSourceConfig<ValuesSource.Numeric> config = ValuesSourceConfig.resolve(
context, ValueType.BOOLEAN, "bool", null, null, null, null);
ValuesSource.Numeric valuesSource = config.toValuesSource(context);
assertNull(valuesSource);
config = ValuesSourceConfig.resolve(
context, ValueType.BOOLEAN, "bool", null, true, null, null);
valuesSource = config.toValuesSource(context);
LeafReaderContext ctx = searcher.reader().leaves().get(0);
SortedNumericDocValues values = valuesSource.longValues(ctx);
values.setDocument(0);
assertEquals(1, values.count());
assertEquals(1, values.valueAt(0));
}
}
}

View File

@ -49,7 +49,7 @@ public abstract class MultiValuesSourceAggregatorFactory<VS extends ValuesSource
HashMap<String, VS> valuesSources = new HashMap<>();
for (Map.Entry<String, ValuesSourceConfig<VS>> config : configs.entrySet()) {
VS vs = config.getValue().toValuesSource(context);
VS vs = config.getValue().toValuesSource(context.getQueryShardContext());
if (vs != null) {
valuesSources.put(config.getKey(), vs);
}