Skip to content

Commit 84a4bf4

Browse files
authored
Move exponential histogram JSON serialization to shared library (elastic#133207)
1 parent e308abb commit 84a4bf4

File tree

4 files changed

+205
-53
lines changed

4 files changed

+205
-53
lines changed

libs/exponential-histogram/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ apply plugin: 'elasticsearch.build'
1313

1414
dependencies {
1515
api project(':libs:core')
16+
api project(':libs:x-content')
1617
api "org.apache.lucene:lucene-core:${versions.lucene}"
1718

1819
testImplementation(project(":test:framework"))
1920
testImplementation('ch.obermuhlner:big-math:2.3.2')
2021
testImplementation('org.apache.commons:commons-math3:3.6.1')
22+
testImplementation project(':libs:x-content:impl')
2123
}
2224

2325
tasks.named('forbiddenApisMain').configure {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright Elasticsearch B.V., and/or licensed to Elasticsearch B.V.
3+
* under one or more license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* This file is based on a modification of https://github.com/open-telemetry/opentelemetry-java which is licensed under the Apache 2.0 License.
20+
*/
21+
22+
package org.elasticsearch.exponentialhistogram;
23+
24+
import org.elasticsearch.xcontent.XContentBuilder;
25+
26+
import java.io.IOException;
27+
28+
/**
29+
* Handles the serialization of an {@link ExponentialHistogram} to XContent.
30+
*/
31+
public class ExponentialHistogramXContent {
32+
33+
public static final String SCALE_FIELD = "scale";
34+
public static final String ZERO_FIELD = "zero";
35+
public static final String ZERO_COUNT_FIELD = "count";
36+
public static final String ZERO_THRESHOLD_FIELD = "threshold";
37+
public static final String POSITIVE_FIELD = "positive";
38+
public static final String NEGATIVE_FIELD = "negative";
39+
public static final String BUCKET_INDICES_FIELD = "indices";
40+
public static final String BUCKET_COUNTS_FIELD = "counts";
41+
42+
/**
43+
* Serializes an {@link ExponentialHistogram} to the provided {@link XContentBuilder}.
44+
* @param builder the XContentBuilder to write to
45+
* @param histogram the ExponentialHistogram to serialize
46+
* @throws IOException if the XContentBuilder throws an IOException
47+
*/
48+
public static void serialize(XContentBuilder builder, ExponentialHistogram histogram) throws IOException {
49+
builder.startObject();
50+
51+
builder.field(SCALE_FIELD, histogram.scale());
52+
double zeroThreshold = histogram.zeroBucket().zeroThreshold();
53+
long zeroCount = histogram.zeroBucket().count();
54+
55+
if (zeroCount != 0 || zeroThreshold != 0) {
56+
builder.startObject(ZERO_FIELD);
57+
if (zeroCount != 0) {
58+
builder.field(ZERO_COUNT_FIELD, zeroCount);
59+
}
60+
if (zeroThreshold != 0) {
61+
builder.field(ZERO_THRESHOLD_FIELD, zeroThreshold);
62+
}
63+
builder.endObject();
64+
}
65+
66+
writeBuckets(builder, POSITIVE_FIELD, histogram.positiveBuckets());
67+
writeBuckets(builder, NEGATIVE_FIELD, histogram.negativeBuckets());
68+
69+
builder.endObject();
70+
}
71+
72+
private static void writeBuckets(XContentBuilder b, String fieldName, ExponentialHistogram.Buckets buckets) throws IOException {
73+
if (buckets.iterator().hasNext() == false) {
74+
return;
75+
}
76+
b.startObject(fieldName);
77+
BucketIterator it = buckets.iterator();
78+
b.startArray(BUCKET_INDICES_FIELD);
79+
while (it.hasNext()) {
80+
b.value(it.peekIndex());
81+
it.advance();
82+
}
83+
b.endArray();
84+
it = buckets.iterator();
85+
b.startArray(BUCKET_COUNTS_FIELD);
86+
while (it.hasNext()) {
87+
b.value(it.peekCount());
88+
it.advance();
89+
}
90+
b.endArray();
91+
b.endObject();
92+
}
93+
94+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright Elasticsearch B.V., and/or licensed to Elasticsearch B.V.
3+
* under one or more license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* This file is based on a modification of https://github.com/open-telemetry/opentelemetry-java which is licensed under the Apache 2.0 License.
20+
*/
21+
22+
package org.elasticsearch.exponentialhistogram;
23+
24+
import org.elasticsearch.common.Strings;
25+
import org.elasticsearch.xcontent.XContentBuilder;
26+
import org.elasticsearch.xcontent.json.JsonXContent;
27+
28+
import java.io.IOException;
29+
30+
import static org.hamcrest.Matchers.equalTo;
31+
32+
public class ExponentialHistogramXContentTests extends ExponentialHistogramTestCase {
33+
34+
public void testEmptyHistogram() {
35+
ExponentialHistogram emptyHistogram = ExponentialHistogram.empty();
36+
assertThat(toJson(emptyHistogram), equalTo("{\"scale\":" + emptyHistogram.scale() + "}"));
37+
}
38+
39+
public void testFullHistogram() {
40+
FixedCapacityExponentialHistogram histo = createAutoReleasedHistogram(100);
41+
histo.setZeroBucket(new ZeroBucket(0.1234, 42));
42+
histo.resetBuckets(7);
43+
histo.tryAddBucket(-10, 15, false);
44+
histo.tryAddBucket(10, 5, false);
45+
histo.tryAddBucket(-11, 10, true);
46+
histo.tryAddBucket(11, 20, true);
47+
assertThat(
48+
toJson(histo),
49+
equalTo(
50+
"{"
51+
+ "\"scale\":7,"
52+
+ "\"zero\":{\"count\":42,\"threshold\":0.1234},"
53+
+ "\"positive\":{\"indices\":[-11,11],\"counts\":[10,20]},"
54+
+ "\"negative\":{\"indices\":[-10,10],\"counts\":[15,5]}"
55+
+ "}"
56+
)
57+
);
58+
}
59+
60+
public void testOnlyZeroThreshold() {
61+
FixedCapacityExponentialHistogram histo = createAutoReleasedHistogram(10);
62+
histo.setZeroBucket(new ZeroBucket(5.0, 0));
63+
histo.resetBuckets(3);
64+
assertThat(toJson(histo), equalTo("{\"scale\":3,\"zero\":{\"threshold\":5.0}}"));
65+
}
66+
67+
public void testOnlyZeroCount() {
68+
FixedCapacityExponentialHistogram histo = createAutoReleasedHistogram(10);
69+
histo.setZeroBucket(new ZeroBucket(0.0, 7));
70+
histo.resetBuckets(2);
71+
assertThat(toJson(histo), equalTo("{\"scale\":2,\"zero\":{\"count\":7}}"));
72+
}
73+
74+
public void testOnlyPositiveBuckets() {
75+
FixedCapacityExponentialHistogram histo = createAutoReleasedHistogram(10);
76+
histo.resetBuckets(4);
77+
histo.tryAddBucket(-1, 3, true);
78+
histo.tryAddBucket(2, 5, true);
79+
assertThat(toJson(histo), equalTo("{\"scale\":4,\"positive\":{\"indices\":[-1,2],\"counts\":[3,5]}}"));
80+
}
81+
82+
public void testOnlyNegativeBuckets() {
83+
FixedCapacityExponentialHistogram histo = createAutoReleasedHistogram(10);
84+
histo.resetBuckets(5);
85+
histo.tryAddBucket(-1, 4, false);
86+
histo.tryAddBucket(2, 6, false);
87+
assertThat(toJson(histo), equalTo("{\"scale\":5,\"negative\":{\"indices\":[-1,2],\"counts\":[4,6]}}"));
88+
}
89+
90+
private static String toJson(ExponentialHistogram histo) {
91+
try (XContentBuilder builder = JsonXContent.contentBuilder()) {
92+
ExponentialHistogramXContent.serialize(builder, histo);
93+
return Strings.toString(builder);
94+
} catch (IOException e) {
95+
throw new RuntimeException(e);
96+
}
97+
}
98+
99+
}

x-pack/plugin/mapper-exponential-histogram/src/main/java/org/elasticsearch/xpack/exponentialhistogram/ExponentialHistogramFieldMapper.java

Lines changed: 10 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import org.elasticsearch.common.Explicit;
2020
import org.elasticsearch.common.io.stream.BytesStreamOutput;
2121
import org.elasticsearch.common.util.FeatureFlag;
22-
import org.elasticsearch.exponentialhistogram.BucketIterator;
2322
import org.elasticsearch.exponentialhistogram.ExponentialHistogram;
23+
import org.elasticsearch.exponentialhistogram.ExponentialHistogramXContent;
2424
import org.elasticsearch.index.fielddata.FieldDataContext;
2525
import org.elasticsearch.index.fielddata.IndexFieldData;
2626
import org.elasticsearch.index.mapper.CompositeSyntheticFieldLoader;
@@ -92,15 +92,15 @@ public class ExponentialHistogramFieldMapper extends FieldMapper {
9292

9393
public static final String CONTENT_TYPE = "exponential_histogram";
9494

95-
public static final ParseField SCALE_FIELD = new ParseField("scale");
96-
public static final ParseField ZERO_FIELD = new ParseField("zero");
97-
public static final ParseField ZERO_COUNT_FIELD = new ParseField("count");
98-
public static final ParseField ZERO_THRESHOLD_FIELD = new ParseField("threshold");
95+
public static final ParseField SCALE_FIELD = new ParseField(ExponentialHistogramXContent.SCALE_FIELD);
96+
public static final ParseField ZERO_FIELD = new ParseField(ExponentialHistogramXContent.ZERO_FIELD);
97+
public static final ParseField ZERO_COUNT_FIELD = new ParseField(ExponentialHistogramXContent.ZERO_COUNT_FIELD);
98+
public static final ParseField ZERO_THRESHOLD_FIELD = new ParseField(ExponentialHistogramXContent.ZERO_THRESHOLD_FIELD);
9999

100-
public static final ParseField POSITIVE_FIELD = new ParseField("positive");
101-
public static final ParseField NEGATIVE_FIELD = new ParseField("negative");
102-
public static final ParseField BUCKET_INDICES_FIELD = new ParseField("indices");
103-
public static final ParseField BUCKET_COUNTS_FIELD = new ParseField("counts");
100+
public static final ParseField POSITIVE_FIELD = new ParseField(ExponentialHistogramXContent.POSITIVE_FIELD);
101+
public static final ParseField NEGATIVE_FIELD = new ParseField(ExponentialHistogramXContent.NEGATIVE_FIELD);
102+
public static final ParseField BUCKET_INDICES_FIELD = new ParseField(ExponentialHistogramXContent.BUCKET_INDICES_FIELD);
103+
public static final ParseField BUCKET_COUNTS_FIELD = new ParseField(ExponentialHistogramXContent.BUCKET_COUNTS_FIELD);
104104

105105
private static ExponentialHistogramFieldMapper toType(FieldMapper in) {
106106
return (ExponentialHistogramFieldMapper) in;
@@ -625,50 +625,7 @@ public void write(XContentBuilder b) throws IOException {
625625
}
626626

627627
histogram.reset(zeroThreshold, valueCount, binaryValue);
628-
629-
b.startObject();
630-
631-
b.field(SCALE_FIELD.getPreferredName(), histogram.scale());
632-
double zeroThreshold = histogram.zeroBucket().zeroThreshold();
633-
long zeroCount = histogram.zeroBucket().count();
634-
635-
if (zeroCount != 0 || zeroThreshold != 0) {
636-
b.startObject(ZERO_FIELD.getPreferredName());
637-
if (zeroCount != 0) {
638-
b.field(ZERO_COUNT_FIELD.getPreferredName(), zeroCount);
639-
}
640-
if (zeroThreshold != 0) {
641-
b.field(ZERO_THRESHOLD_FIELD.getPreferredName(), zeroThreshold);
642-
}
643-
b.endObject();
644-
}
645-
646-
writeBuckets(b, POSITIVE_FIELD.getPreferredName(), histogram.positiveBuckets());
647-
writeBuckets(b, NEGATIVE_FIELD.getPreferredName(), histogram.negativeBuckets());
648-
649-
b.endObject();
650-
}
651-
652-
private static void writeBuckets(XContentBuilder b, String fieldName, ExponentialHistogram.Buckets buckets) throws IOException {
653-
if (buckets.iterator().hasNext() == false) {
654-
return;
655-
}
656-
b.startObject(fieldName);
657-
BucketIterator it = buckets.iterator();
658-
b.startArray(BUCKET_INDICES_FIELD.getPreferredName());
659-
while (it.hasNext()) {
660-
b.value(it.peekIndex());
661-
it.advance();
662-
}
663-
b.endArray();
664-
it = buckets.iterator();
665-
b.startArray(BUCKET_COUNTS_FIELD.getPreferredName());
666-
while (it.hasNext()) {
667-
b.value(it.peekCount());
668-
it.advance();
669-
}
670-
b.endArray();
671-
b.endObject();
628+
ExponentialHistogramXContent.serialize(b, histogram);
672629
}
673630

674631
@Override

0 commit comments

Comments
 (0)