Skip to content

Commit 28b7af5

Browse files
refactor: expose a way of ordering explorer query by interval (#74)
1 parent 4e8c9b5 commit 28b7af5

File tree

11 files changed

+371
-28
lines changed

11 files changed

+371
-28
lines changed

hypertrace-graphql-explorer-schema/build.gradle.kts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
plugins {
22
`java-library`
3+
jacoco
4+
id("org.hypertrace.jacoco-report-plugin")
35
}
46

57
dependencies {
@@ -23,4 +25,12 @@ dependencies {
2325
implementation("org.hypertrace.core.graphql:hypertrace-core-graphql-attribute-store")
2426
implementation("org.hypertrace.core.graphql:hypertrace-core-graphql-deserialization")
2527
implementation("org.hypertrace.core.graphql:hypertrace-core-graphql-schema-utils")
28+
29+
testImplementation("org.junit.jupiter:junit-jupiter")
30+
testImplementation("org.mockito:mockito-core")
31+
testImplementation("org.mockito:mockito-junit-jupiter")
32+
}
33+
34+
tasks.test {
35+
useJUnitPlatform()
2636
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.hypertrace.graphql.explorer.dao;
2+
3+
import io.reactivex.rxjava3.core.Maybe;
4+
import io.reactivex.rxjava3.core.Observable;
5+
import io.reactivex.rxjava3.core.Single;
6+
import java.util.List;
7+
import java.util.stream.Collectors;
8+
import javax.inject.Inject;
9+
import lombok.extern.slf4j.Slf4j;
10+
import org.hypertrace.core.graphql.common.request.AttributeAssociation;
11+
import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderDirection;
12+
import org.hypertrace.core.graphql.common.utils.Converter;
13+
import org.hypertrace.gateway.service.v1.common.ColumnIdentifier;
14+
import org.hypertrace.gateway.service.v1.common.Expression;
15+
import org.hypertrace.gateway.service.v1.common.OrderByExpression;
16+
import org.hypertrace.gateway.service.v1.common.SortOrder;
17+
import org.hypertrace.gateway.service.v1.explore.ColumnName;
18+
import org.hypertrace.graphql.explorer.request.ExploreOrderArgument;
19+
import org.hypertrace.graphql.metric.schema.argument.AggregatableOrderArgument;
20+
21+
@Slf4j
22+
class GatewayServiceExploreOrderArgumentConverter
23+
implements Converter<List<ExploreOrderArgument>, List<OrderByExpression>> {
24+
25+
private final Converter<
26+
List<AttributeAssociation<AggregatableOrderArgument>>, List<OrderByExpression>>
27+
attributeOrderConverter;
28+
private final Converter<OrderDirection, SortOrder> sortOrderConverter;
29+
30+
@Inject
31+
GatewayServiceExploreOrderArgumentConverter(
32+
Converter<List<AttributeAssociation<AggregatableOrderArgument>>, List<OrderByExpression>>
33+
attributeOrderConverter,
34+
Converter<OrderDirection, SortOrder> sortOrderConverter) {
35+
this.attributeOrderConverter = attributeOrderConverter;
36+
this.sortOrderConverter = sortOrderConverter;
37+
}
38+
39+
@Override
40+
public Single<List<OrderByExpression>> convert(List<ExploreOrderArgument> arguments) {
41+
return Observable.fromIterable(arguments)
42+
.concatMapMaybe(this::convert)
43+
.collect(Collectors.toUnmodifiableList());
44+
}
45+
46+
private Maybe<OrderByExpression> convert(ExploreOrderArgument argument) {
47+
switch (argument.type()) {
48+
case ATTRIBUTE:
49+
return this.buildAttributeOrderExpression(argument);
50+
case INTERVAL_START:
51+
return this.buildIntervalStartOrderExpression(argument);
52+
default:
53+
log.error("Unrecognized order argument type: {}", argument);
54+
return Maybe.empty();
55+
}
56+
}
57+
58+
private Maybe<OrderByExpression> buildAttributeOrderExpression(ExploreOrderArgument argument) {
59+
return Maybe.fromOptional(argument.attribute())
60+
.doOnComplete(() -> log.error("Attribute order argument missing attribute: {} ", argument))
61+
.flatMapSingle(
62+
attribute ->
63+
this.attributeOrderConverter.convert(
64+
List.of(AttributeAssociation.of(attribute, argument.argument()))))
65+
.mapOptional(list -> list.stream().findFirst());
66+
}
67+
68+
private Maybe<OrderByExpression> buildIntervalStartOrderExpression(
69+
ExploreOrderArgument argument) {
70+
return this.sortOrderConverter
71+
.convert(argument.argument().direction())
72+
.map(this::buildIntervalStartOrderExpression)
73+
.toMaybe();
74+
}
75+
76+
private OrderByExpression buildIntervalStartOrderExpression(SortOrder sortOrder) {
77+
return OrderByExpression.newBuilder()
78+
.setOrder(sortOrder)
79+
.setExpression(
80+
Expression.newBuilder()
81+
.setColumnIdentifier(
82+
ColumnIdentifier.newBuilder()
83+
.setColumnName(ColumnName.INTERVAL_START_TIME.name())))
84+
.build();
85+
}
86+
}

hypertrace-graphql-explorer-schema/src/main/java/org/hypertrace/graphql/explorer/dao/GatewayServiceExploreRequestBuilder.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.time.Duration;
99
import java.util.Collection;
1010
import java.util.Collections;
11-
import java.util.List;
1211
import java.util.Set;
1312
import java.util.stream.Collectors;
1413
import javax.inject.Inject;
@@ -21,19 +20,15 @@
2120
import org.hypertrace.core.graphql.common.utils.Converter;
2221
import org.hypertrace.gateway.service.v1.common.Expression;
2322
import org.hypertrace.gateway.service.v1.common.Filter;
24-
import org.hypertrace.gateway.service.v1.common.OrderByExpression;
2523
import org.hypertrace.gateway.service.v1.common.TimeAggregation;
2624
import org.hypertrace.graphql.explorer.request.ExploreRequest;
2725
import org.hypertrace.graphql.explorer.schema.argument.IntervalArgument;
2826
import org.hypertrace.graphql.metric.request.MetricAggregationRequest;
2927
import org.hypertrace.graphql.metric.request.MetricSeriesRequest;
30-
import org.hypertrace.graphql.metric.schema.argument.AggregatableOrderArgument;
3128

3229
public class GatewayServiceExploreRequestBuilder {
3330
private final Converter<Collection<AttributeAssociation<FilterArgument>>, Filter> filterConverter;
34-
private final Converter<
35-
List<AttributeAssociation<AggregatableOrderArgument>>, List<OrderByExpression>>
36-
orderConverter;
31+
private final GatewayServiceExploreOrderArgumentConverter orderConverter;
3732
private final Converter<Collection<AttributeRequest>, Set<Expression>> attributeConverter;
3833
private final Converter<Collection<MetricAggregationRequest>, Set<Expression>>
3934
aggregationConverter;
@@ -42,8 +37,7 @@ public class GatewayServiceExploreRequestBuilder {
4237
@Inject
4338
GatewayServiceExploreRequestBuilder(
4439
Converter<Collection<AttributeAssociation<FilterArgument>>, Filter> filterConverter,
45-
Converter<List<AttributeAssociation<AggregatableOrderArgument>>, List<OrderByExpression>>
46-
orderConverter,
40+
GatewayServiceExploreOrderArgumentConverter orderConverter,
4741
Converter<Collection<AttributeRequest>, Set<Expression>> attributeConverter,
4842
Converter<Collection<MetricAggregationRequest>, Set<Expression>> aggregationConverter,
4943
Converter<Collection<MetricSeriesRequest>, Set<TimeAggregation>> seriesConverter) {

hypertrace-graphql-explorer-schema/src/main/java/org/hypertrace/graphql/explorer/request/DefaultExploreRequestBuilder.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.hypertrace.core.graphql.common.request.FilterRequestBuilder;
2222
import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument;
2323
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
24-
import org.hypertrace.core.graphql.common.schema.results.arguments.order.OrderArgument;
2524
import org.hypertrace.core.graphql.common.schema.results.arguments.page.LimitArgument;
2625
import org.hypertrace.core.graphql.common.schema.results.arguments.page.OffsetArgument;
2726
import org.hypertrace.core.graphql.common.schema.results.arguments.space.SpaceArgument;
@@ -47,6 +46,7 @@ class DefaultExploreRequestBuilder implements ExploreRequestBuilder {
4746
private final ExploreSelectionRequestBuilder selectionRequestBuilder;
4847
private final FilterRequestBuilder filterRequestBuilder;
4948
private final AttributeScopeStringTranslator scopeStringTranslator;
49+
private final ExploreOrderArgumentBuilder exploreOrderArgumentBuilder;
5050

5151
@Inject
5252
DefaultExploreRequestBuilder(
@@ -55,13 +55,15 @@ class DefaultExploreRequestBuilder implements ExploreRequestBuilder {
5555
AttributeAssociator attributeAssociator,
5656
ExploreSelectionRequestBuilder selectionRequestBuilder,
5757
FilterRequestBuilder filterRequestBuilder,
58-
AttributeScopeStringTranslator scopeStringTranslator) {
58+
AttributeScopeStringTranslator scopeStringTranslator,
59+
ExploreOrderArgumentBuilder exploreOrderArgumentBuilder) {
5960
this.attributeRequestBuilder = attributeRequestBuilder;
6061
this.argumentDeserializer = argumentDeserializer;
6162
this.attributeAssociator = attributeAssociator;
6263
this.selectionRequestBuilder = selectionRequestBuilder;
6364
this.filterRequestBuilder = filterRequestBuilder;
6465
this.scopeStringTranslator = scopeStringTranslator;
66+
this.exploreOrderArgumentBuilder = exploreOrderArgumentBuilder;
6567
}
6668

6769
@Override
@@ -135,18 +137,16 @@ private Single<ExploreRequest> build(
135137
this.selectionRequestBuilder.getAggregationSelections(
136138
requestContext, explorerScope, selectionSet);
137139

138-
Single<List<AttributeAssociation<AggregatableOrderArgument>>> orderSingle =
139-
this.attributeAssociator
140-
.associateAttributes(requestContext, explorerScope, requestedOrders, OrderArgument::key)
141-
.collect(Collectors.toUnmodifiableList());
140+
Single<List<ExploreOrderArgument>> orderArguments =
141+
this.exploreOrderArgumentBuilder.buildList(requestContext, explorerScope, requestedOrders);
142142

143143
Single<List<AttributeAssociation<FilterArgument>>> filterSingle =
144144
this.filterRequestBuilder.build(requestContext, explorerScope, requestedFilters);
145145

146146
return zip(
147147
attributeSelections,
148148
aggregationSelections,
149-
orderSingle,
149+
orderArguments,
150150
filterSingle,
151151
this.buildGroupByAttributes(requestContext, explorerScope, groupByKeys),
152152
(attributes, aggregations, orders, filters, groupByAttribute) ->
@@ -183,7 +183,7 @@ private static class DefaultExploreRequest implements ExploreRequest {
183183
int offset;
184184
Set<AttributeRequest> attributeRequests;
185185
Set<MetricAggregationRequest> aggregationRequests;
186-
List<AttributeAssociation<AggregatableOrderArgument>> orderArguments;
186+
List<ExploreOrderArgument> orderArguments;
187187
List<AttributeAssociation<FilterArgument>> filterArguments;
188188
Set<AttributeRequest> groupByAttributeRequests;
189189
Optional<IntervalArgument> timeInterval;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.hypertrace.graphql.explorer.request;
2+
3+
import java.util.Optional;
4+
import org.hypertrace.core.graphql.attributes.AttributeModel;
5+
import org.hypertrace.graphql.metric.schema.argument.AggregatableOrderArgument;
6+
7+
public interface ExploreOrderArgument {
8+
9+
ExploreOrderArgumentType type();
10+
11+
AggregatableOrderArgument argument();
12+
13+
Optional<AttributeModel> attribute();
14+
15+
enum ExploreOrderArgumentType {
16+
ATTRIBUTE,
17+
INTERVAL_START
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.hypertrace.graphql.explorer.request;
2+
3+
import io.reactivex.rxjava3.core.Observable;
4+
import io.reactivex.rxjava3.core.Single;
5+
import java.util.List;
6+
import java.util.Optional;
7+
import java.util.stream.Collectors;
8+
import javax.inject.Inject;
9+
import lombok.Value;
10+
import lombok.experimental.Accessors;
11+
import org.hypertrace.core.graphql.attributes.AttributeModel;
12+
import org.hypertrace.core.graphql.attributes.AttributeStore;
13+
import org.hypertrace.core.graphql.context.GraphQlRequestContext;
14+
import org.hypertrace.graphql.explorer.schema.ExploreResult;
15+
import org.hypertrace.graphql.metric.schema.argument.AggregatableOrderArgument;
16+
17+
class ExploreOrderArgumentBuilder {
18+
19+
private final AttributeStore attributeStore;
20+
21+
@Inject
22+
ExploreOrderArgumentBuilder(AttributeStore attributeStore) {
23+
this.attributeStore = attributeStore;
24+
}
25+
26+
Single<List<ExploreOrderArgument>> buildList(
27+
GraphQlRequestContext context,
28+
String requestScope,
29+
List<AggregatableOrderArgument> providedOrders) {
30+
31+
return Observable.fromIterable(providedOrders)
32+
.concatMapSingle(
33+
argument -> this.buildExploreOrderArgument(context, requestScope, argument))
34+
.collect(Collectors.toUnmodifiableList());
35+
}
36+
37+
private Single<ExploreOrderArgument> buildExploreOrderArgument(
38+
GraphQlRequestContext requestContext, String scope, AggregatableOrderArgument argument) {
39+
40+
if (ExploreResult.EXPLORE_RESULT_INTERVAL_START_KEY.equals(argument.key())) {
41+
return this.buildIntervalStartExploreOrderArgument(argument);
42+
}
43+
return this.buildAttributeExploreOrderArgument(requestContext, scope, argument);
44+
}
45+
46+
private Single<ExploreOrderArgument> buildIntervalStartExploreOrderArgument(
47+
AggregatableOrderArgument argument) {
48+
return Single.just(new IntervalStartExploreOrderArgument(argument));
49+
}
50+
51+
private Single<ExploreOrderArgument> buildAttributeExploreOrderArgument(
52+
GraphQlRequestContext requestContext, String scope, AggregatableOrderArgument argument) {
53+
return this.attributeStore
54+
.get(requestContext, scope, argument.key())
55+
.map(model -> new AttributeExploreOrderArgument(argument, Optional.of(model)));
56+
}
57+
58+
@Value
59+
@Accessors(fluent = true)
60+
private static class IntervalStartExploreOrderArgument implements ExploreOrderArgument {
61+
ExploreOrderArgumentType type = ExploreOrderArgumentType.INTERVAL_START;
62+
AggregatableOrderArgument argument;
63+
Optional<AttributeModel> attribute = Optional.empty();
64+
}
65+
66+
@Value
67+
@Accessors(fluent = true)
68+
private static class AttributeExploreOrderArgument implements ExploreOrderArgument {
69+
ExploreOrderArgumentType type = ExploreOrderArgumentType.ATTRIBUTE;
70+
AggregatableOrderArgument argument;
71+
Optional<AttributeModel> attribute;
72+
}
73+
}

hypertrace-graphql-explorer-schema/src/main/java/org/hypertrace/graphql/explorer/request/ExploreRequest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import org.hypertrace.core.graphql.context.GraphQlRequestContext;
1111
import org.hypertrace.graphql.explorer.schema.argument.IntervalArgument;
1212
import org.hypertrace.graphql.metric.request.MetricAggregationRequest;
13-
import org.hypertrace.graphql.metric.schema.argument.AggregatableOrderArgument;
1413

1514
public interface ExploreRequest {
1615
GraphQlRequestContext requestContext();
@@ -29,7 +28,7 @@ public interface ExploreRequest {
2928

3029
Optional<IntervalArgument> timeInterval();
3130

32-
List<AttributeAssociation<AggregatableOrderArgument>> orderArguments();
31+
List<ExploreOrderArgument> orderArguments();
3332

3433
List<AttributeAssociation<FilterArgument>> filterArguments();
3534

0 commit comments

Comments
 (0)