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:
parent
7e5058037b
commit
149ef74b26
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue