Skip to content

Commit b1ee5ce

Browse files
committed
Support global ords in top_metrics
Adds support for fetching `keyword` and `ip` fields in `top_metrics`. This only works if we can get global ordinals for those fields so we can't support the entire `keyword` family, but this gets us *somewhere*. Closes elastic#64774
1 parent c6493a8 commit b1ee5ce

File tree

10 files changed

+500
-155
lines changed

10 files changed

+500
-155
lines changed

server/src/main/java/org/elasticsearch/search/sort/BucketedSort.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public int getBucketSize() {
171171
*/
172172
@FunctionalInterface
173173
public interface ResultBuilder<T> {
174-
T build(long index, SortValue sortValue);
174+
T build(long index, SortValue sortValue) throws IOException;
175175
}
176176

177177
/**
@@ -180,7 +180,7 @@ public interface ResultBuilder<T> {
180180
* @param builder builds results. See {@link ExtraData} for how to store
181181
* data along side the sort for this to extract.
182182
*/
183-
public final <T extends Comparable<T>> List<T> getValues(long bucket, ResultBuilder<T> builder) {
183+
public final <T extends Comparable<T>> List<T> getValues(long bucket, ResultBuilder<T> builder) throws IOException {
184184
long rootIndex = bucket * bucketSize;
185185
if (rootIndex >= values().size()) {
186186
// We've never seen this bucket.
@@ -201,7 +201,7 @@ public final <T extends Comparable<T>> List<T> getValues(long bucket, ResultBuil
201201
* Get the values for a bucket if it has been collected. If it hasn't
202202
* then returns an empty array.
203203
*/
204-
public final List<SortValue> getValues(long bucket) {
204+
public final List<SortValue> getValues(long bucket) throws IOException {
205205
return getValues(bucket, (i, sv) -> sv);
206206
}
207207

server/src/main/java/org/elasticsearch/search/sort/SortValue.java

+81-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.elasticsearch.search.sort;
2121

22+
import org.apache.lucene.util.BytesRef;
2223
import org.elasticsearch.common.io.stream.NamedWriteable;
2324
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
2425
import org.elasticsearch.common.io.stream.StreamInput;
@@ -48,13 +49,22 @@ public static SortValue from(long l) {
4849
return new LongSortValue(l);
4950
}
5051

52+
/**
53+
* Get a {@linkplain SortValue} for bytes. Callers should be sure that they
54+
* have a {@link BytesRef#deepCopyOf} of any mutable references.
55+
*/
56+
public static SortValue from(BytesRef bytes) {
57+
return new BytesSortValue(bytes);
58+
}
59+
5160
/**
5261
* Get the list of {@linkplain NamedWriteable}s that this class needs.
5362
*/
5463
public static List<NamedWriteableRegistry.Entry> namedWriteables() {
5564
return Arrays.asList(
5665
new NamedWriteableRegistry.Entry(SortValue.class, DoubleSortValue.NAME, DoubleSortValue::new),
57-
new NamedWriteableRegistry.Entry(SortValue.class, LongSortValue.NAME, LongSortValue::new));
66+
new NamedWriteableRegistry.Entry(SortValue.class, LongSortValue.NAME, LongSortValue::new),
67+
new NamedWriteableRegistry.Entry(SortValue.class, BytesSortValue.NAME, BytesSortValue::new));
5868
}
5969

6070
private SortValue() {
@@ -200,7 +210,7 @@ private static class LongSortValue extends SortValue {
200210
this.key = key;
201211
}
202212

203-
LongSortValue(StreamInput in) throws IOException {
213+
private LongSortValue(StreamInput in) throws IOException {
204214
key = in.readLong();
205215
}
206216

@@ -259,4 +269,73 @@ public Number numberValue() {
259269
return key;
260270
}
261271
}
272+
273+
private static class BytesSortValue extends SortValue {
274+
public static final String NAME = "bytes";
275+
276+
private final BytesRef key;
277+
278+
BytesSortValue(BytesRef key) {
279+
this.key = key;
280+
}
281+
282+
private BytesSortValue(StreamInput in) throws IOException {
283+
key = in.readBytesRef();
284+
}
285+
286+
@Override
287+
public void writeTo(StreamOutput out) throws IOException {
288+
out.writeBytesRef(key);
289+
}
290+
291+
@Override
292+
public String getWriteableName() {
293+
return NAME;
294+
}
295+
296+
@Override
297+
public Object getKey() {
298+
return key;
299+
}
300+
301+
@Override
302+
public String format(DocValueFormat format) {
303+
return format.format(key).toString();
304+
}
305+
306+
@Override
307+
protected XContentBuilder rawToXContent(XContentBuilder builder) throws IOException {
308+
return builder.value(key.utf8ToString());
309+
}
310+
311+
@Override
312+
protected int compareToSameType(SortValue obj) {
313+
BytesSortValue other = (BytesSortValue) obj;
314+
return key.compareTo(other.key);
315+
}
316+
317+
@Override
318+
public boolean equals(Object obj) {
319+
if (obj == null || false == getClass().equals(obj.getClass())) {
320+
return false;
321+
}
322+
BytesSortValue other = (BytesSortValue) obj;
323+
return key.equals(other.key);
324+
}
325+
326+
@Override
327+
public int hashCode() {
328+
return key.hashCode();
329+
}
330+
331+
@Override
332+
public String toString() {
333+
return key.toString();
334+
}
335+
336+
@Override
337+
public Number numberValue() {
338+
throw new UnsupportedOperationException();
339+
}
340+
}
262341
}

server/src/test/java/org/elasticsearch/search/sort/BucketedSortTestCase.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ private T build(SortOrder order, int bucketSize, double[] values) {
7373
return build(order, format, bucketSize, BucketedSort.NOOP_EXTRA_DATA, values);
7474
}
7575

76-
public final void testNeverCalled() {
76+
public final void testNeverCalled() throws IOException {
7777
SortOrder order = randomFrom(SortOrder.values());
7878
DocValueFormat format = randomFrom(DocValueFormat.RAW, DocValueFormat.BINARY, DocValueFormat.BOOLEAN);
7979
try (T sort = build(order, format, 1, BucketedSort.NOOP_EXTRA_DATA, new double[] {})) {

server/src/test/java/org/elasticsearch/search/sort/SortValueTests.java

+28-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919

2020
package org.elasticsearch.search.sort;
2121

22+
import org.apache.lucene.document.InetAddressPoint;
23+
import org.apache.lucene.util.BytesRef;
2224
import org.elasticsearch.common.Strings;
2325
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
26+
import org.elasticsearch.common.network.InetAddresses;
2427
import org.elasticsearch.common.time.DateFormatter;
2528
import org.elasticsearch.common.xcontent.ToXContentFragment;
2629
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -51,7 +54,12 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() {
5154

5255
@Override
5356
protected SortValue createTestInstance() {
54-
return randomBoolean() ? SortValue.from(randomDouble()) : SortValue.from(randomLong());
57+
switch (between(0, 2)) {
58+
case 0: return SortValue.from(randomDouble());
59+
case 1: return SortValue.from(randomLong());
60+
case 2: return SortValue.from(new BytesRef(randomAlphaOfLength(5)));
61+
default: throw new AssertionError();
62+
}
5563
}
5664

5765
@Override
@@ -81,11 +89,23 @@ public void testToXContentLong() {
8189
assertThat(toXContent(SortValue.from(1), STRICT_DATE_TIME), equalTo("{\"test\":\"1970-01-01T00:00:00.001Z\"}"));
8290
}
8391

92+
public void testToXContentBytes() {
93+
assertThat(toXContent(SortValue.from(new BytesRef("cat")), DocValueFormat.RAW), equalTo("{\"test\":\"cat\"}"));
94+
assertThat(
95+
toXContent(SortValue.from(new BytesRef(InetAddressPoint.encode(InetAddresses.forString("127.0.0.1")))), DocValueFormat.IP),
96+
equalTo("{\"test\":\"127.0.0.1\"}")
97+
);
98+
}
99+
84100
public void testCompareDifferentTypes() {
85101
assertThat(SortValue.from(1.0), lessThan(SortValue.from(1)));
86102
assertThat(SortValue.from(Double.MAX_VALUE), lessThan(SortValue.from(Long.MIN_VALUE)));
87103
assertThat(SortValue.from(1), greaterThan(SortValue.from(1.0)));
88104
assertThat(SortValue.from(Long.MIN_VALUE), greaterThan(SortValue.from(Double.MAX_VALUE)));
105+
assertThat(SortValue.from(new BytesRef("cat")), lessThan(SortValue.from(1)));
106+
assertThat(SortValue.from(1), greaterThan(SortValue.from(new BytesRef("cat"))));
107+
assertThat(SortValue.from(new BytesRef("cat")), lessThan(SortValue.from(1.0)));
108+
assertThat(SortValue.from(1.0), greaterThan(SortValue.from(new BytesRef("cat"))));
89109
}
90110

91111
public void testCompareDoubles() {
@@ -102,6 +122,13 @@ public void testCompareLongs() {
102122
assertThat(SortValue.from(r), greaterThan(SortValue.from(r - 1)));
103123
}
104124

125+
public void testBytes() {
126+
String r = randomAlphaOfLength(5);
127+
assertThat(SortValue.from(new BytesRef(r)), equalTo(SortValue.from(new BytesRef(r))));
128+
assertThat(SortValue.from(new BytesRef(r)), lessThan(SortValue.from(new BytesRef(r + "with_suffix"))));
129+
assertThat(SortValue.from(new BytesRef(r)), greaterThan(SortValue.from(new BytesRef(new byte[] {}))));
130+
}
131+
105132
public String toXContent(SortValue sortValue, DocValueFormat format) {
106133
return Strings.toString(new ToXContentFragment() {
107134
@Override

x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/AnalyticsPlugin.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ public List<AggregationSpec> getAggregations() {
114114
TopMetricsAggregationBuilder.NAME,
115115
TopMetricsAggregationBuilder::new,
116116
usage.track(AnalyticsStatsAction.Item.TOP_METRICS, checkLicense(TopMetricsAggregationBuilder.PARSER)))
117-
.addResultReader(InternalTopMetrics::new),
117+
.addResultReader(InternalTopMetrics::new)
118+
.setAggregatorRegistrar(TopMetricsAggregationBuilder::registerAggregators),
118119
new AggregationSpec(
119120
TTestAggregationBuilder.NAME,
120121
TTestAggregationBuilder::new,

x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/topmetrics/TopMetricsAggregationBuilder.java

+25
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
1919
import org.elasticsearch.search.aggregations.AggregatorFactory;
2020
import org.elasticsearch.search.aggregations.support.AggregationContext;
21+
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
2122
import org.elasticsearch.search.aggregations.support.MultiValuesSourceFieldConfig;
23+
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
24+
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry.RegistryKey;
2225
import org.elasticsearch.search.sort.SortBuilder;
2326

2427
import java.io.IOException;
@@ -34,6 +37,28 @@ public class TopMetricsAggregationBuilder extends AbstractAggregationBuilder<Top
3437
public static final String NAME = "top_metrics";
3538
public static final ParseField METRIC_FIELD = new ParseField("metrics");
3639

40+
static final RegistryKey<TopMetricsAggregator.MetricValuesSupplier> REGISTRY_KEY = new RegistryKey<>(
41+
TopMetricsAggregationBuilder.NAME,
42+
TopMetricsAggregator.MetricValuesSupplier.class
43+
);
44+
45+
public static void registerAggregators(ValuesSourceRegistry.Builder registry) {
46+
registry.registerUsage(NAME);
47+
registry.register(REGISTRY_KEY, List.of(CoreValuesSourceType.NUMERIC), TopMetricsAggregator::buildNumericMetricValues, true);
48+
registry.register(
49+
REGISTRY_KEY,
50+
List.of(CoreValuesSourceType.BOOLEAN, CoreValuesSourceType.DATE),
51+
TopMetricsAggregator.LongMetricValues::build,
52+
true
53+
);
54+
registry.register(
55+
REGISTRY_KEY,
56+
List.of(CoreValuesSourceType.BYTES, CoreValuesSourceType.IP),
57+
TopMetricsAggregator.GlobalOrdsValues::build,
58+
true
59+
);
60+
}
61+
3762
/**
3863
* Default to returning only a single top metric.
3964
*/

0 commit comments

Comments
 (0)