Support _first and _last parameter for missing bucket ordering in composite aggregation (#1942)
Adds support for _first and _last parameters for missing bucket ordering in composite aggregation. By default, if order is asc, missing_bucket at first, if order is desc, missing_bucket at last. If missing_order is "_first" or "_last", regardless order, missing_bucket is at first or last respectively. Signed-off-by: Peng Huo <penghuo@gmail.com>
This commit is contained in:
parent
447b20c457
commit
7a9314a3bb
|
@ -47,8 +47,10 @@ import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.index.mapper.StringFieldType;
|
import org.opensearch.index.mapper.StringFieldType;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.LeafBucketCollector;
|
import org.opensearch.search.aggregations.LeafBucketCollector;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.LongConsumer;
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,10 +70,11 @@ class BinaryValuesSource extends SingleDimensionValuesSource<BytesRef> {
|
||||||
CheckedFunction<LeafReaderContext, SortedBinaryDocValues, IOException> docValuesFunc,
|
CheckedFunction<LeafReaderContext, SortedBinaryDocValues, IOException> docValuesFunc,
|
||||||
DocValueFormat format,
|
DocValueFormat format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
int size,
|
int size,
|
||||||
int reverseMul
|
int reverseMul
|
||||||
) {
|
) {
|
||||||
super(bigArrays, format, fieldType, missingBucket, size, reverseMul);
|
super(bigArrays, format, fieldType, missingBucket, missingOrder, size, reverseMul);
|
||||||
this.breakerConsumer = breakerConsumer;
|
this.breakerConsumer = breakerConsumer;
|
||||||
this.docValuesFunc = docValuesFunc;
|
this.docValuesFunc = docValuesFunc;
|
||||||
this.values = bigArrays.newObjectArray(Math.min(size, 100));
|
this.values = bigArrays.newObjectArray(Math.min(size, 100));
|
||||||
|
@ -101,10 +104,9 @@ class BinaryValuesSource extends SingleDimensionValuesSource<BytesRef> {
|
||||||
@Override
|
@Override
|
||||||
int compare(int from, int to) {
|
int compare(int from, int to) {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (values.get(from) == null) {
|
int result = missingOrder.compare(() -> Objects.isNull(values.get(from)), () -> Objects.isNull(values.get(to)), reverseMul);
|
||||||
return values.get(to) == null ? 0 : -1 * reverseMul;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (values.get(to) == null) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(values.get(from), values.get(to));
|
return compareValues(values.get(from), values.get(to));
|
||||||
|
@ -113,10 +115,9 @@ class BinaryValuesSource extends SingleDimensionValuesSource<BytesRef> {
|
||||||
@Override
|
@Override
|
||||||
int compareCurrent(int slot) {
|
int compareCurrent(int slot) {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (currentValue == null) {
|
int result = missingOrder.compare(() -> Objects.isNull(currentValue), () -> Objects.isNull(values.get(slot)), reverseMul);
|
||||||
return values.get(slot) == null ? 0 : -1 * reverseMul;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (values.get(slot) == null) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(currentValue, values.get(slot));
|
return compareValues(currentValue, values.get(slot));
|
||||||
|
@ -125,10 +126,9 @@ class BinaryValuesSource extends SingleDimensionValuesSource<BytesRef> {
|
||||||
@Override
|
@Override
|
||||||
int compareCurrentWithAfter() {
|
int compareCurrentWithAfter() {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (currentValue == null) {
|
int result = missingOrder.compare(() -> Objects.isNull(currentValue), () -> Objects.isNull(afterValue), reverseMul);
|
||||||
return afterValue == null ? 0 : -1 * reverseMul;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (afterValue == null) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(currentValue, afterValue);
|
return compareValues(currentValue, afterValue);
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
package org.opensearch.search.aggregations.bucket.composite;
|
package org.opensearch.search.aggregations.bucket.composite;
|
||||||
|
|
||||||
|
import org.opensearch.Version;
|
||||||
import org.opensearch.common.io.stream.StreamInput;
|
import org.opensearch.common.io.stream.StreamInput;
|
||||||
import org.opensearch.common.io.stream.StreamOutput;
|
import org.opensearch.common.io.stream.StreamOutput;
|
||||||
import org.opensearch.common.io.stream.Writeable;
|
import org.opensearch.common.io.stream.Writeable;
|
||||||
|
@ -39,6 +40,7 @@ import org.opensearch.common.xcontent.ToXContentFragment;
|
||||||
import org.opensearch.common.xcontent.XContentBuilder;
|
import org.opensearch.common.xcontent.XContentBuilder;
|
||||||
import org.opensearch.index.query.QueryShardContext;
|
import org.opensearch.index.query.QueryShardContext;
|
||||||
import org.opensearch.script.Script;
|
import org.opensearch.script.Script;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.support.ValueType;
|
import org.opensearch.search.aggregations.support.ValueType;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSource;
|
import org.opensearch.search.aggregations.support.ValuesSource;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
||||||
|
@ -49,6 +51,8 @@ import java.io.IOException;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.opensearch.search.aggregations.bucket.missing.MissingOrder.fromString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ValuesSource} builder for {@link CompositeAggregationBuilder}
|
* A {@link ValuesSource} builder for {@link CompositeAggregationBuilder}
|
||||||
*/
|
*/
|
||||||
|
@ -59,6 +63,7 @@ public abstract class CompositeValuesSourceBuilder<AB extends CompositeValuesSou
|
||||||
private Script script = null;
|
private Script script = null;
|
||||||
private ValueType userValueTypeHint = null;
|
private ValueType userValueTypeHint = null;
|
||||||
private boolean missingBucket = false;
|
private boolean missingBucket = false;
|
||||||
|
private MissingOrder missingOrder = MissingOrder.DEFAULT;
|
||||||
private SortOrder order = SortOrder.ASC;
|
private SortOrder order = SortOrder.ASC;
|
||||||
private String format = null;
|
private String format = null;
|
||||||
|
|
||||||
|
@ -76,6 +81,9 @@ public abstract class CompositeValuesSourceBuilder<AB extends CompositeValuesSou
|
||||||
this.userValueTypeHint = ValueType.readFromStream(in);
|
this.userValueTypeHint = ValueType.readFromStream(in);
|
||||||
}
|
}
|
||||||
this.missingBucket = in.readBoolean();
|
this.missingBucket = in.readBoolean();
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_2_0_0)) {
|
||||||
|
this.missingOrder = MissingOrder.readFromStream(in);
|
||||||
|
}
|
||||||
this.order = SortOrder.readFromStream(in);
|
this.order = SortOrder.readFromStream(in);
|
||||||
this.format = in.readOptionalString();
|
this.format = in.readOptionalString();
|
||||||
}
|
}
|
||||||
|
@ -95,6 +103,9 @@ public abstract class CompositeValuesSourceBuilder<AB extends CompositeValuesSou
|
||||||
userValueTypeHint.writeTo(out);
|
userValueTypeHint.writeTo(out);
|
||||||
}
|
}
|
||||||
out.writeBoolean(missingBucket);
|
out.writeBoolean(missingBucket);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_2_0_0)) {
|
||||||
|
missingOrder.writeTo(out);
|
||||||
|
}
|
||||||
order.writeTo(out);
|
order.writeTo(out);
|
||||||
out.writeOptionalString(format);
|
out.writeOptionalString(format);
|
||||||
innerWriteTo(out);
|
innerWriteTo(out);
|
||||||
|
@ -120,6 +131,9 @@ public abstract class CompositeValuesSourceBuilder<AB extends CompositeValuesSou
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
builder.field("format", format);
|
builder.field("format", format);
|
||||||
}
|
}
|
||||||
|
if (MissingOrder.isDefault(missingOrder) == false) {
|
||||||
|
builder.field(MissingOrder.NAME, missingOrder.toString());
|
||||||
|
}
|
||||||
builder.field("order", order);
|
builder.field("order", order);
|
||||||
doXContentBody(builder, params);
|
doXContentBody(builder, params);
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
|
@ -142,6 +156,7 @@ public abstract class CompositeValuesSourceBuilder<AB extends CompositeValuesSou
|
||||||
&& Objects.equals(script, that.script())
|
&& Objects.equals(script, that.script())
|
||||||
&& Objects.equals(userValueTypeHint, that.userValuetypeHint())
|
&& Objects.equals(userValueTypeHint, that.userValuetypeHint())
|
||||||
&& Objects.equals(missingBucket, that.missingBucket())
|
&& Objects.equals(missingBucket, that.missingBucket())
|
||||||
|
&& Objects.equals(missingOrder, that.missingOrder())
|
||||||
&& Objects.equals(order, that.order())
|
&& Objects.equals(order, that.order())
|
||||||
&& Objects.equals(format, that.format());
|
&& Objects.equals(format, that.format());
|
||||||
}
|
}
|
||||||
|
@ -226,6 +241,29 @@ public abstract class CompositeValuesSourceBuilder<AB extends CompositeValuesSou
|
||||||
return missingBucket;
|
return missingBucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link MissingOrder} to use to order missing value.
|
||||||
|
*/
|
||||||
|
public AB missingOrder(MissingOrder missingOrder) {
|
||||||
|
this.missingOrder = missingOrder;
|
||||||
|
return (AB) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link MissingOrder} to use to order missing value.
|
||||||
|
* @param missingOrder "first", "last" or "default".
|
||||||
|
*/
|
||||||
|
public AB missingOrder(String missingOrder) {
|
||||||
|
return missingOrder(fromString(missingOrder));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Missing value order. {@link MissingOrder}.
|
||||||
|
*/
|
||||||
|
public MissingOrder missingOrder() {
|
||||||
|
return missingOrder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link SortOrder} to use to sort values produced this source
|
* Sets the {@link SortOrder} to use to sort values produced this source
|
||||||
*/
|
*/
|
||||||
|
@ -286,6 +324,9 @@ public abstract class CompositeValuesSourceBuilder<AB extends CompositeValuesSou
|
||||||
protected abstract ValuesSourceType getDefaultValuesSourceType();
|
protected abstract ValuesSourceType getDefaultValuesSourceType();
|
||||||
|
|
||||||
public final CompositeValuesSourceConfig build(QueryShardContext queryShardContext) throws IOException {
|
public final CompositeValuesSourceConfig build(QueryShardContext queryShardContext) throws IOException {
|
||||||
|
if (missingBucket == false && missingOrder != MissingOrder.DEFAULT) {
|
||||||
|
throw new IllegalArgumentException(MissingOrder.NAME + " require missing_bucket is true");
|
||||||
|
}
|
||||||
ValuesSourceConfig config = ValuesSourceConfig.resolve(
|
ValuesSourceConfig config = ValuesSourceConfig.resolve(
|
||||||
queryShardContext,
|
queryShardContext,
|
||||||
userValueTypeHint,
|
userValueTypeHint,
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.opensearch.common.Nullable;
|
||||||
import org.opensearch.common.util.BigArrays;
|
import org.opensearch.common.util.BigArrays;
|
||||||
import org.opensearch.index.mapper.MappedFieldType;
|
import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSource;
|
import org.opensearch.search.aggregations.support.ValuesSource;
|
||||||
import org.opensearch.search.sort.SortOrder;
|
import org.opensearch.search.sort.SortOrder;
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ public class CompositeValuesSourceConfig {
|
||||||
private final DocValueFormat format;
|
private final DocValueFormat format;
|
||||||
private final int reverseMul;
|
private final int reverseMul;
|
||||||
private final boolean missingBucket;
|
private final boolean missingBucket;
|
||||||
|
private final MissingOrder missingOrder;
|
||||||
private final boolean hasScript;
|
private final boolean hasScript;
|
||||||
private final SingleDimensionValuesSourceProvider singleDimensionValuesSourceProvider;
|
private final SingleDimensionValuesSourceProvider singleDimensionValuesSourceProvider;
|
||||||
|
|
||||||
|
@ -83,6 +85,7 @@ public class CompositeValuesSourceConfig {
|
||||||
DocValueFormat format,
|
DocValueFormat format,
|
||||||
SortOrder order,
|
SortOrder order,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
boolean hasScript,
|
boolean hasScript,
|
||||||
SingleDimensionValuesSourceProvider singleDimensionValuesSourceProvider
|
SingleDimensionValuesSourceProvider singleDimensionValuesSourceProvider
|
||||||
) {
|
) {
|
||||||
|
@ -94,6 +97,7 @@ public class CompositeValuesSourceConfig {
|
||||||
this.missingBucket = missingBucket;
|
this.missingBucket = missingBucket;
|
||||||
this.hasScript = hasScript;
|
this.hasScript = hasScript;
|
||||||
this.singleDimensionValuesSourceProvider = singleDimensionValuesSourceProvider;
|
this.singleDimensionValuesSourceProvider = singleDimensionValuesSourceProvider;
|
||||||
|
this.missingOrder = missingOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,6 +136,13 @@ public class CompositeValuesSourceConfig {
|
||||||
return missingBucket;
|
return missingBucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link MissingOrder} for the config.
|
||||||
|
*/
|
||||||
|
MissingOrder missingOrder() {
|
||||||
|
return missingOrder;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the source contains a script that can change the value.
|
* Returns true if the source contains a script that can change the value.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.opensearch.common.xcontent.ToXContent.Params;
|
||||||
import org.opensearch.common.xcontent.XContentBuilder;
|
import org.opensearch.common.xcontent.XContentBuilder;
|
||||||
import org.opensearch.common.xcontent.XContentParser;
|
import org.opensearch.common.xcontent.XContentParser;
|
||||||
import org.opensearch.script.Script;
|
import org.opensearch.script.Script;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.support.ValueType;
|
import org.opensearch.search.aggregations.support.ValueType;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -54,6 +55,7 @@ public class CompositeValuesSourceParserHelper {
|
||||||
static <VB extends CompositeValuesSourceBuilder<VB>, T> void declareValuesSourceFields(AbstractObjectParser<VB, T> objectParser) {
|
static <VB extends CompositeValuesSourceBuilder<VB>, T> void declareValuesSourceFields(AbstractObjectParser<VB, T> objectParser) {
|
||||||
objectParser.declareField(VB::field, XContentParser::text, new ParseField("field"), ObjectParser.ValueType.STRING);
|
objectParser.declareField(VB::field, XContentParser::text, new ParseField("field"), ObjectParser.ValueType.STRING);
|
||||||
objectParser.declareBoolean(VB::missingBucket, new ParseField("missing_bucket"));
|
objectParser.declareBoolean(VB::missingBucket, new ParseField("missing_bucket"));
|
||||||
|
objectParser.declareString(VB::missingOrder, new ParseField(MissingOrder.NAME));
|
||||||
|
|
||||||
objectParser.declareField(VB::userValuetypeHint, p -> {
|
objectParser.declareField(VB::userValuetypeHint, p -> {
|
||||||
ValueType valueType = ValueType.lenientParse(p.text());
|
ValueType valueType = ValueType.lenientParse(p.text());
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval
|
||||||
import org.opensearch.search.aggregations.bucket.histogram.DateIntervalConsumer;
|
import org.opensearch.search.aggregations.bucket.histogram.DateIntervalConsumer;
|
||||||
import org.opensearch.search.aggregations.bucket.histogram.DateIntervalWrapper;
|
import org.opensearch.search.aggregations.bucket.histogram.DateIntervalWrapper;
|
||||||
import org.opensearch.search.aggregations.bucket.histogram.Histogram;
|
import org.opensearch.search.aggregations.bucket.histogram.Histogram;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSource;
|
import org.opensearch.search.aggregations.support.ValuesSource;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
||||||
|
@ -81,6 +82,7 @@ public class DateHistogramValuesSourceBuilder extends CompositeValuesSourceBuild
|
||||||
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
||||||
String format,
|
String format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
SortOrder order
|
SortOrder order
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -288,7 +290,7 @@ public class DateHistogramValuesSourceBuilder extends CompositeValuesSourceBuild
|
||||||
builder.register(
|
builder.register(
|
||||||
REGISTRY_KEY,
|
REGISTRY_KEY,
|
||||||
org.opensearch.common.collect.List.of(CoreValuesSourceType.DATE, CoreValuesSourceType.NUMERIC),
|
org.opensearch.common.collect.List.of(CoreValuesSourceType.DATE, CoreValuesSourceType.NUMERIC),
|
||||||
(valuesSourceConfig, rounding, name, hasScript, format, missingBucket, order) -> {
|
(valuesSourceConfig, rounding, name, hasScript, format, missingBucket, missingOrder, order) -> {
|
||||||
ValuesSource.Numeric numeric = (ValuesSource.Numeric) valuesSourceConfig.getValuesSource();
|
ValuesSource.Numeric numeric = (ValuesSource.Numeric) valuesSourceConfig.getValuesSource();
|
||||||
// TODO once composite is plugged in to the values source registry or at least understands Date values source types use it
|
// TODO once composite is plugged in to the values source registry or at least understands Date values source types use it
|
||||||
// here
|
// here
|
||||||
|
@ -304,6 +306,7 @@ public class DateHistogramValuesSourceBuilder extends CompositeValuesSourceBuild
|
||||||
docValueFormat,
|
docValueFormat,
|
||||||
order,
|
order,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
missingOrder,
|
||||||
hasScript,
|
hasScript,
|
||||||
(
|
(
|
||||||
BigArrays bigArrays,
|
BigArrays bigArrays,
|
||||||
|
@ -319,6 +322,7 @@ public class DateHistogramValuesSourceBuilder extends CompositeValuesSourceBuild
|
||||||
roundingValuesSource::round,
|
roundingValuesSource::round,
|
||||||
compositeValuesSourceConfig.format(),
|
compositeValuesSourceConfig.format(),
|
||||||
compositeValuesSourceConfig.missingBucket(),
|
compositeValuesSourceConfig.missingBucket(),
|
||||||
|
compositeValuesSourceConfig.missingOrder(),
|
||||||
size,
|
size,
|
||||||
compositeValuesSourceConfig.reverseMul()
|
compositeValuesSourceConfig.reverseMul()
|
||||||
);
|
);
|
||||||
|
@ -339,6 +343,6 @@ public class DateHistogramValuesSourceBuilder extends CompositeValuesSourceBuild
|
||||||
Rounding rounding = dateHistogramInterval.createRounding(timeZone(), offset);
|
Rounding rounding = dateHistogramInterval.createRounding(timeZone(), offset);
|
||||||
return queryShardContext.getValuesSourceRegistry()
|
return queryShardContext.getValuesSourceRegistry()
|
||||||
.getAggregator(REGISTRY_KEY, config)
|
.getAggregator(REGISTRY_KEY, config)
|
||||||
.apply(config, rounding, name, config.script() != null, format(), missingBucket(), order());
|
.apply(config, rounding, name, config.script() != null, format(), missingBucket(), missingOrder(), order());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.opensearch.index.fielddata.SortedNumericDoubleValues;
|
||||||
import org.opensearch.index.mapper.MappedFieldType;
|
import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.LeafBucketCollector;
|
import org.opensearch.search.aggregations.LeafBucketCollector;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -63,10 +64,11 @@ class DoubleValuesSource extends SingleDimensionValuesSource<Double> {
|
||||||
CheckedFunction<LeafReaderContext, SortedNumericDoubleValues, IOException> docValuesFunc,
|
CheckedFunction<LeafReaderContext, SortedNumericDoubleValues, IOException> docValuesFunc,
|
||||||
DocValueFormat format,
|
DocValueFormat format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
int size,
|
int size,
|
||||||
int reverseMul
|
int reverseMul
|
||||||
) {
|
) {
|
||||||
super(bigArrays, format, fieldType, missingBucket, size, reverseMul);
|
super(bigArrays, format, fieldType, missingBucket, missingOrder, size, reverseMul);
|
||||||
this.docValuesFunc = docValuesFunc;
|
this.docValuesFunc = docValuesFunc;
|
||||||
this.bits = missingBucket ? new BitArray(100, bigArrays) : null;
|
this.bits = missingBucket ? new BitArray(100, bigArrays) : null;
|
||||||
this.values = bigArrays.newDoubleArray(Math.min(size, 100), false);
|
this.values = bigArrays.newDoubleArray(Math.min(size, 100), false);
|
||||||
|
@ -89,10 +91,9 @@ class DoubleValuesSource extends SingleDimensionValuesSource<Double> {
|
||||||
@Override
|
@Override
|
||||||
int compare(int from, int to) {
|
int compare(int from, int to) {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (bits.get(from) == false) {
|
int result = missingOrder.compare(() -> bits.get(from) == false, () -> bits.get(to) == false, reverseMul);
|
||||||
return bits.get(to) ? -1 * reverseMul : 0;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (bits.get(to) == false) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(values.get(from), values.get(to));
|
return compareValues(values.get(from), values.get(to));
|
||||||
|
@ -101,10 +102,9 @@ class DoubleValuesSource extends SingleDimensionValuesSource<Double> {
|
||||||
@Override
|
@Override
|
||||||
int compareCurrent(int slot) {
|
int compareCurrent(int slot) {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (missingCurrentValue) {
|
int result = missingOrder.compare(() -> missingCurrentValue, () -> bits.get(slot) == false, reverseMul);
|
||||||
return bits.get(slot) ? -1 * reverseMul : 0;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (bits.get(slot) == false) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(currentValue, values.get(slot));
|
return compareValues(currentValue, values.get(slot));
|
||||||
|
@ -113,10 +113,9 @@ class DoubleValuesSource extends SingleDimensionValuesSource<Double> {
|
||||||
@Override
|
@Override
|
||||||
int compareCurrentWithAfter() {
|
int compareCurrentWithAfter() {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (missingCurrentValue) {
|
int result = missingOrder.compare(() -> missingCurrentValue, () -> afterValue == null, reverseMul);
|
||||||
return afterValue != null ? -1 * reverseMul : 0;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (afterValue == null) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(currentValue, afterValue);
|
return compareValues(currentValue, afterValue);
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.bucket.geogrid.CellIdSource;
|
import org.opensearch.search.aggregations.bucket.geogrid.CellIdSource;
|
||||||
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder;
|
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder;
|
||||||
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSource;
|
import org.opensearch.search.aggregations.support.ValuesSource;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
||||||
|
@ -72,6 +73,7 @@ public class GeoTileGridValuesSourceBuilder extends CompositeValuesSourceBuilder
|
||||||
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
||||||
String format,
|
String format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
SortOrder order
|
SortOrder order
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -103,7 +105,7 @@ public class GeoTileGridValuesSourceBuilder extends CompositeValuesSourceBuilder
|
||||||
builder.register(
|
builder.register(
|
||||||
REGISTRY_KEY,
|
REGISTRY_KEY,
|
||||||
CoreValuesSourceType.GEOPOINT,
|
CoreValuesSourceType.GEOPOINT,
|
||||||
(valuesSourceConfig, precision, boundingBox, name, hasScript, format, missingBucket, order) -> {
|
(valuesSourceConfig, precision, boundingBox, name, hasScript, format, missingBucket, missingOrder, order) -> {
|
||||||
ValuesSource.GeoPoint geoPoint = (ValuesSource.GeoPoint) valuesSourceConfig.getValuesSource();
|
ValuesSource.GeoPoint geoPoint = (ValuesSource.GeoPoint) valuesSourceConfig.getValuesSource();
|
||||||
// is specified in the builder.
|
// is specified in the builder.
|
||||||
final MappedFieldType fieldType = valuesSourceConfig.fieldType();
|
final MappedFieldType fieldType = valuesSourceConfig.fieldType();
|
||||||
|
@ -115,6 +117,7 @@ public class GeoTileGridValuesSourceBuilder extends CompositeValuesSourceBuilder
|
||||||
DocValueFormat.GEOTILE,
|
DocValueFormat.GEOTILE,
|
||||||
order,
|
order,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
missingOrder,
|
||||||
hasScript,
|
hasScript,
|
||||||
(
|
(
|
||||||
BigArrays bigArrays,
|
BigArrays bigArrays,
|
||||||
|
@ -132,6 +135,7 @@ public class GeoTileGridValuesSourceBuilder extends CompositeValuesSourceBuilder
|
||||||
LongUnaryOperator.identity(),
|
LongUnaryOperator.identity(),
|
||||||
compositeValuesSourceConfig.format(),
|
compositeValuesSourceConfig.format(),
|
||||||
compositeValuesSourceConfig.missingBucket(),
|
compositeValuesSourceConfig.missingBucket(),
|
||||||
|
compositeValuesSourceConfig.missingOrder(),
|
||||||
size,
|
size,
|
||||||
compositeValuesSourceConfig.reverseMul()
|
compositeValuesSourceConfig.reverseMul()
|
||||||
);
|
);
|
||||||
|
@ -220,7 +224,7 @@ public class GeoTileGridValuesSourceBuilder extends CompositeValuesSourceBuilder
|
||||||
protected CompositeValuesSourceConfig innerBuild(QueryShardContext queryShardContext, ValuesSourceConfig config) throws IOException {
|
protected CompositeValuesSourceConfig innerBuild(QueryShardContext queryShardContext, ValuesSourceConfig config) throws IOException {
|
||||||
return queryShardContext.getValuesSourceRegistry()
|
return queryShardContext.getValuesSourceRegistry()
|
||||||
.getAggregator(REGISTRY_KEY, config)
|
.getAggregator(REGISTRY_KEY, config)
|
||||||
.apply(config, precision, geoBoundingBox(), name, script() != null, format(), missingBucket(), order());
|
.apply(config, precision, geoBoundingBox(), name, script() != null, format(), missingBucket(), missingOrder(), order());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.opensearch.common.util.BigArrays;
|
||||||
import org.opensearch.index.mapper.MappedFieldType;
|
import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.function.LongUnaryOperator;
|
import java.util.function.LongUnaryOperator;
|
||||||
|
@ -57,10 +58,11 @@ class GeoTileValuesSource extends LongValuesSource {
|
||||||
LongUnaryOperator rounding,
|
LongUnaryOperator rounding,
|
||||||
DocValueFormat format,
|
DocValueFormat format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
int size,
|
int size,
|
||||||
int reverseMul
|
int reverseMul
|
||||||
) {
|
) {
|
||||||
super(bigArrays, fieldType, docValuesFunc, rounding, format, missingBucket, size, reverseMul);
|
super(bigArrays, fieldType, docValuesFunc, rounding, format, missingBucket, missingOrder, size, reverseMul);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.index.mapper.StringFieldType;
|
import org.opensearch.index.mapper.StringFieldType;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.LeafBucketCollector;
|
import org.opensearch.search.aggregations.LeafBucketCollector;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -71,10 +72,11 @@ class GlobalOrdinalValuesSource extends SingleDimensionValuesSource<BytesRef> {
|
||||||
CheckedFunction<LeafReaderContext, SortedSetDocValues, IOException> docValuesFunc,
|
CheckedFunction<LeafReaderContext, SortedSetDocValues, IOException> docValuesFunc,
|
||||||
DocValueFormat format,
|
DocValueFormat format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
int size,
|
int size,
|
||||||
int reverseMul
|
int reverseMul
|
||||||
) {
|
) {
|
||||||
super(bigArrays, format, type, missingBucket, size, reverseMul);
|
super(bigArrays, format, type, missingBucket, missingOrder, size, reverseMul);
|
||||||
this.docValuesFunc = docValuesFunc;
|
this.docValuesFunc = docValuesFunc;
|
||||||
this.values = bigArrays.newLongArray(Math.min(size, 100), false);
|
this.values = bigArrays.newLongArray(Math.min(size, 100), false);
|
||||||
}
|
}
|
||||||
|
@ -87,16 +89,34 @@ class GlobalOrdinalValuesSource extends SingleDimensionValuesSource<BytesRef> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
int compare(int from, int to) {
|
int compare(int from, int to) {
|
||||||
|
if (missingBucket) {
|
||||||
|
int result = missingOrder.compare(() -> values.get(from) == -1, () -> values.get(to) == -1, reverseMul);
|
||||||
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
return Long.compare(values.get(from), values.get(to)) * reverseMul;
|
return Long.compare(values.get(from), values.get(to)) * reverseMul;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
int compareCurrent(int slot) {
|
int compareCurrent(int slot) {
|
||||||
|
if (missingBucket) {
|
||||||
|
int result = missingOrder.compare(() -> currentValue == -1, () -> values.get(slot) == -1, reverseMul);
|
||||||
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
return Long.compare(currentValue, values.get(slot)) * reverseMul;
|
return Long.compare(currentValue, values.get(slot)) * reverseMul;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
int compareCurrentWithAfter() {
|
int compareCurrentWithAfter() {
|
||||||
|
if (missingBucket) {
|
||||||
|
int result = missingOrder.compare(() -> currentValue == -1, () -> afterValueGlobalOrd == -1, reverseMul);
|
||||||
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
int cmp = Long.compare(currentValue, afterValueGlobalOrd);
|
int cmp = Long.compare(currentValue, afterValueGlobalOrd);
|
||||||
if (cmp == 0 && isTopValueInsertionPoint) {
|
if (cmp == 0 && isTopValueInsertionPoint) {
|
||||||
// the top value is missing in this shard, the comparison is against
|
// the top value is missing in this shard, the comparison is against
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.opensearch.common.xcontent.XContentParser;
|
||||||
import org.opensearch.index.mapper.MappedFieldType;
|
import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.index.query.QueryShardContext;
|
import org.opensearch.index.query.QueryShardContext;
|
||||||
import org.opensearch.search.aggregations.bucket.histogram.Histogram;
|
import org.opensearch.search.aggregations.bucket.histogram.Histogram;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSource;
|
import org.opensearch.search.aggregations.support.ValuesSource;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
||||||
|
@ -67,6 +68,7 @@ public class HistogramValuesSourceBuilder extends CompositeValuesSourceBuilder<H
|
||||||
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
||||||
String format,
|
String format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
SortOrder order
|
SortOrder order
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -92,7 +94,7 @@ public class HistogramValuesSourceBuilder extends CompositeValuesSourceBuilder<H
|
||||||
builder.register(
|
builder.register(
|
||||||
REGISTRY_KEY,
|
REGISTRY_KEY,
|
||||||
org.opensearch.common.collect.List.of(CoreValuesSourceType.DATE, CoreValuesSourceType.NUMERIC),
|
org.opensearch.common.collect.List.of(CoreValuesSourceType.DATE, CoreValuesSourceType.NUMERIC),
|
||||||
(valuesSourceConfig, interval, name, hasScript, format, missingBucket, order) -> {
|
(valuesSourceConfig, interval, name, hasScript, format, missingBucket, missingOrder, order) -> {
|
||||||
ValuesSource.Numeric numeric = (ValuesSource.Numeric) valuesSourceConfig.getValuesSource();
|
ValuesSource.Numeric numeric = (ValuesSource.Numeric) valuesSourceConfig.getValuesSource();
|
||||||
final HistogramValuesSource vs = new HistogramValuesSource(numeric, interval);
|
final HistogramValuesSource vs = new HistogramValuesSource(numeric, interval);
|
||||||
final MappedFieldType fieldType = valuesSourceConfig.fieldType();
|
final MappedFieldType fieldType = valuesSourceConfig.fieldType();
|
||||||
|
@ -103,6 +105,7 @@ public class HistogramValuesSourceBuilder extends CompositeValuesSourceBuilder<H
|
||||||
valuesSourceConfig.format(),
|
valuesSourceConfig.format(),
|
||||||
order,
|
order,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
missingOrder,
|
||||||
hasScript,
|
hasScript,
|
||||||
(
|
(
|
||||||
BigArrays bigArrays,
|
BigArrays bigArrays,
|
||||||
|
@ -117,6 +120,7 @@ public class HistogramValuesSourceBuilder extends CompositeValuesSourceBuilder<H
|
||||||
numericValuesSource::doubleValues,
|
numericValuesSource::doubleValues,
|
||||||
compositeValuesSourceConfig.format(),
|
compositeValuesSourceConfig.format(),
|
||||||
compositeValuesSourceConfig.missingBucket(),
|
compositeValuesSourceConfig.missingBucket(),
|
||||||
|
compositeValuesSourceConfig.missingOrder(),
|
||||||
size,
|
size,
|
||||||
compositeValuesSourceConfig.reverseMul()
|
compositeValuesSourceConfig.reverseMul()
|
||||||
);
|
);
|
||||||
|
@ -194,6 +198,6 @@ public class HistogramValuesSourceBuilder extends CompositeValuesSourceBuilder<H
|
||||||
protected CompositeValuesSourceConfig innerBuild(QueryShardContext queryShardContext, ValuesSourceConfig config) throws IOException {
|
protected CompositeValuesSourceConfig innerBuild(QueryShardContext queryShardContext, ValuesSourceConfig config) throws IOException {
|
||||||
return queryShardContext.getValuesSourceRegistry()
|
return queryShardContext.getValuesSourceRegistry()
|
||||||
.getAggregator(REGISTRY_KEY, config)
|
.getAggregator(REGISTRY_KEY, config)
|
||||||
.apply(config, interval, name, script() != null, format(), missingBucket(), order());
|
.apply(config, interval, name, script() != null, format(), missingBucket(), missingOrder(), order());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,10 @@ import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.index.mapper.NumberFieldMapper;
|
import org.opensearch.index.mapper.NumberFieldMapper;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.LeafBucketCollector;
|
import org.opensearch.search.aggregations.LeafBucketCollector;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.LongUnaryOperator;
|
import java.util.function.LongUnaryOperator;
|
||||||
import java.util.function.ToLongFunction;
|
import java.util.function.ToLongFunction;
|
||||||
|
|
||||||
|
@ -79,10 +81,11 @@ class LongValuesSource extends SingleDimensionValuesSource<Long> {
|
||||||
LongUnaryOperator rounding,
|
LongUnaryOperator rounding,
|
||||||
DocValueFormat format,
|
DocValueFormat format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
int size,
|
int size,
|
||||||
int reverseMul
|
int reverseMul
|
||||||
) {
|
) {
|
||||||
super(bigArrays, format, fieldType, missingBucket, size, reverseMul);
|
super(bigArrays, format, fieldType, missingBucket, missingOrder, size, reverseMul);
|
||||||
this.bigArrays = bigArrays;
|
this.bigArrays = bigArrays;
|
||||||
this.docValuesFunc = docValuesFunc;
|
this.docValuesFunc = docValuesFunc;
|
||||||
this.rounding = rounding;
|
this.rounding = rounding;
|
||||||
|
@ -107,10 +110,9 @@ class LongValuesSource extends SingleDimensionValuesSource<Long> {
|
||||||
@Override
|
@Override
|
||||||
int compare(int from, int to) {
|
int compare(int from, int to) {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (bits.get(from) == false) {
|
int result = missingOrder.compare(() -> bits.get(from) == false, () -> bits.get(to) == false, reverseMul);
|
||||||
return bits.get(to) ? -1 * reverseMul : 0;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (bits.get(to) == false) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(values.get(from), values.get(to));
|
return compareValues(values.get(from), values.get(to));
|
||||||
|
@ -119,10 +121,9 @@ class LongValuesSource extends SingleDimensionValuesSource<Long> {
|
||||||
@Override
|
@Override
|
||||||
int compareCurrent(int slot) {
|
int compareCurrent(int slot) {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (missingCurrentValue) {
|
int result = missingOrder.compare(() -> missingCurrentValue, () -> bits.get(slot) == false, reverseMul);
|
||||||
return bits.get(slot) ? -1 * reverseMul : 0;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (bits.get(slot) == false) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(currentValue, values.get(slot));
|
return compareValues(currentValue, values.get(slot));
|
||||||
|
@ -131,10 +132,9 @@ class LongValuesSource extends SingleDimensionValuesSource<Long> {
|
||||||
@Override
|
@Override
|
||||||
int compareCurrentWithAfter() {
|
int compareCurrentWithAfter() {
|
||||||
if (missingBucket) {
|
if (missingBucket) {
|
||||||
if (missingCurrentValue) {
|
int result = missingOrder.compare(() -> missingCurrentValue, () -> Objects.isNull(afterValue), reverseMul);
|
||||||
return afterValue != null ? -1 * reverseMul : 0;
|
if (MissingOrder.unknownOrder(result) == false) {
|
||||||
} else if (afterValue == null) {
|
return result;
|
||||||
return reverseMul;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return compareValues(currentValue, afterValue);
|
return compareValues(currentValue, afterValue);
|
||||||
|
|
|
@ -41,10 +41,13 @@ import org.opensearch.common.util.BigArrays;
|
||||||
import org.opensearch.index.mapper.MappedFieldType;
|
import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.LeafBucketCollector;
|
import org.opensearch.search.aggregations.LeafBucketCollector;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.sort.SortOrder;
|
import org.opensearch.search.sort.SortOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.opensearch.search.aggregations.bucket.missing.MissingOrder.LAST;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A source that can record and compare values of similar type.
|
* A source that can record and compare values of similar type.
|
||||||
*/
|
*/
|
||||||
|
@ -54,6 +57,7 @@ abstract class SingleDimensionValuesSource<T extends Comparable<T>> implements R
|
||||||
@Nullable
|
@Nullable
|
||||||
protected final MappedFieldType fieldType;
|
protected final MappedFieldType fieldType;
|
||||||
protected final boolean missingBucket;
|
protected final boolean missingBucket;
|
||||||
|
protected final MissingOrder missingOrder;
|
||||||
|
|
||||||
protected final int size;
|
protected final int size;
|
||||||
protected final int reverseMul;
|
protected final int reverseMul;
|
||||||
|
@ -67,6 +71,7 @@ abstract class SingleDimensionValuesSource<T extends Comparable<T>> implements R
|
||||||
* @param format The format of the source.
|
* @param format The format of the source.
|
||||||
* @param fieldType The field type or null if the source is a script.
|
* @param fieldType The field type or null if the source is a script.
|
||||||
* @param missingBucket If true, an explicit `null bucket represents documents with missing values.
|
* @param missingBucket If true, an explicit `null bucket represents documents with missing values.
|
||||||
|
* @param missingOrder The `null bucket's position.
|
||||||
* @param size The number of values to record.
|
* @param size The number of values to record.
|
||||||
* @param reverseMul -1 if the natural order ({@link SortOrder#ASC} should be reversed.
|
* @param reverseMul -1 if the natural order ({@link SortOrder#ASC} should be reversed.
|
||||||
*/
|
*/
|
||||||
|
@ -75,6 +80,7 @@ abstract class SingleDimensionValuesSource<T extends Comparable<T>> implements R
|
||||||
DocValueFormat format,
|
DocValueFormat format,
|
||||||
@Nullable MappedFieldType fieldType,
|
@Nullable MappedFieldType fieldType,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
int size,
|
int size,
|
||||||
int reverseMul
|
int reverseMul
|
||||||
) {
|
) {
|
||||||
|
@ -82,6 +88,7 @@ abstract class SingleDimensionValuesSource<T extends Comparable<T>> implements R
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.fieldType = fieldType;
|
this.fieldType = fieldType;
|
||||||
this.missingBucket = missingBucket;
|
this.missingBucket = missingBucket;
|
||||||
|
this.missingOrder = missingOrder;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.reverseMul = reverseMul;
|
this.reverseMul = reverseMul;
|
||||||
this.afterValue = null;
|
this.afterValue = null;
|
||||||
|
@ -167,8 +174,11 @@ abstract class SingleDimensionValuesSource<T extends Comparable<T>> implements R
|
||||||
* Returns true if a {@link SortedDocsProducer} should be used to optimize the execution.
|
* Returns true if a {@link SortedDocsProducer} should be used to optimize the execution.
|
||||||
*/
|
*/
|
||||||
protected boolean checkIfSortedDocsIsApplicable(IndexReader reader, MappedFieldType fieldType) {
|
protected boolean checkIfSortedDocsIsApplicable(IndexReader reader, MappedFieldType fieldType) {
|
||||||
if (fieldType == null || (missingBucket && afterValue == null) || fieldType.isSearchable() == false ||
|
if (fieldType == null
|
||||||
// inverse of the natural order
|
|| (missingBucket && (afterValue == null || reverseMul == 1 && missingOrder == LAST))
|
||||||
|
|| fieldType.isSearchable() == false
|
||||||
|
||
|
||||||
|
// inverse of the natural order
|
||||||
reverseMul == -1) {
|
reverseMul == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.opensearch.common.xcontent.XContentParser;
|
||||||
import org.opensearch.index.query.QueryShardContext;
|
import org.opensearch.index.query.QueryShardContext;
|
||||||
import org.opensearch.script.Script;
|
import org.opensearch.script.Script;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSource;
|
import org.opensearch.search.aggregations.support.ValuesSource;
|
||||||
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
import org.opensearch.search.aggregations.support.ValuesSourceConfig;
|
||||||
|
@ -68,6 +69,7 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
boolean hasScript, // probably redundant with the config, but currently we check this two different ways...
|
||||||
String format,
|
String format,
|
||||||
boolean missingBucket,
|
boolean missingBucket,
|
||||||
|
MissingOrder missingOrder,
|
||||||
SortOrder order
|
SortOrder order
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +113,7 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
builder.register(
|
builder.register(
|
||||||
REGISTRY_KEY,
|
REGISTRY_KEY,
|
||||||
org.opensearch.common.collect.List.of(CoreValuesSourceType.DATE, CoreValuesSourceType.NUMERIC, CoreValuesSourceType.BOOLEAN),
|
org.opensearch.common.collect.List.of(CoreValuesSourceType.DATE, CoreValuesSourceType.NUMERIC, CoreValuesSourceType.BOOLEAN),
|
||||||
(valuesSourceConfig, name, hasScript, format, missingBucket, order) -> {
|
(valuesSourceConfig, name, hasScript, format, missingBucket, missingOrder, order) -> {
|
||||||
final DocValueFormat docValueFormat;
|
final DocValueFormat docValueFormat;
|
||||||
if (format == null && valuesSourceConfig.valueSourceType() == CoreValuesSourceType.DATE) {
|
if (format == null && valuesSourceConfig.valueSourceType() == CoreValuesSourceType.DATE) {
|
||||||
// defaults to the raw format on date fields (preserve timestamp as longs).
|
// defaults to the raw format on date fields (preserve timestamp as longs).
|
||||||
|
@ -126,6 +128,7 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
docValueFormat,
|
docValueFormat,
|
||||||
order,
|
order,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
missingOrder,
|
||||||
hasScript,
|
hasScript,
|
||||||
(
|
(
|
||||||
BigArrays bigArrays,
|
BigArrays bigArrays,
|
||||||
|
@ -142,6 +145,7 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
vs::doubleValues,
|
vs::doubleValues,
|
||||||
compositeValuesSourceConfig.format(),
|
compositeValuesSourceConfig.format(),
|
||||||
compositeValuesSourceConfig.missingBucket(),
|
compositeValuesSourceConfig.missingBucket(),
|
||||||
|
compositeValuesSourceConfig.missingOrder(),
|
||||||
size,
|
size,
|
||||||
compositeValuesSourceConfig.reverseMul()
|
compositeValuesSourceConfig.reverseMul()
|
||||||
);
|
);
|
||||||
|
@ -156,6 +160,7 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
rounding,
|
rounding,
|
||||||
compositeValuesSourceConfig.format(),
|
compositeValuesSourceConfig.format(),
|
||||||
compositeValuesSourceConfig.missingBucket(),
|
compositeValuesSourceConfig.missingBucket(),
|
||||||
|
compositeValuesSourceConfig.missingOrder(),
|
||||||
size,
|
size,
|
||||||
compositeValuesSourceConfig.reverseMul()
|
compositeValuesSourceConfig.reverseMul()
|
||||||
);
|
);
|
||||||
|
@ -170,13 +175,14 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
builder.register(
|
builder.register(
|
||||||
REGISTRY_KEY,
|
REGISTRY_KEY,
|
||||||
org.opensearch.common.collect.List.of(CoreValuesSourceType.BYTES, CoreValuesSourceType.IP),
|
org.opensearch.common.collect.List.of(CoreValuesSourceType.BYTES, CoreValuesSourceType.IP),
|
||||||
(valuesSourceConfig, name, hasScript, format, missingBucket, order) -> new CompositeValuesSourceConfig(
|
(valuesSourceConfig, name, hasScript, format, missingBucket, missingOrder, order) -> new CompositeValuesSourceConfig(
|
||||||
name,
|
name,
|
||||||
valuesSourceConfig.fieldType(),
|
valuesSourceConfig.fieldType(),
|
||||||
valuesSourceConfig.getValuesSource(),
|
valuesSourceConfig.getValuesSource(),
|
||||||
valuesSourceConfig.format(),
|
valuesSourceConfig.format(),
|
||||||
order,
|
order,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
missingOrder,
|
||||||
hasScript,
|
hasScript,
|
||||||
(
|
(
|
||||||
BigArrays bigArrays,
|
BigArrays bigArrays,
|
||||||
|
@ -193,6 +199,7 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
vs::globalOrdinalsValues,
|
vs::globalOrdinalsValues,
|
||||||
compositeValuesSourceConfig.format(),
|
compositeValuesSourceConfig.format(),
|
||||||
compositeValuesSourceConfig.missingBucket(),
|
compositeValuesSourceConfig.missingBucket(),
|
||||||
|
compositeValuesSourceConfig.missingOrder(),
|
||||||
size,
|
size,
|
||||||
compositeValuesSourceConfig.reverseMul()
|
compositeValuesSourceConfig.reverseMul()
|
||||||
);
|
);
|
||||||
|
@ -205,6 +212,7 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
vs::bytesValues,
|
vs::bytesValues,
|
||||||
compositeValuesSourceConfig.format(),
|
compositeValuesSourceConfig.format(),
|
||||||
compositeValuesSourceConfig.missingBucket(),
|
compositeValuesSourceConfig.missingBucket(),
|
||||||
|
compositeValuesSourceConfig.missingOrder(),
|
||||||
size,
|
size,
|
||||||
compositeValuesSourceConfig.reverseMul()
|
compositeValuesSourceConfig.reverseMul()
|
||||||
);
|
);
|
||||||
|
@ -224,6 +232,6 @@ public class TermsValuesSourceBuilder extends CompositeValuesSourceBuilder<Terms
|
||||||
protected CompositeValuesSourceConfig innerBuild(QueryShardContext queryShardContext, ValuesSourceConfig config) throws IOException {
|
protected CompositeValuesSourceConfig innerBuild(QueryShardContext queryShardContext, ValuesSourceConfig config) throws IOException {
|
||||||
return queryShardContext.getValuesSourceRegistry()
|
return queryShardContext.getValuesSourceRegistry()
|
||||||
.getAggregator(REGISTRY_KEY, config)
|
.getAggregator(REGISTRY_KEY, config)
|
||||||
.apply(config, name, script() != null, format(), missingBucket(), order());
|
.apply(config, name, script() != null, format(), missingBucket(), missingOrder(), order());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* The OpenSearch Contributors require contributions made to
|
||||||
|
* this file be licensed under the Apache-2.0 license or a
|
||||||
|
* compatible open source license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.opensearch.search.aggregations.bucket.missing;
|
||||||
|
|
||||||
|
import org.opensearch.common.inject.Provider;
|
||||||
|
import org.opensearch.common.io.stream.StreamInput;
|
||||||
|
import org.opensearch.common.io.stream.StreamOutput;
|
||||||
|
import org.opensearch.common.io.stream.Writeable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composite Aggregation Missing bucket order.
|
||||||
|
*/
|
||||||
|
public enum MissingOrder implements Writeable {
|
||||||
|
/**
|
||||||
|
* missing first.
|
||||||
|
*/
|
||||||
|
FIRST {
|
||||||
|
@Override
|
||||||
|
public int compare(Provider<Boolean> leftIsMissing, Provider<Boolean> rightIsMissing, int reverseMul) {
|
||||||
|
if (leftIsMissing.get()) {
|
||||||
|
return rightIsMissing.get() ? 0 : -1;
|
||||||
|
} else if (rightIsMissing.get()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return MISSING_ORDER_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "first";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* missing last.
|
||||||
|
*/
|
||||||
|
LAST {
|
||||||
|
@Override
|
||||||
|
public int compare(Provider<Boolean> leftIsMissing, Provider<Boolean> rightIsMissing, int reverseMul) {
|
||||||
|
if (leftIsMissing.get()) {
|
||||||
|
return rightIsMissing.get() ? 0 : 1;
|
||||||
|
} else if (rightIsMissing.get()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return MISSING_ORDER_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "last";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default: ASC missing first / DESC missing last
|
||||||
|
*/
|
||||||
|
DEFAULT {
|
||||||
|
@Override
|
||||||
|
public int compare(Provider<Boolean> leftIsMissing, Provider<Boolean> rightIsMissing, int reverseMul) {
|
||||||
|
if (leftIsMissing.get()) {
|
||||||
|
return rightIsMissing.get() ? 0 : -1 * reverseMul;
|
||||||
|
} else if (rightIsMissing.get()) {
|
||||||
|
return reverseMul;
|
||||||
|
}
|
||||||
|
return MISSING_ORDER_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final String NAME = "missing_order";
|
||||||
|
|
||||||
|
private static int MISSING_ORDER_UNKNOWN = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
public static MissingOrder readFromStream(StreamInput in) throws IOException {
|
||||||
|
return in.readEnum(MissingOrder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
out.writeEnum(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDefault(MissingOrder order) {
|
||||||
|
return order == DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MissingOrder fromString(String order) {
|
||||||
|
return valueOf(order.toUpperCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean unknownOrder(int v) {
|
||||||
|
return v == MISSING_ORDER_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract int compare(Provider<Boolean> leftIsMissing, Provider<Boolean> rightIsMissing, int reverseMul);
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import org.opensearch.script.Script;
|
||||||
import org.opensearch.search.aggregations.BaseAggregationTestCase;
|
import org.opensearch.search.aggregations.BaseAggregationTestCase;
|
||||||
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
||||||
import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.sort.SortOrder;
|
import org.opensearch.search.sort.SortOrder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -69,6 +70,7 @@ public class CompositeAggregationBuilderTests extends BaseAggregationTestCase<Co
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
histo.missingBucket(true);
|
histo.missingBucket(true);
|
||||||
}
|
}
|
||||||
|
histo.missingOrder(randomFrom(MissingOrder.values()));
|
||||||
return histo;
|
return histo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +96,7 @@ public class CompositeAggregationBuilderTests extends BaseAggregationTestCase<Co
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
terms.missingBucket(true);
|
terms.missingBucket(true);
|
||||||
}
|
}
|
||||||
|
terms.missingOrder(randomFrom(MissingOrder.values()));
|
||||||
return terms;
|
return terms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +111,7 @@ public class CompositeAggregationBuilderTests extends BaseAggregationTestCase<Co
|
||||||
histo.missingBucket(true);
|
histo.missingBucket(true);
|
||||||
}
|
}
|
||||||
histo.interval(randomDoubleBetween(Math.nextUp(0), Double.MAX_VALUE, false));
|
histo.interval(randomDoubleBetween(Math.nextUp(0), Double.MAX_VALUE, false));
|
||||||
|
histo.missingOrder(randomFrom(MissingOrder.values()));
|
||||||
return histo;
|
return histo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ import org.opensearch.search.aggregations.AggregatorTestCase;
|
||||||
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder;
|
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder;
|
||||||
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils;
|
||||||
import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.search.aggregations.bucket.terms.StringTerms;
|
import org.opensearch.search.aggregations.bucket.terms.StringTerms;
|
||||||
import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
|
||||||
import org.opensearch.search.aggregations.metrics.InternalMax;
|
import org.opensearch.search.aggregations.metrics.InternalMax;
|
||||||
|
@ -586,6 +587,84 @@ public class CompositeAggregatorTests extends AggregatorTestCase {
|
||||||
assertEquals(0, result.getBuckets().size());
|
assertEquals(0, result.getBuckets().size());
|
||||||
assertNull(result.afterKey());
|
assertNull(result.afterKey());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// sort ascending, null bucket is first, same as default.
|
||||||
|
testSearchCase(Arrays.asList(new MatchAllDocsQuery()), dataset, () -> {
|
||||||
|
TermsValuesSourceBuilder terms = new TermsValuesSourceBuilder("keyword").field("keyword")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.FIRST);
|
||||||
|
return new CompositeAggregationBuilder("name", Collections.singletonList(terms));
|
||||||
|
}, (result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=d}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=null}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{keyword=a}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{keyword=c}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{keyword=d}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(3).getDocCount());
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort ascending, null bucket is last.
|
||||||
|
testSearchCase(Arrays.asList(new MatchAllDocsQuery()), dataset, () -> {
|
||||||
|
TermsValuesSourceBuilder terms = new TermsValuesSourceBuilder("keyword").field("keyword")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.LAST);
|
||||||
|
return new CompositeAggregationBuilder("name", Collections.singletonList(terms));
|
||||||
|
}, (result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=a}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{keyword=c}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{keyword=d}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{keyword=null}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(3).getDocCount());
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort descending, null bucket is last, same as default
|
||||||
|
testSearchCase(Arrays.asList(new MatchAllDocsQuery()), dataset, () -> {
|
||||||
|
TermsValuesSourceBuilder terms = new TermsValuesSourceBuilder("keyword").field("keyword")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.LAST)
|
||||||
|
.order(SortOrder.DESC);
|
||||||
|
return new CompositeAggregationBuilder("name", Collections.singletonList(terms));
|
||||||
|
}, (result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=null}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(3).getDocCount());
|
||||||
|
assertEquals("{keyword=a}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{keyword=c}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{keyword=d}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort descending, null bucket is first
|
||||||
|
testSearchCase(Arrays.asList(new MatchAllDocsQuery()), dataset, () -> {
|
||||||
|
TermsValuesSourceBuilder terms = new TermsValuesSourceBuilder("keyword").field("keyword")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.FIRST)
|
||||||
|
.order(SortOrder.DESC);
|
||||||
|
return new CompositeAggregationBuilder("name", Collections.singletonList(terms));
|
||||||
|
}, (result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=a}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=null}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{keyword=d}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{keyword=c}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{keyword=a}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(3).getDocCount());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWithKeywordMissingAfter() throws Exception {
|
public void testWithKeywordMissingAfter() throws Exception {
|
||||||
|
@ -901,14 +980,14 @@ public class CompositeAggregatorTests extends AggregatorTestCase {
|
||||||
final List<Map<String, List<Object>>> dataset = new ArrayList<>();
|
final List<Map<String, List<Object>>> dataset = new ArrayList<>();
|
||||||
dataset.addAll(
|
dataset.addAll(
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
createDocument("keyword", "a", "long", 100L),
|
createDocument("double", 0d, "keyword", "a", "long", 100L),
|
||||||
createDocument("double", 0d),
|
createDocument("double", 0d),
|
||||||
createDocument("keyword", "c", "long", 100L),
|
createDocument("double", 0d, "keyword", "c", "long", 100L),
|
||||||
createDocument("keyword", "a", "long", 0L),
|
createDocument("double", 0d, "keyword", "a", "long", 0L),
|
||||||
createDocument("keyword", "d", "long", 10L),
|
createDocument("double", 0d, "keyword", "d", "long", 10L),
|
||||||
createDocument("keyword", "c"),
|
createDocument("double", 0d, "keyword", "c"),
|
||||||
createDocument("keyword", "c", "long", 100L),
|
createDocument("double", 0d, "keyword", "c", "long", 100L),
|
||||||
createDocument("long", 100L),
|
createDocument("double", 0d, "long", 100L),
|
||||||
createDocument("double", 0d)
|
createDocument("double", 0d)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -961,6 +1040,112 @@ public class CompositeAggregatorTests extends AggregatorTestCase {
|
||||||
assertEquals(1L, result.getBuckets().get(1).getDocCount());
|
assertEquals(1L, result.getBuckets().get(1).getDocCount());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// keyword null bucket is last, long null bucket is last
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("double")),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new TermsValuesSourceBuilder("keyword").field("keyword").missingBucket(true).missingOrder(MissingOrder.LAST),
|
||||||
|
new TermsValuesSourceBuilder("long").field("long").missingBucket(true).missingOrder(MissingOrder.LAST)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(7, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=null, long=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=null, long=null}", result.getBuckets().get(6).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(6).getDocCount());
|
||||||
|
assertEquals("{keyword=null, long=100}", result.getBuckets().get(5).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(5).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// keyword null bucket is last, long null bucket is first
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("double")),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new TermsValuesSourceBuilder("keyword").field("keyword").missingBucket(true).missingOrder(MissingOrder.LAST),
|
||||||
|
new TermsValuesSourceBuilder("long").field("long").missingBucket(true).missingOrder(MissingOrder.FIRST)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(7, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=null, long=100}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=null, long=100}", result.getBuckets().get(6).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(6).getDocCount());
|
||||||
|
assertEquals("{keyword=null, long=null}", result.getBuckets().get(5).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(5).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// asc, null bucket is last, search after non null value
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("double")),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(new TermsValuesSourceBuilder("keyword").field("keyword").missingBucket(true).missingOrder(MissingOrder.LAST))
|
||||||
|
).aggregateAfter(createAfterKey("keyword", "c")),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(2, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=d}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{keyword=null}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(3L, result.getBuckets().get(1).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// desc, null bucket is last, search after non null value
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("double")),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new TermsValuesSourceBuilder("keyword").field("keyword")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.LAST)
|
||||||
|
.order(SortOrder.DESC)
|
||||||
|
)
|
||||||
|
).aggregateAfter(createAfterKey("keyword", "c")),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(2, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=a}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{keyword=null}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(3L, result.getBuckets().get(1).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// keyword null bucket is last, long null bucket is last
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("double")),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new TermsValuesSourceBuilder("keyword").field("keyword").missingBucket(true).missingOrder(MissingOrder.LAST),
|
||||||
|
new TermsValuesSourceBuilder("long").field("long").missingBucket(true).missingOrder(MissingOrder.LAST)
|
||||||
|
)
|
||||||
|
).aggregateAfter(createAfterKey("keyword", "c", "long", null)),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(3, result.getBuckets().size());
|
||||||
|
assertEquals("{keyword=null, long=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{keyword=d, long=10}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{keyword=null, long=100}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{keyword=null, long=null}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(2).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiValuedWithKeywordAndLong() throws Exception {
|
public void testMultiValuedWithKeywordAndLong() throws Exception {
|
||||||
|
@ -1719,6 +1904,240 @@ public class CompositeAggregatorTests extends AggregatorTestCase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWithHistogramBucketMissing() throws IOException {
|
||||||
|
final List<Map<String, List<Object>>> dataset = new ArrayList<>();
|
||||||
|
dataset.addAll(
|
||||||
|
Arrays.asList(
|
||||||
|
createDocument("price", 50L, "long", 1L),
|
||||||
|
createDocument("price", 60L, "long", 2L),
|
||||||
|
createDocument("price", 70L, "long", 3L),
|
||||||
|
createDocument("price", 62L, "long", 4L),
|
||||||
|
createDocument("long", 5L)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// asc, null bucket is first
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.FIRST)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{price=70.0}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=null}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{price=50.0}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{price=60.0}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{price=70.0}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(3).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// asc, null bucket is first, after 50.0
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.FIRST)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
).aggregateAfter(createAfterKey("price", 60.0d)),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(1, result.getBuckets().size());
|
||||||
|
assertEquals("{price=70.0}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=70.0}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// asc, null bucket is first, after null
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.FIRST)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
).aggregateAfter(createAfterKey("price", null)),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(3, result.getBuckets().size());
|
||||||
|
assertEquals("{price=70.0}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=50.0}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{price=60.0}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{price=70.0}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(2).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// asc, null bucket is last
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.LAST)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{price=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=50.0}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{price=60.0}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{price=70.0}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{price=null}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(3).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// asc, null bucket is last, after 70.0
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.LAST)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
).aggregateAfter(createAfterKey("price", 70.0)),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(1, result.getBuckets().size());
|
||||||
|
assertEquals("{price=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=null}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// desc, null bucket is first
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.FIRST)
|
||||||
|
.order(SortOrder.DESC)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{price=50.0}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=null}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{price=70.0}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{price=60.0}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{price=50.0}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(3).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// desc, null bucket is first, after 60.0
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.FIRST)
|
||||||
|
.order(SortOrder.DESC)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
).aggregateAfter(createAfterKey("price", 60.0)),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(1, result.getBuckets().size());
|
||||||
|
assertEquals("{price=50.0}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=50.0}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// desc, null bucket is last
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.LAST)
|
||||||
|
.order(SortOrder.DESC)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(4, result.getBuckets().size());
|
||||||
|
assertEquals("{price=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=70.0}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
assertEquals("{price=60.0}", result.getBuckets().get(1).getKeyAsString());
|
||||||
|
assertEquals(2L, result.getBuckets().get(1).getDocCount());
|
||||||
|
assertEquals("{price=50.0}", result.getBuckets().get(2).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(2).getDocCount());
|
||||||
|
assertEquals("{price=null}", result.getBuckets().get(3).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(3).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// desc, null bucket is last, after 50.0
|
||||||
|
testSearchCase(
|
||||||
|
Arrays.asList(new MatchAllDocsQuery()),
|
||||||
|
dataset,
|
||||||
|
() -> new CompositeAggregationBuilder(
|
||||||
|
"name",
|
||||||
|
Arrays.asList(
|
||||||
|
new HistogramValuesSourceBuilder("price").field("price")
|
||||||
|
.missingBucket(true)
|
||||||
|
.missingOrder(MissingOrder.LAST)
|
||||||
|
.order(SortOrder.DESC)
|
||||||
|
.interval(10)
|
||||||
|
)
|
||||||
|
).aggregateAfter(createAfterKey("price", 50.0)),
|
||||||
|
(result) -> {
|
||||||
|
assertEquals(1, result.getBuckets().size());
|
||||||
|
assertEquals("{price=null}", result.afterKey().toString());
|
||||||
|
assertEquals("{price=null}", result.getBuckets().get(0).getKeyAsString());
|
||||||
|
assertEquals(1L, result.getBuckets().get(0).getDocCount());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void testWithKeywordAndDateHistogram() throws IOException {
|
public void testWithKeywordAndDateHistogram() throws IOException {
|
||||||
final List<Map<String, List<Object>>> dataset = new ArrayList<>();
|
final List<Map<String, List<Object>>> dataset = new ArrayList<>();
|
||||||
dataset.addAll(
|
dataset.addAll(
|
||||||
|
|
|
@ -63,6 +63,7 @@ import org.opensearch.index.mapper.NumberFieldMapper;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
import org.opensearch.search.aggregations.AggregatorTestCase;
|
import org.opensearch.search.aggregations.AggregatorTestCase;
|
||||||
import org.opensearch.search.aggregations.LeafBucketCollector;
|
import org.opensearch.search.aggregations.LeafBucketCollector;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -277,6 +278,7 @@ public class CompositeValuesCollectorQueueTests extends AggregatorTestCase {
|
||||||
value -> value,
|
value -> value,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
size,
|
size,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -287,6 +289,7 @@ public class CompositeValuesCollectorQueueTests extends AggregatorTestCase {
|
||||||
context -> FieldData.sortableLongBitsToDoubles(DocValues.getSortedNumeric(context.reader(), fieldType.name())),
|
context -> FieldData.sortableLongBitsToDoubles(DocValues.getSortedNumeric(context.reader(), fieldType.name())),
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
size,
|
size,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -300,6 +303,7 @@ public class CompositeValuesCollectorQueueTests extends AggregatorTestCase {
|
||||||
context -> DocValues.getSortedSet(context.reader(), fieldType.name()),
|
context -> DocValues.getSortedSet(context.reader(), fieldType.name()),
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
size,
|
size,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -311,6 +315,7 @@ public class CompositeValuesCollectorQueueTests extends AggregatorTestCase {
|
||||||
context -> FieldData.toString(DocValues.getSortedSet(context.reader(), fieldType.name())),
|
context -> FieldData.toString(DocValues.getSortedSet(context.reader(), fieldType.name())),
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
missingBucket,
|
missingBucket,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
size,
|
size,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.opensearch.index.mapper.KeywordFieldMapper;
|
||||||
import org.opensearch.index.mapper.MappedFieldType;
|
import org.opensearch.index.mapper.MappedFieldType;
|
||||||
import org.opensearch.index.mapper.NumberFieldMapper;
|
import org.opensearch.index.mapper.NumberFieldMapper;
|
||||||
import org.opensearch.search.DocValueFormat;
|
import org.opensearch.search.DocValueFormat;
|
||||||
|
import org.opensearch.search.aggregations.bucket.missing.MissingOrder;
|
||||||
import org.opensearch.test.OpenSearchTestCase;
|
import org.opensearch.test.OpenSearchTestCase;
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
@ -62,6 +63,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
context -> null,
|
context -> null,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
false,
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -79,6 +81,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
context -> null,
|
context -> null,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
true,
|
true,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -92,13 +95,24 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
context -> null,
|
context -> null,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
false,
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
0,
|
0,
|
||||||
-1
|
-1
|
||||||
);
|
);
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
||||||
|
|
||||||
MappedFieldType ip = new IpFieldMapper.IpFieldType("ip");
|
MappedFieldType ip = new IpFieldMapper.IpFieldType("ip");
|
||||||
source = new BinaryValuesSource(BigArrays.NON_RECYCLING_INSTANCE, (b) -> {}, ip, context -> null, DocValueFormat.RAW, false, 1, 1);
|
source = new BinaryValuesSource(
|
||||||
|
BigArrays.NON_RECYCLING_INSTANCE,
|
||||||
|
(b) -> {},
|
||||||
|
ip,
|
||||||
|
context -> null,
|
||||||
|
DocValueFormat.RAW,
|
||||||
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
);
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +124,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
context -> null,
|
context -> null,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
false,
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -120,7 +135,16 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("keyword", "toto)"))));
|
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("keyword", "toto)"))));
|
||||||
|
|
||||||
source = new GlobalOrdinalValuesSource(BigArrays.NON_RECYCLING_INSTANCE, keyword, context -> null, DocValueFormat.RAW, true, 1, 1);
|
source = new GlobalOrdinalValuesSource(
|
||||||
|
BigArrays.NON_RECYCLING_INSTANCE,
|
||||||
|
keyword,
|
||||||
|
context -> null,
|
||||||
|
DocValueFormat.RAW,
|
||||||
|
true,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
);
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, new MatchAllDocsQuery()));
|
assertNull(source.createSortedDocsProducerOrNull(reader, new MatchAllDocsQuery()));
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
||||||
|
@ -131,6 +155,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
context -> null,
|
context -> null,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
false,
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
-1
|
-1
|
||||||
);
|
);
|
||||||
|
@ -138,7 +163,16 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
||||||
|
|
||||||
final MappedFieldType ip = new IpFieldMapper.IpFieldType("ip");
|
final MappedFieldType ip = new IpFieldMapper.IpFieldType("ip");
|
||||||
source = new GlobalOrdinalValuesSource(BigArrays.NON_RECYCLING_INSTANCE, ip, context -> null, DocValueFormat.RAW, false, 1, 1);
|
source = new GlobalOrdinalValuesSource(
|
||||||
|
BigArrays.NON_RECYCLING_INSTANCE,
|
||||||
|
ip,
|
||||||
|
context -> null,
|
||||||
|
DocValueFormat.RAW,
|
||||||
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
);
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
assertNull(source.createSortedDocsProducerOrNull(reader, null));
|
||||||
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
assertNull(source.createSortedDocsProducerOrNull(reader, new TermQuery(new Term("foo", "bar"))));
|
||||||
}
|
}
|
||||||
|
@ -159,6 +193,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
value -> value,
|
value -> value,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
false,
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -192,6 +227,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
value -> value,
|
value -> value,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
true,
|
true,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
@ -213,6 +249,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
value -> value,
|
value -> value,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
false,
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
-1
|
-1
|
||||||
);
|
);
|
||||||
|
@ -231,6 +268,7 @@ public class SingleDimensionValuesSourceTests extends OpenSearchTestCase {
|
||||||
context -> null,
|
context -> null,
|
||||||
DocValueFormat.RAW,
|
DocValueFormat.RAW,
|
||||||
false,
|
false,
|
||||||
|
MissingOrder.DEFAULT,
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue