Add parsing for String/Long/Double Terms aggregations (#24521)

This commit is contained in:
Tanguy Leroux 2017-05-10 10:17:25 +02:00 committed by GitHub
parent 01b976071f
commit 3c66ac06ae
14 changed files with 542 additions and 79 deletions

View File

@ -47,7 +47,7 @@ public class Aggregations implements Iterable<Aggregation>, ToXContent {
protected Aggregations() { protected Aggregations() {
} }
protected Aggregations(List<? extends Aggregation> aggregations) { public Aggregations(List<? extends Aggregation> aggregations) {
this.aggregations = aggregations; this.aggregations = aggregations;
} }

View File

@ -19,6 +19,7 @@
package org.elasticsearch.search.aggregations; package org.elasticsearch.search.aggregations;
import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -33,10 +34,11 @@ import java.util.function.Supplier;
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
public abstract class ParsedMultiBucketAggregation extends ParsedAggregation implements MultiBucketsAggregation { public abstract class ParsedMultiBucketAggregation<B extends ParsedMultiBucketAggregation.Bucket>
extends ParsedAggregation implements MultiBucketsAggregation {
protected final List<ParsedBucket<?>> buckets = new ArrayList<>(); protected final List<B> buckets = new ArrayList<>();
protected boolean keyed; protected boolean keyed = false;
@Override @Override
protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException {
@ -45,7 +47,7 @@ public abstract class ParsedMultiBucketAggregation extends ParsedAggregation imp
} else { } else {
builder.startArray(CommonFields.BUCKETS.getPreferredName()); builder.startArray(CommonFields.BUCKETS.getPreferredName());
} }
for (ParsedBucket<?> bucket : buckets) { for (B bucket : buckets) {
bucket.toXContent(builder, params); bucket.toXContent(builder, params);
} }
if (keyed) { if (keyed) {
@ -57,8 +59,8 @@ public abstract class ParsedMultiBucketAggregation extends ParsedAggregation imp
} }
protected static void declareMultiBucketAggregationFields(final ObjectParser<? extends ParsedMultiBucketAggregation, Void> objectParser, protected static void declareMultiBucketAggregationFields(final ObjectParser<? extends ParsedMultiBucketAggregation, Void> objectParser,
final CheckedFunction<XContentParser, ParsedBucket<?>, IOException> bucketParser, final CheckedFunction<XContentParser, ParsedBucket, IOException> bucketParser,
final CheckedFunction<XContentParser, ParsedBucket<?>, IOException> keyedBucketParser) { final CheckedFunction<XContentParser, ParsedBucket, IOException> keyedBucketParser) {
declareAggregationFields(objectParser); declareAggregationFields(objectParser);
objectParser.declareField((parser, aggregation, context) -> { objectParser.declareField((parser, aggregation, context) -> {
XContentParser.Token token = parser.currentToken(); XContentParser.Token token = parser.currentToken();
@ -76,23 +78,13 @@ public abstract class ParsedMultiBucketAggregation extends ParsedAggregation imp
}, CommonFields.BUCKETS, ObjectParser.ValueType.OBJECT_ARRAY); }, CommonFields.BUCKETS, ObjectParser.ValueType.OBJECT_ARRAY);
} }
public static class ParsedBucket<T> implements MultiBucketsAggregation.Bucket { public static abstract class ParsedBucket implements MultiBucketsAggregation.Bucket {
private Aggregations aggregations; private Aggregations aggregations;
private T key;
private String keyAsString; private String keyAsString;
private long docCount; private long docCount;
private boolean keyed; private boolean keyed;
protected void setKey(T key) {
this.key = key;
}
@Override
public Object getKey() {
return key;
}
protected void setKeyAsString(String keyAsString) { protected void setKeyAsString(String keyAsString) {
this.keyAsString = keyAsString; this.keyAsString = keyAsString;
} }
@ -137,17 +129,21 @@ public abstract class ParsedMultiBucketAggregation extends ParsedAggregation imp
if (keyAsString != null) { if (keyAsString != null) {
builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString()); builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString());
} }
builder.field(CommonFields.KEY.getPreferredName(), key); keyToXContent(builder);
builder.field(CommonFields.DOC_COUNT.getPreferredName(), docCount); builder.field(CommonFields.DOC_COUNT.getPreferredName(), docCount);
aggregations.toXContentInternal(builder, params); aggregations.toXContentInternal(builder, params);
builder.endObject(); builder.endObject();
return builder; return builder;
} }
protected static <T, B extends ParsedBucket<T>> B parseXContent(final XContentParser parser, protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException {
return builder.field(CommonFields.KEY.getPreferredName(), getKey());
}
protected static <B extends ParsedBucket> B parseXContent(final XContentParser parser,
final boolean keyed, final boolean keyed,
final Supplier<B> bucketSupplier, final Supplier<B> bucketSupplier,
final CheckedFunction<XContentParser, T, IOException> keyParser) final CheckedBiConsumer<XContentParser, B, IOException> keyConsumer)
throws IOException { throws IOException {
final B bucket = bucketSupplier.get(); final B bucket = bucketSupplier.get();
bucket.setKeyed(keyed); bucket.setKeyed(keyed);
@ -166,7 +162,7 @@ public abstract class ParsedMultiBucketAggregation extends ParsedAggregation imp
if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) { if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) {
bucket.setKeyAsString(parser.text()); bucket.setKeyAsString(parser.text());
} else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) { } else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) {
bucket.setKey(keyParser.apply(parser)); keyConsumer.accept(parser, bucket);
} else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) { } else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) {
bucket.setDocCount(parser.longValue()); bucket.setDocCount(parser.longValue());
} }

View File

@ -20,17 +20,16 @@
package org.elasticsearch.search.aggregations.bucket.histogram; package org.elasticsearch.search.aggregations.bucket.histogram;
import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements Histogram { public class ParsedDateHistogram extends ParsedMultiBucketAggregation<ParsedDateHistogram.ParsedBucket> implements Histogram {
@Override @Override
protected String getType() { protected String getType() {
@ -39,7 +38,7 @@ public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements
@Override @Override
public List<? extends Histogram.Bucket> getBuckets() { public List<? extends Histogram.Bucket> getBuckets() {
return buckets.stream().map(bucket -> (Histogram.Bucket) bucket).collect(Collectors.toList()); return buckets;
} }
private static ObjectParser<ParsedDateHistogram, Void> PARSER = private static ObjectParser<ParsedDateHistogram, Void> PARSER =
@ -56,11 +55,16 @@ public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements
return aggregation; return aggregation;
} }
public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket<Long> implements Histogram.Bucket { public static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket {
private Long key;
@Override @Override
public Object getKey() { public Object getKey() {
return new DateTime(super.getKey(), DateTimeZone.UTC); if (key != null) {
return new DateTime(key, DateTimeZone.UTC);
}
return null;
} }
@Override @Override
@ -68,13 +72,20 @@ public class ParsedDateHistogram extends ParsedMultiBucketAggregation implements
String keyAsString = super.getKeyAsString(); String keyAsString = super.getKeyAsString();
if (keyAsString != null) { if (keyAsString != null) {
return keyAsString; return keyAsString;
} else {
return DocValueFormat.RAW.format((Long) super.getKey());
} }
if (key != null) {
return Long.toString(key);
}
return null;
}
@Override
protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException {
return builder.field(CommonFields.KEY.getPreferredName(), key);
} }
static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException { static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException {
return parseXContent(parser, keyed, ParsedBucket::new, XContentParser::longValue); return parseXContent(parser, keyed, ParsedBucket::new, (p, bucket) -> bucket.key = p.longValue());
} }
} }
} }

View File

@ -21,14 +21,12 @@ package org.elasticsearch.search.aggregations.bucket.histogram;
import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class ParsedHistogram extends ParsedMultiBucketAggregation implements Histogram { public class ParsedHistogram extends ParsedMultiBucketAggregation<ParsedHistogram.ParsedBucket> implements Histogram {
@Override @Override
protected String getType() { protected String getType() {
@ -37,7 +35,7 @@ public class ParsedHistogram extends ParsedMultiBucketAggregation implements His
@Override @Override
public List<? extends Histogram.Bucket> getBuckets() { public List<? extends Histogram.Bucket> getBuckets() {
return buckets.stream().map(bucket -> (Histogram.Bucket) bucket).collect(Collectors.toList()); return buckets;
} }
private static ObjectParser<ParsedHistogram, Void> PARSER = private static ObjectParser<ParsedHistogram, Void> PARSER =
@ -54,20 +52,29 @@ public class ParsedHistogram extends ParsedMultiBucketAggregation implements His
return aggregation; return aggregation;
} }
static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket<Double> implements Histogram.Bucket { static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Histogram.Bucket {
private Double key;
@Override
public Object getKey() {
return key;
}
@Override @Override
public String getKeyAsString() { public String getKeyAsString() {
String keyAsString = super.getKeyAsString(); String keyAsString = super.getKeyAsString();
if (keyAsString != null) { if (keyAsString != null) {
return keyAsString; return keyAsString;
} else {
return DocValueFormat.RAW.format((Double) getKey());
} }
if (key != null) {
return Double.toString(key);
}
return null;
} }
static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException { static ParsedBucket fromXContent(XContentParser parser, boolean keyed) throws IOException {
return parseXContent(parser, keyed, ParsedBucket::new, XContentParser::doubleValue); return parseXContent(parser, keyed, ParsedBucket::new, (p, bucket) -> bucket.key = p.doubleValue());
} }
} }
} }

View File

@ -0,0 +1,85 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.bucket.terms;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
public class ParsedDoubleTerms extends ParsedTerms {
@Override
protected String getType() {
return DoubleTerms.NAME;
}
private static ObjectParser<ParsedDoubleTerms, Void> PARSER =
new ObjectParser<>(ParsedDoubleTerms.class.getSimpleName(), true, ParsedDoubleTerms::new);
static {
declareParsedTermsFields(PARSER, ParsedBucket::fromXContent);
}
public static ParsedDoubleTerms fromXContent(XContentParser parser, String name) throws IOException {
ParsedDoubleTerms aggregation = PARSER.parse(parser, null);
aggregation.setName(name);
return aggregation;
}
public static class ParsedBucket extends ParsedTerms.ParsedBucket {
private Double key;
@Override
public Object getKey() {
return key;
}
@Override
public String getKeyAsString() {
String keyAsString = super.getKeyAsString();
if (keyAsString != null) {
return keyAsString;
}
if (key != null) {
return Double.toString(key);
}
return null;
}
public Number getKeyAsNumber() {
return key;
}
@Override
protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException {
builder.field(CommonFields.KEY.getPreferredName(), key);
if (super.getKeyAsString() != null) {
builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString());
}
return builder;
}
static ParsedBucket fromXContent(XContentParser parser) throws IOException {
return parseTermsBucketXContent(parser, ParsedBucket::new, (p, bucket) -> bucket.key = p.doubleValue());
}
}
}

View File

@ -0,0 +1,85 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.bucket.terms;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
public class ParsedLongTerms extends ParsedTerms {
@Override
protected String getType() {
return LongTerms.NAME;
}
private static ObjectParser<ParsedLongTerms, Void> PARSER =
new ObjectParser<>(ParsedLongTerms.class.getSimpleName(), true, ParsedLongTerms::new);
static {
declareParsedTermsFields(PARSER, ParsedBucket::fromXContent);
}
public static ParsedLongTerms fromXContent(XContentParser parser, String name) throws IOException {
ParsedLongTerms aggregation = PARSER.parse(parser, null);
aggregation.setName(name);
return aggregation;
}
public static class ParsedBucket extends ParsedTerms.ParsedBucket {
private Long key;
@Override
public Object getKey() {
return key;
}
@Override
public String getKeyAsString() {
String keyAsString = super.getKeyAsString();
if (keyAsString != null) {
return keyAsString;
}
if (key != null) {
return Long.toString(key);
}
return null;
}
public Number getKeyAsNumber() {
return key;
}
@Override
protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException {
builder.field(CommonFields.KEY.getPreferredName(), key);
if (super.getKeyAsString() != null) {
builder.field(CommonFields.KEY_AS_STRING.getPreferredName(), getKeyAsString());
}
return builder;
}
static ParsedBucket fromXContent(XContentParser parser) throws IOException {
return parseTermsBucketXContent(parser, ParsedBucket::new, (p, bucket) -> bucket.key = p.longValue());
}
}
}

View File

@ -0,0 +1,85 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.bucket.terms;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
public class ParsedStringTerms extends ParsedTerms {
@Override
protected String getType() {
return StringTerms.NAME;
}
private static ObjectParser<ParsedStringTerms, Void> PARSER =
new ObjectParser<>(ParsedStringTerms.class.getSimpleName(), true, ParsedStringTerms::new);
static {
declareParsedTermsFields(PARSER, ParsedBucket::fromXContent);
}
public static ParsedStringTerms fromXContent(XContentParser parser, String name) throws IOException {
ParsedStringTerms aggregation = PARSER.parse(parser, null);
aggregation.setName(name);
return aggregation;
}
public static class ParsedBucket extends ParsedTerms.ParsedBucket {
private BytesRef key;
@Override
public Object getKey() {
return getKeyAsString();
}
@Override
public String getKeyAsString() {
String keyAsString = super.getKeyAsString();
if (keyAsString != null) {
return keyAsString;
}
if (key != null) {
return key.utf8ToString();
}
return null;
}
public Number getKeyAsNumber() {
if (key != null) {
return Double.parseDouble(key.utf8ToString());
}
return null;
}
@Override
protected XContentBuilder keyToXContent(XContentBuilder builder) throws IOException {
return builder.field(CommonFields.KEY.getPreferredName(), getKey());
}
static ParsedBucket fromXContent(XContentParser parser) throws IOException {
return parseTermsBucketXContent(parser, ParsedBucket::new, (p, bucket) -> bucket.key = p.utf8BytesOrNull());
}
}
}

View File

@ -0,0 +1,151 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.bucket.terms;
import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import static org.elasticsearch.search.aggregations.bucket.terms.InternalTerms.DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME;
import static org.elasticsearch.search.aggregations.bucket.terms.InternalTerms.SUM_OF_OTHER_DOC_COUNTS;
public abstract class ParsedTerms extends ParsedMultiBucketAggregation<ParsedTerms.ParsedBucket> implements Terms {
protected long docCountErrorUpperBound;
protected long sumOtherDocCount;
@Override
public long getDocCountError() {
return docCountErrorUpperBound;
}
@Override
public long getSumOfOtherDocCounts() {
return sumOtherDocCount;
}
@Override
public List<? extends Terms.Bucket> getBuckets() {
return buckets;
}
@Override
public Terms.Bucket getBucketByKey(String term) {
for (Terms.Bucket bucket : getBuckets()) {
if (bucket.getKeyAsString().equals(term)) {
return bucket;
}
}
return null;
}
@Override
protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException {
builder.field(DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME.getPreferredName(), getDocCountError());
builder.field(SUM_OF_OTHER_DOC_COUNTS.getPreferredName(), getSumOfOtherDocCounts());
builder.startArray(CommonFields.BUCKETS.getPreferredName());
for (Terms.Bucket bucket : getBuckets()) {
bucket.toXContent(builder, params);
}
builder.endArray();
return builder;
}
static void declareParsedTermsFields(final ObjectParser<? extends ParsedTerms, Void> objectParser,
final CheckedFunction<XContentParser, ParsedBucket, IOException> bucketParser) {
declareMultiBucketAggregationFields(objectParser, bucketParser::apply, bucketParser::apply);
objectParser.declareLong((parsedTerms, value) -> parsedTerms.docCountErrorUpperBound = value ,
DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME);
objectParser.declareLong((parsedTerms, value) -> parsedTerms.sumOtherDocCount = value,
SUM_OF_OTHER_DOC_COUNTS);
}
public abstract static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements Terms.Bucket {
boolean showDocCountError = false;
protected long docCountError;
@Override
public int compareTerm(Terms.Bucket other) {
throw new UnsupportedOperationException();
}
@Override
public long getDocCountError() {
return docCountError;
}
@Override
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
keyToXContent(builder);
builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount());
if (showDocCountError) {
builder.field(DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME.getPreferredName(), getDocCountError());
}
getAggregations().toXContentInternal(builder, params);
builder.endObject();
return builder;
}
static <B extends ParsedBucket> B parseTermsBucketXContent(final XContentParser parser, final Supplier<B> bucketSupplier,
final CheckedBiConsumer<XContentParser, B, IOException> keyConsumer)
throws IOException {
final B bucket = bucketSupplier.get();
final List<Aggregation> aggregations = new ArrayList<>();
XContentParser.Token token;
String currentFieldName = parser.currentName();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (CommonFields.KEY_AS_STRING.getPreferredName().equals(currentFieldName)) {
bucket.setKeyAsString(parser.text());
} else if (CommonFields.KEY.getPreferredName().equals(currentFieldName)) {
keyConsumer.accept(parser, bucket);
} else if (CommonFields.DOC_COUNT.getPreferredName().equals(currentFieldName)) {
bucket.setDocCount(parser.longValue());
} else if (DOC_COUNT_ERROR_UPPER_BOUND_FIELD_NAME.getPreferredName().equals(currentFieldName)) {
bucket.docCountError = parser.longValue();
bucket.showDocCountError = true;
}
} else if (token == XContentParser.Token.START_OBJECT) {
aggregations.add(XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class));
}
}
bucket.setAggregations(new Aggregations(aggregations));
return bucket;
}
}
}

View File

@ -22,7 +22,6 @@ package org.elasticsearch.search.aggregations.metrics.percentiles;
import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.ParsedAggregation;
import java.io.IOException; import java.io.IOException;
@ -60,7 +59,7 @@ public abstract class ParsedPercentiles extends ParsedAggregation implements Ite
} }
Double value = getPercentile(percent); Double value = getPercentile(percent);
if (value != null) { if (value != null) {
return DocValueFormat.RAW.format(value); return Double.toString(value);
} }
return null; return null;
} }

View File

@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.ContextParser;
import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParserUtils;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.rest.action.search.RestSearchAction;
@ -38,6 +39,12 @@ import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggre
import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram; import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram;
import org.elasticsearch.search.aggregations.bucket.terms.DoubleTerms;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedDoubleTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg;
import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder;
@ -127,6 +134,9 @@ public abstract class InternalAggregationTestCase<T extends InternalAggregation>
namedXContents.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c)); namedXContents.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c));
namedXContents.put(HistogramAggregationBuilder.NAME, (p, c) -> ParsedHistogram.fromXContent(p, (String) c)); namedXContents.put(HistogramAggregationBuilder.NAME, (p, c) -> ParsedHistogram.fromXContent(p, (String) c));
namedXContents.put(DateHistogramAggregationBuilder.NAME, (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c)); namedXContents.put(DateHistogramAggregationBuilder.NAME, (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c));
namedXContents.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c));
namedXContents.put(LongTerms.NAME, (p, c) -> ParsedLongTerms.fromXContent(p, (String) c));
namedXContents.put(DoubleTerms.NAME, (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c));
return namedXContents.entrySet().stream() return namedXContents.entrySet().stream()
.map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue()))
@ -259,12 +269,7 @@ public abstract class InternalAggregationTestCase<T extends InternalAggregation>
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
String currentName = parser.currentName(); parsedAggregation = XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class);
int i = currentName.indexOf(InternalAggregation.TYPED_KEYS_DELIMITER);
String aggType = currentName.substring(0, i);
String aggName = currentName.substring(i + 1);
parsedAggregation = parser.namedObject(Aggregation.class, aggType, aggName);
assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken());
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
@ -277,7 +282,7 @@ public abstract class InternalAggregationTestCase<T extends InternalAggregation>
assertEquals(aggregation.getType(), ((ParsedAggregation) parsedAggregation).getType()); assertEquals(aggregation.getType(), ((ParsedAggregation) parsedAggregation).getType());
} }
BytesReference parsedBytes = toXContent((ToXContent) parsedAggregation, xContentType, params, humanReadable); BytesReference parsedBytes = toXContent(parsedAggregation, xContentType, params, humanReadable);
assertToXContentEquivalent(originalBytes, parsedBytes, xContentType); assertToXContentEquivalent(originalBytes, parsedBytes, xContentType);
return (P) parsedAggregation; return (P) parsedAggregation;

View File

@ -22,6 +22,7 @@ package org.elasticsearch.search.aggregations.bucket.terms;
import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.InternalAggregations;
import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation;
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
import java.util.ArrayList; import java.util.ArrayList;
@ -33,17 +34,17 @@ import java.util.Set;
public class DoubleTermsTests extends InternalTermsTestCase { public class DoubleTermsTests extends InternalTermsTestCase {
@Override @Override
protected InternalTerms<?, ?> createTestInstance( protected InternalTerms<?, ?> createTestInstance(String name,
String name,
List<PipelineAggregator> pipelineAggregators, List<PipelineAggregator> pipelineAggregators,
Map<String, Object> metaData) { Map<String, Object> metaData,
InternalAggregations aggregations,
boolean showTermDocCountError,
long docCountError) {
Terms.Order order = Terms.Order.count(false); Terms.Order order = Terms.Order.count(false);
long minDocCount = 1; long minDocCount = 1;
int requiredSize = 3; int requiredSize = 3;
int shardSize = requiredSize + 2; int shardSize = requiredSize + 2;
DocValueFormat format = DocValueFormat.RAW; DocValueFormat format = randomNumericDocValueFormat();
boolean showTermDocCountError = false;
long docCountError = -1;
long otherDocCount = 0; long otherDocCount = 0;
List<DoubleTerms.Bucket> buckets = new ArrayList<>(); List<DoubleTerms.Bucket> buckets = new ArrayList<>();
final int numBuckets = randomInt(shardSize); final int numBuckets = randomInt(shardSize);
@ -51,8 +52,7 @@ public class DoubleTermsTests extends InternalTermsTestCase {
for (int i = 0; i < numBuckets; ++i) { for (int i = 0; i < numBuckets; ++i) {
double term = randomValueOtherThanMany(d -> terms.add(d) == false, random()::nextDouble); double term = randomValueOtherThanMany(d -> terms.add(d) == false, random()::nextDouble);
int docCount = randomIntBetween(1, 100); int docCount = randomIntBetween(1, 100);
buckets.add(new DoubleTerms.Bucket(term, docCount, InternalAggregations.EMPTY, buckets.add(new DoubleTerms.Bucket(term, docCount, aggregations, showTermDocCountError, docCountError, format));
showTermDocCountError, docCountError, format));
} }
return new DoubleTerms(name, order, requiredSize, minDocCount, pipelineAggregators, return new DoubleTerms(name, order, requiredSize, minDocCount, pipelineAggregators,
metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError); metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError);
@ -63,4 +63,9 @@ public class DoubleTermsTests extends InternalTermsTestCase {
return DoubleTerms::new; return DoubleTerms::new;
} }
@Override
protected Class<? extends ParsedMultiBucketAggregation> implementationClass() {
return ParsedDoubleTerms.class;
}
} }

View File

@ -19,18 +19,44 @@
package org.elasticsearch.search.aggregations.bucket.terms; package org.elasticsearch.search.aggregations.bucket.terms;
import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.InternalAggregations;
import org.elasticsearch.search.aggregations.InternalMultiBucketAggregationTestCase;
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
import org.junit.Before;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public abstract class InternalTermsTestCase extends InternalAggregationTestCase<InternalTerms<?,?>> { public abstract class InternalTermsTestCase extends InternalMultiBucketAggregationTestCase<InternalTerms<?, ?>> {
private boolean showDocCount;
private long docCountError;
@Before
public void init() {
showDocCount = randomBoolean();
docCountError = showDocCount ? randomInt(1000) : -1;
}
@Override
protected InternalTerms<?, ?> createTestInstance(String name,
List<PipelineAggregator> pipelineAggregators,
Map<String, Object> metaData,
InternalAggregations aggregations) {
return createTestInstance(name, pipelineAggregators, metaData, aggregations, showDocCount, docCountError);
}
protected abstract InternalTerms<?, ?> createTestInstance(String name,
List<PipelineAggregator> pipelineAggregators,
Map<String, Object> metaData,
InternalAggregations aggregations,
boolean showTermDocCountError,
long docCountError);
@Override @Override
protected InternalTerms<?, ?> createUnmappedInstance( protected InternalTerms<?, ?> createUnmappedInstance(

View File

@ -22,6 +22,7 @@ package org.elasticsearch.search.aggregations.bucket.terms;
import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.InternalAggregations;
import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation;
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
import java.util.ArrayList; import java.util.ArrayList;
@ -33,17 +34,17 @@ import java.util.Set;
public class LongTermsTests extends InternalTermsTestCase { public class LongTermsTests extends InternalTermsTestCase {
@Override @Override
protected InternalTerms<?, ?> createTestInstance( protected InternalTerms<?, ?> createTestInstance(String name,
String name,
List<PipelineAggregator> pipelineAggregators, List<PipelineAggregator> pipelineAggregators,
Map<String, Object> metaData) { Map<String, Object> metaData,
InternalAggregations aggregations,
boolean showTermDocCountError,
long docCountError) {
Terms.Order order = Terms.Order.count(false); Terms.Order order = Terms.Order.count(false);
long minDocCount = 1; long minDocCount = 1;
int requiredSize = 3; int requiredSize = 3;
int shardSize = requiredSize + 2; int shardSize = requiredSize + 2;
DocValueFormat format = DocValueFormat.RAW; DocValueFormat format = randomNumericDocValueFormat();
boolean showTermDocCountError = false;
long docCountError = -1;
long otherDocCount = 0; long otherDocCount = 0;
List<LongTerms.Bucket> buckets = new ArrayList<>(); List<LongTerms.Bucket> buckets = new ArrayList<>();
final int numBuckets = randomInt(shardSize); final int numBuckets = randomInt(shardSize);
@ -51,8 +52,7 @@ public class LongTermsTests extends InternalTermsTestCase {
for (int i = 0; i < numBuckets; ++i) { for (int i = 0; i < numBuckets; ++i) {
long term = randomValueOtherThanMany(l -> terms.add(l) == false, random()::nextLong); long term = randomValueOtherThanMany(l -> terms.add(l) == false, random()::nextLong);
int docCount = randomIntBetween(1, 100); int docCount = randomIntBetween(1, 100);
buckets.add(new LongTerms.Bucket(term, docCount, InternalAggregations.EMPTY, buckets.add(new LongTerms.Bucket(term, docCount, aggregations, showTermDocCountError, docCountError, format));
showTermDocCountError, docCountError, format));
} }
return new LongTerms(name, order, requiredSize, minDocCount, pipelineAggregators, return new LongTerms(name, order, requiredSize, minDocCount, pipelineAggregators,
metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError); metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError);
@ -63,4 +63,8 @@ public class LongTermsTests extends InternalTermsTestCase {
return LongTerms::new; return LongTerms::new;
} }
@Override
protected Class<? extends ParsedMultiBucketAggregation> implementationClass() {
return ParsedLongTerms.class;
}
} }

View File

@ -23,6 +23,7 @@ import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.DocValueFormat;
import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.InternalAggregations;
import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation;
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,17 +35,17 @@ import java.util.Set;
public class StringTermsTests extends InternalTermsTestCase { public class StringTermsTests extends InternalTermsTestCase {
@Override @Override
protected InternalTerms<?, ?> createTestInstance( protected InternalTerms<?, ?> createTestInstance(String name,
String name,
List<PipelineAggregator> pipelineAggregators, List<PipelineAggregator> pipelineAggregators,
Map<String, Object> metaData) { Map<String, Object> metaData,
InternalAggregations aggregations,
boolean showTermDocCountError,
long docCountError) {
Terms.Order order = Terms.Order.count(false); Terms.Order order = Terms.Order.count(false);
long minDocCount = 1; long minDocCount = 1;
int requiredSize = 3; int requiredSize = 3;
int shardSize = requiredSize + 2; int shardSize = requiredSize + 2;
DocValueFormat format = DocValueFormat.RAW; DocValueFormat format = DocValueFormat.RAW;
boolean showTermDocCountError = false;
long docCountError = -1;
long otherDocCount = 0; long otherDocCount = 0;
List<StringTerms.Bucket> buckets = new ArrayList<>(); List<StringTerms.Bucket> buckets = new ArrayList<>();
final int numBuckets = randomInt(shardSize); final int numBuckets = randomInt(shardSize);
@ -52,8 +53,7 @@ public class StringTermsTests extends InternalTermsTestCase {
for (int i = 0; i < numBuckets; ++i) { for (int i = 0; i < numBuckets; ++i) {
BytesRef term = randomValueOtherThanMany(b -> terms.add(b) == false, () -> new BytesRef(randomAlphaOfLength(10))); BytesRef term = randomValueOtherThanMany(b -> terms.add(b) == false, () -> new BytesRef(randomAlphaOfLength(10)));
int docCount = randomIntBetween(1, 100); int docCount = randomIntBetween(1, 100);
buckets.add(new StringTerms.Bucket(term, docCount, InternalAggregations.EMPTY, buckets.add(new StringTerms.Bucket(term, docCount, aggregations, showTermDocCountError, docCountError, format));
showTermDocCountError, docCountError, format));
} }
return new StringTerms(name, order, requiredSize, minDocCount, pipelineAggregators, return new StringTerms(name, order, requiredSize, minDocCount, pipelineAggregators,
metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError); metaData, format, shardSize, showTermDocCountError, otherDocCount, buckets, docCountError);
@ -64,4 +64,8 @@ public class StringTermsTests extends InternalTermsTestCase {
return StringTerms::new; return StringTerms::new;
} }
@Override
protected Class<? extends ParsedMultiBucketAggregation> implementationClass() {
return ParsedStringTerms.class;
}
} }