Skip to content

Commit

Permalink
Fix parsing RareTerms aggregation response failed in RestHighLevelCli…
Browse files Browse the repository at this point in the history
…ent (#64454)

- Add LongRareTerms and StringRareTerms to the DefaultNamedXContents,
  ensure that the response of RareTerms aggregation can be parsed
  correctly.
- Add testSearchWithRareTermsAgg method to test the response of
  RareTerms aggregation can be parsed correctly.
- Add some test code to ensure the AggregationsTests can execute
  successfully.
  • Loading branch information
gaobinlong committed Nov 16, 2020
1 parent 9fef6e7 commit e4a90d5
Show file tree
Hide file tree
Showing 10 changed files with 632 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,19 @@
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.sampler.InternalSampler;
import org.elasticsearch.search.aggregations.bucket.sampler.ParsedSampler;
import org.elasticsearch.search.aggregations.bucket.terms.LongRareTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongRareTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedSignificantLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedSignificantStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringRareTerms;
import org.elasticsearch.search.aggregations.bucket.terms.SignificantLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.SignificantStringTerms;
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.StringRareTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.CardinalityAggregationBuilder;
Expand Down Expand Up @@ -1953,6 +1957,8 @@ static List<NamedXContentRegistry.Entry> getDefaultNamedXContents() {
map.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c));
map.put(LongTerms.NAME, (p, c) -> ParsedLongTerms.fromXContent(p, (String) c));
map.put(DoubleTerms.NAME, (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c));
map.put(LongRareTerms.NAME, (p, c) -> ParsedLongRareTerms.fromXContent(p, (String) c));
map.put(StringRareTerms.NAME, (p, c) -> ParsedStringRareTerms.fromXContent(p, (String) c));
map.put(MissingAggregationBuilder.NAME, (p, c) -> ParsedMissing.fromXContent(p, (String) c));
map.put(NestedAggregationBuilder.NAME, (p, c) -> ParsedNested.fromXContent(p, (String) c));
map.put(ReverseNestedAggregationBuilder.NAME, (p, c) -> ParsedReverseNested.fromXContent(p, (String) c));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
import org.elasticsearch.search.aggregations.bucket.composite.TermsValuesSourceBuilder;
import org.elasticsearch.search.aggregations.bucket.range.Range;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.RareTerms;
import org.elasticsearch.search.aggregations.bucket.terms.RareTermsAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.matrix.stats.MatrixStats;
Expand Down Expand Up @@ -279,6 +281,26 @@ public void testSearchWithTermsAgg() throws IOException {
assertEquals(0, type2.getAggregations().asList().size());
}

public void testSearchWithRareTermsAgg() throws IOException {
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(new RareTermsAggregationBuilder("agg1").userValueTypeHint(ValueType.STRING)
.field("type.keyword").maxDocCount(2));
searchSourceBuilder.size(0);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync);
assertSearchHeader(searchResponse);
assertNull(searchResponse.getSuggest());
assertEquals(Collections.emptyMap(), searchResponse.getProfileResults());
assertEquals(0, searchResponse.getHits().getHits().length);
RareTerms termsAgg = searchResponse.getAggregations().get("agg1");
assertEquals("agg1", termsAgg.getName());
assertEquals(1, termsAgg.getBuckets().size());
RareTerms.Bucket type2 = termsAgg.getBucketByKey("type2");
assertEquals(2, type2.getDocCount());
assertEquals(0, type2.getAggregations().asList().size());
}

public void testSearchWithCompositeAgg() throws IOException {
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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 ParsedLongRareTerms extends ParsedRareTerms {
@Override
public String getType() {
return LongRareTerms.NAME;
}

private static final ObjectParser<ParsedLongRareTerms, Void> PARSER =
new ObjectParser<>(ParsedLongRareTerms.class.getSimpleName(), true, ParsedLongRareTerms::new);

static {
declareParsedTermsFields(PARSER, ParsedBucket::fromXContent);
}

public static ParsedLongRareTerms fromXContent(XContentParser parser, String name) throws IOException {
ParsedLongRareTerms aggregation = PARSER.parse(parser, null);
aggregation.setName(name);
return aggregation;
}

public static class ParsedBucket extends ParsedRareTerms.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 ParsedLongRareTerms.ParsedBucket fromXContent(XContentParser parser) throws IOException {
return parseRareTermsBucketXContent(parser, ParsedLongRareTerms.ParsedBucket::new, (p, bucket) -> bucket.key = p.longValue());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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;

public abstract class ParsedRareTerms extends ParsedMultiBucketAggregation<ParsedRareTerms.ParsedBucket> implements RareTerms {
@Override
public List<? extends RareTerms.Bucket> getBuckets() {
return buckets;
}

@Override
public RareTerms.Bucket getBucketByKey(String term) {
for (RareTerms.Bucket bucket : getBuckets()) {
if (bucket.getKeyAsString().equals(term)) {
return bucket;
}
}
return null;
}

@Override
protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException {
builder.startArray(CommonFields.BUCKETS.getPreferredName());
for (RareTerms.Bucket bucket : getBuckets()) {
bucket.toXContent(builder, params);
}
builder.endArray();
return builder;
}

static void declareParsedTermsFields(final ObjectParser<? extends ParsedRareTerms, Void> objectParser,
final CheckedFunction<XContentParser, ParsedBucket, IOException> bucketParser) {
declareMultiBucketAggregationFields(objectParser, bucketParser::apply, bucketParser::apply);
}

public abstract static class ParsedBucket extends ParsedMultiBucketAggregation.ParsedBucket implements RareTerms.Bucket {

@Override
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
keyToXContent(builder);
builder.field(CommonFields.DOC_COUNT.getPreferredName(), getDocCount());
getAggregations().toXContentInternal(builder, params);
builder.endObject();
return builder;
}


static <B extends ParsedBucket> B parseRareTermsBucketXContent(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 (token == XContentParser.Token.START_OBJECT) {
XContentParserUtils.parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class,
aggregations::add);
}
}
bucket.setAggregations(new Aggregations(aggregations));
return bucket;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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 java.io.IOException;
import java.nio.CharBuffer;

import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;

public class ParsedStringRareTerms extends ParsedRareTerms {
@Override
public String getType() {
return StringRareTerms.NAME;
}

private static final ObjectParser<ParsedStringRareTerms, Void> PARSER =
new ObjectParser<>(ParsedStringRareTerms.class.getSimpleName(), true, ParsedStringRareTerms::new);

static {
declareParsedTermsFields(PARSER, ParsedBucket::fromXContent);
}

public static ParsedStringRareTerms fromXContent(XContentParser parser, String name) throws IOException {
ParsedStringRareTerms aggregation = PARSER.parse(parser, null);
aggregation.setName(name);
return aggregation;
}

public static class ParsedBucket extends ParsedRareTerms.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 ParsedStringRareTerms.ParsedBucket fromXContent(XContentParser parser) throws IOException {
return parseRareTermsBucketXContent(parser, ParsedStringRareTerms.ParsedBucket::new, (p, bucket) -> {
CharBuffer cb = p.charBufferOrNull();
if (cb == null) {
bucket.key = null;
} else {
bucket.key = new BytesRef(cb);
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@
import org.elasticsearch.search.aggregations.bucket.range.InternalRangeTests;
import org.elasticsearch.search.aggregations.bucket.sampler.InternalSamplerTests;
import org.elasticsearch.search.aggregations.bucket.terms.DoubleTermsTests;
import org.elasticsearch.search.aggregations.bucket.terms.LongRareTermsTests;
import org.elasticsearch.search.aggregations.bucket.terms.LongTermsTests;
import org.elasticsearch.search.aggregations.bucket.terms.SignificantLongTermsTests;
import org.elasticsearch.search.aggregations.bucket.terms.SignificantStringTermsTests;
import org.elasticsearch.search.aggregations.bucket.terms.StringRareTermsTests;
import org.elasticsearch.search.aggregations.bucket.terms.StringTermsTests;
import org.elasticsearch.search.aggregations.metrics.InternalAvgTests;
import org.elasticsearch.search.aggregations.metrics.InternalCardinalityTests;
Expand Down Expand Up @@ -132,6 +134,8 @@ public class AggregationsTests extends ESTestCase {
new LongTermsTests(),
new DoubleTermsTests(),
new StringTermsTests(),
new LongRareTermsTests(),
new StringRareTermsTests(),
new InternalMissingTests(),
new InternalNestedTests(),
new InternalReverseNestedTests(),
Expand Down

0 comments on commit e4a90d5

Please sign in to comment.