Skip to content

Commit 009e884

Browse files
authored
chore | adding the support of edge filters in edges (#167)
* chore | adding the support of edge filters in edges
1 parent cd79b26 commit 009e884

File tree

11 files changed

+201
-17
lines changed

11 files changed

+201
-17
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.hypertrace.graphql.entity.dao;
2+
3+
import graphql.schema.DataFetcher;
4+
import graphql.schema.DataFetchingEnvironment;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.Optional;
9+
import java.util.concurrent.CompletableFuture;
10+
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
11+
import org.hypertrace.core.graphql.deserialization.ArgumentDeserializer;
12+
import org.hypertrace.graphql.entity.schema.EdgeResultSet;
13+
import org.hypertrace.graphql.entity.schema.Entity;
14+
import org.hypertrace.graphql.entity.schema.EntityType;
15+
import org.hypertrace.graphql.entity.schema.argument.NeighborEntityScopeArgument;
16+
import org.hypertrace.graphql.entity.schema.argument.NeighborEntityTypeArgument;
17+
18+
abstract class EdgesDataFetcherImpl implements DataFetcher<CompletableFuture<EdgeResultSet>> {
19+
20+
private final ArgumentDeserializer argumentDeserializer;
21+
22+
protected abstract EdgeResultSet getEdges(
23+
Entity entity, EntityType neighborType, String neighborScope, List<FilterArgument> filterBy);
24+
25+
EdgesDataFetcherImpl(ArgumentDeserializer argumentDeserializer) {
26+
this.argumentDeserializer = argumentDeserializer;
27+
}
28+
29+
@Override
30+
public CompletableFuture<EdgeResultSet> get(DataFetchingEnvironment environment) {
31+
// TODO: Edges when used with filters have two limitations for now
32+
// 1. We can only have one entityType incoming/outgoing edge
33+
// 2. If different filters for same entity type, filters will be merged into one AND filter.
34+
//
35+
// We are passing the empty filter as argument to query the edges.
36+
// This will work for now because we have restricted multiple edges selection in a request
37+
// Fix this when we will solve the above two limitations.
38+
return CompletableFuture.completedFuture(
39+
getEdges(
40+
environment.getSource(),
41+
getNeighborEntityType(environment.getArguments()).orElse(null),
42+
getNeighborEntityScope(environment.getArguments()).orElse(null),
43+
Collections.emptyList()));
44+
}
45+
46+
private Optional<String> getNeighborEntityScope(Map<String, Object> arguments) {
47+
return this.argumentDeserializer.deserializePrimitive(
48+
arguments, NeighborEntityScopeArgument.class);
49+
}
50+
51+
private Optional<EntityType> getNeighborEntityType(Map<String, Object> arguments) {
52+
return this.argumentDeserializer.deserializePrimitive(
53+
arguments, NeighborEntityTypeArgument.class);
54+
}
55+
}

hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityConverter.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import lombok.experimental.Accessors;
1717
import org.hypertrace.core.graphql.common.request.AttributeRequest;
1818
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
19+
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
1920
import org.hypertrace.core.graphql.common.utils.BiConverter;
2021
import org.hypertrace.core.graphql.common.utils.TriConverter;
2122
import org.hypertrace.gateway.service.v1.baseline.BaselineEntitiesResponse;
@@ -33,6 +34,7 @@
3334
import org.hypertrace.graphql.metric.schema.MetricContainer;
3435

3536
class GatewayServiceEntityConverter {
37+
3638
private final BiConverter<
3739
Collection<AttributeRequest>, Map<String, Value>, Map<AttributeExpression, Object>>
3840
attributeMapConverter;
@@ -143,6 +145,7 @@ private Single<Entity> convertEntity(
143145
@lombok.Value
144146
@Accessors(fluent = true)
145147
private static class ConvertedEntity implements Entity {
148+
146149
String id;
147150
String type;
148151
Map<AttributeExpression, Object> attributeValues;
@@ -161,13 +164,15 @@ public MetricContainer metric(AttributeExpression attributeExpression) {
161164
}
162165

163166
@Override
164-
public EdgeResultSet incomingEdges(EntityType neighborType, String neighborScope) {
167+
public EdgeResultSet incomingEdges(
168+
EntityType neighborType, String neighborScope, List<FilterArgument> filterBy) {
165169
return this.incomingEdges.getOrDefault(
166170
this.resolveEntityScope(neighborType, neighborScope), EMPTY_EDGE_RESULT_SET);
167171
}
168172

169173
@Override
170-
public EdgeResultSet outgoingEdges(EntityType neighborType, String neighborScope) {
174+
public EdgeResultSet outgoingEdges(
175+
EntityType neighborType, String neighborScope, List<FilterArgument> filterBy) {
171176

172177
return this.outgoingEdges.getOrDefault(
173178
this.resolveEntityScope(neighborType, neighborScope), EMPTY_EDGE_RESULT_SET);
@@ -188,6 +193,7 @@ private String resolveEntityScope(
188193
@lombok.Value
189194
@Accessors(fluent = true)
190195
private static class ConvertedEntityResultSet implements EntityResultSet {
196+
191197
List<Entity> results;
192198
long total;
193199
long count;

hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityInteractionRequestBuilder.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.Collection;
88
import java.util.Set;
99
import java.util.stream.Collectors;
10+
import java.util.stream.Stream;
1011
import javax.inject.Inject;
1112
import lombok.Value;
1213
import lombok.experimental.Accessors;
@@ -25,6 +26,7 @@
2526
import org.hypertrace.graphql.metric.request.MetricAggregationRequest;
2627

2728
class GatewayServiceEntityInteractionRequestBuilder {
29+
2830
private static final Integer DEFAULT_INTERACTION_LIMIT = 1000;
2931
private final Converter<Collection<AttributeRequest>, Set<Expression>> selectionConverter;
3032
private final Converter<Collection<MetricAggregationRequest>, Set<Expression>>
@@ -48,7 +50,7 @@ Single<InteractionsRequest> build(EdgeSetGroupRequest edgeSetRequestGroup) {
4850

4951
return zip(
5052
this.collectSelectionsAndAggregations(edgeSetRequestGroup),
51-
this.buildEntityTypeFilter(edgeSetRequestGroup),
53+
this.buildEntityInteractionFilter(edgeSetRequestGroup),
5254
(selections, filter) ->
5355
InteractionsRequest.newBuilder()
5456
.addAllSelection(selections)
@@ -66,8 +68,8 @@ private Single<Set<Expression>> collectSelectionsAndAggregations(EdgeSetGroupReq
6668
.collect(Collectors.toUnmodifiableSet());
6769
}
6870

69-
private Single<Filter> buildEntityTypeFilter(EdgeSetGroupRequest request) {
70-
return Observable.fromIterable(request.entityTypes())
71+
private Single<Filter> buildEntityInteractionFilter(EdgeSetGroupRequest request) {
72+
return Observable.fromIterable(request.entityTypes()) // add entity types filter
7173
.collect(Collectors.toUnmodifiableSet())
7274
.map(
7375
entityTypes ->
@@ -76,7 +78,13 @@ private Single<Filter> buildEntityTypeFilter(EdgeSetGroupRequest request) {
7678
new EntityNeighborTypeFilter(
7779
request.neighborTypeAttribute().attributeExpressionAssociation().value(),
7880
entityTypes)))
79-
.flatMap(filterAssociation -> this.filterConverter.convert(Set.of(filterAssociation)));
81+
.flatMap(
82+
filterAssociation ->
83+
this.filterConverter.convert(
84+
Stream.concat(
85+
request.filterArguments().stream(), // add all other filters
86+
Stream.of(filterAssociation))
87+
.collect(Collectors.toUnmodifiableSet())));
8088
}
8189

8290
@Value
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.hypertrace.graphql.entity.dao;
2+
3+
import java.util.List;
4+
import javax.inject.Inject;
5+
import org.hypertrace.core.graphql.common.fetcher.InjectableDataFetcher;
6+
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
7+
import org.hypertrace.core.graphql.deserialization.ArgumentDeserializer;
8+
import org.hypertrace.graphql.entity.schema.EdgeResultSet;
9+
import org.hypertrace.graphql.entity.schema.Entity;
10+
import org.hypertrace.graphql.entity.schema.EntityType;
11+
12+
public class IncomingEdgesDataFetcher extends InjectableDataFetcher<EdgeResultSet> {
13+
public IncomingEdgesDataFetcher() {
14+
super(IncomingEdgesDataFetcherImpl.class);
15+
}
16+
17+
private static class IncomingEdgesDataFetcherImpl extends EdgesDataFetcherImpl {
18+
19+
@Inject
20+
IncomingEdgesDataFetcherImpl(ArgumentDeserializer argumentDeserializer) {
21+
super(argumentDeserializer);
22+
}
23+
24+
@Override
25+
protected EdgeResultSet getEdges(
26+
Entity entity,
27+
EntityType neighborType,
28+
String neighborScope,
29+
List<FilterArgument> filterBy) {
30+
return entity.incomingEdges(neighborType, neighborScope, filterBy);
31+
}
32+
}
33+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.hypertrace.graphql.entity.dao;
2+
3+
import java.util.List;
4+
import javax.inject.Inject;
5+
import org.hypertrace.core.graphql.common.fetcher.InjectableDataFetcher;
6+
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
7+
import org.hypertrace.core.graphql.deserialization.ArgumentDeserializer;
8+
import org.hypertrace.graphql.entity.schema.EdgeResultSet;
9+
import org.hypertrace.graphql.entity.schema.Entity;
10+
import org.hypertrace.graphql.entity.schema.EntityType;
11+
12+
public class OutgoingEdgesDataFetcher extends InjectableDataFetcher<EdgeResultSet> {
13+
14+
public OutgoingEdgesDataFetcher() {
15+
super(OutgoingEdgesDataFetcherImpl.class);
16+
}
17+
18+
private static class OutgoingEdgesDataFetcherImpl extends EdgesDataFetcherImpl {
19+
20+
@Inject
21+
OutgoingEdgesDataFetcherImpl(ArgumentDeserializer argumentDeserializer) {
22+
super(argumentDeserializer);
23+
}
24+
25+
@Override
26+
protected EdgeResultSet getEdges(
27+
Entity entity,
28+
EntityType neighborType,
29+
String neighborScope,
30+
List<FilterArgument> filterBy) {
31+
return entity.outgoingEdges(neighborType, neighborScope, filterBy);
32+
}
33+
}
34+
}

hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/joiner/DefaultEntityJoinerBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ private static class EmptyEdgeSetGroupRequest implements EdgeSetGroupRequest {
320320
Set<String> entityTypes = Collections.emptySet();
321321
Collection<AttributeRequest> attributeRequests = Collections.emptyList();
322322
Collection<MetricAggregationRequest> metricAggregationRequests = Collections.emptyList();
323+
Collection<AttributeAssociation<FilterArgument>> filterArguments = Collections.emptyList();
323324
AttributeRequest neighborIdAttribute = null;
324325
AttributeRequest neighborTypeAttribute = null;
325326

hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeRequestBuilder.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static io.reactivex.rxjava3.core.Single.zip;
44

55
import graphql.schema.SelectedField;
6+
import io.grpc.Status;
67
import io.reactivex.rxjava3.core.Single;
78
import java.util.Collection;
89
import java.util.List;
@@ -16,11 +17,14 @@
1617
import javax.inject.Provider;
1718
import lombok.Value;
1819
import lombok.experimental.Accessors;
20+
import org.hypertrace.core.graphql.common.request.AttributeAssociation;
1921
import org.hypertrace.core.graphql.common.request.AttributeRequest;
2022
import org.hypertrace.core.graphql.common.request.AttributeRequestBuilder;
23+
import org.hypertrace.core.graphql.common.request.FilterRequestBuilder;
2124
import org.hypertrace.core.graphql.common.schema.arguments.TimeRangeArgument;
2225
import org.hypertrace.core.graphql.common.schema.attributes.arguments.AttributeExpression;
2326
import org.hypertrace.core.graphql.common.schema.results.ResultSet;
27+
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
2428
import org.hypertrace.core.graphql.context.GraphQlRequestContext;
2529
import org.hypertrace.core.graphql.deserialization.ArgumentDeserializer;
2630
import org.hypertrace.core.graphql.utils.schema.GraphQlSelectionFinder;
@@ -33,13 +37,15 @@
3337
import org.hypertrace.graphql.metric.request.MetricAggregationRequestBuilder;
3438

3539
class EdgeRequestBuilder {
40+
3641
private final String INCOMING_ENTITY_ID_KEY = "fromEntityId";
3742
private final String INCOMING_ENTITY_TYPE_KEY = "fromEntityType";
3843
private final String OUTGOING_ENTITY_ID_KEY = "toEntityId";
3944
private final String OUTGOING_ENTITY_TYPE_KEY = "toEntityType";
4045
private final ArgumentDeserializer argumentDeserializer;
4146
private final GraphQlSelectionFinder selectionFinder;
4247
private final MetricAggregationRequestBuilder metricAggregationRequestBuilder;
48+
private final FilterRequestBuilder filterRequestBuilder;
4349
private final AttributeRequestBuilder attributeRequestBuilder;
4450
// Use provider to avoid cycle
4551
private final Provider<NeighborEntitiesRequestBuilder> neighborEntitiesRequestBuilderProvider;
@@ -49,40 +55,49 @@ class EdgeRequestBuilder {
4955
ArgumentDeserializer argumentDeserializer,
5056
GraphQlSelectionFinder selectionFinder,
5157
MetricAggregationRequestBuilder metricAggregationRequestBuilder,
58+
FilterRequestBuilder filterRequestBuilder,
5259
AttributeRequestBuilder attributeRequestBuilder,
5360
Provider<NeighborEntitiesRequestBuilder> neighborEntitiesRequestBuilderProvider) {
5461
this.argumentDeserializer = argumentDeserializer;
5562
this.selectionFinder = selectionFinder;
5663
this.metricAggregationRequestBuilder = metricAggregationRequestBuilder;
5764
this.attributeRequestBuilder = attributeRequestBuilder;
5865
this.neighborEntitiesRequestBuilderProvider = neighborEntitiesRequestBuilderProvider;
66+
this.filterRequestBuilder = filterRequestBuilder;
5967
}
6068

6169
Single<EdgeSetGroupRequest> buildIncomingEdgeRequest(
6270
GraphQlRequestContext context,
6371
TimeRangeArgument timeRange,
6472
Optional<String> space,
6573
Stream<SelectedField> edgeSetFields) {
66-
return this.buildEdgeRequest(
67-
context, timeRange, space, this.getEdgesByType(edgeSetFields), EdgeType.INCOMING);
74+
return this.buildEdgeRequest(context, timeRange, space, edgeSetFields, EdgeType.INCOMING);
6875
}
6976

7077
Single<EdgeSetGroupRequest> buildOutgoingEdgeRequest(
7178
GraphQlRequestContext context,
7279
TimeRangeArgument timeRange,
7380
Optional<String> space,
7481
Stream<SelectedField> edgeSetFields) {
75-
return this.buildEdgeRequest(
76-
context, timeRange, space, this.getEdgesByType(edgeSetFields), EdgeType.OUTGOING);
82+
return this.buildEdgeRequest(context, timeRange, space, edgeSetFields, EdgeType.OUTGOING);
7783
}
7884

7985
private Single<EdgeSetGroupRequest> buildEdgeRequest(
8086
GraphQlRequestContext context,
8187
TimeRangeArgument timeRange,
8288
Optional<String> space,
83-
Map<String, Set<SelectedField>> edgesByType,
89+
Stream<SelectedField> edgeSetFields,
8490
EdgeType edgeType) {
91+
Set<SelectedField> edgeFields = edgeSetFields.collect(Collectors.toUnmodifiableSet());
92+
List<FilterArgument> filterArguments = this.getFilters(edgeFields);
93+
94+
if (!filterArguments.isEmpty() && edgeFields.size() > 1) {
95+
throw Status.UNIMPLEMENTED
96+
.withDescription("Cannot specify more than one edge type with edge filters")
97+
.asRuntimeException();
98+
}
8599

100+
Map<String, Set<SelectedField>> edgesByType = this.getEdgesByType(edgeFields.stream());
86101
Set<SelectedField> allEdges =
87102
edgesByType.values().stream()
88103
.flatMap(Collection::stream)
@@ -94,7 +109,9 @@ private Single<EdgeSetGroupRequest> buildEdgeRequest(
94109
this.getNeighborTypeAttribute(context, edgeType),
95110
this.metricAggregationRequestBuilder.build(
96111
context, HypertraceAttributeScopeString.INTERACTION, allEdges.stream()),
97-
(attributeRequests, neighborIdRequest, neighborTypeRequest, metricRequests) ->
112+
this.filterRequestBuilder.build(
113+
context, HypertraceAttributeScopeString.INTERACTION, filterArguments),
114+
(attributeRequests, neighborIdRequest, neighborTypeRequest, metricRequests, filters) ->
98115
new DefaultEdgeSetGroupRequest(
99116
edgesByType.keySet(),
100117
attributeRequests,
@@ -110,7 +127,8 @@ private Single<EdgeSetGroupRequest> buildEdgeRequest(
110127
timeRange,
111128
space,
112129
neighborIds,
113-
edgesByType.get(entityType))));
130+
edgesByType.get(entityType)),
131+
filters));
114132
}
115133

116134
private Map<String, Set<SelectedField>> getEdgesByType(Stream<SelectedField> edgeSetStream) {
@@ -165,6 +183,17 @@ private Single<AttributeRequest> getNeighborIdAttribute(
165183
}
166184
}
167185

186+
private List<FilterArgument> getFilters(Set<SelectedField> selectedFields) {
187+
return selectedFields.stream()
188+
.map(
189+
selectedField ->
190+
this.argumentDeserializer.deserializeObjectList(
191+
selectedField.getArguments(), FilterArgument.class))
192+
.flatMap(Optional::stream)
193+
.flatMap(Collection::stream)
194+
.collect(Collectors.toUnmodifiableList());
195+
}
196+
168197
private Single<AttributeRequest> getNeighborTypeAttribute(
169198
GraphQlRequestContext context, EdgeType edgeType) {
170199
switch (edgeType) {
@@ -191,12 +220,14 @@ private enum EdgeType {
191220
@Value
192221
@Accessors(fluent = true)
193222
private static class DefaultEdgeSetGroupRequest implements EdgeSetGroupRequest {
223+
194224
Set<String> entityTypes;
195225
Collection<AttributeRequest> attributeRequests;
196226
Collection<MetricAggregationRequest> metricAggregationRequests;
197227
AttributeRequest neighborIdAttribute;
198228
AttributeRequest neighborTypeAttribute;
199229
BiFunction<String, Collection<String>, Single<EntityRequest>> neighborRequestBuilder;
230+
Collection<AttributeAssociation<FilterArgument>> filterArguments;
200231

201232
@Override
202233
public Single<EntityRequest> buildNeighborRequest(

hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetGroupRequest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import io.reactivex.rxjava3.core.Single;
44
import java.util.Collection;
55
import java.util.Set;
6+
import org.hypertrace.core.graphql.common.request.AttributeAssociation;
67
import org.hypertrace.core.graphql.common.request.AttributeRequest;
8+
import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument;
79
import org.hypertrace.graphql.metric.request.MetricAggregationRequest;
810

911
public interface EdgeSetGroupRequest {
@@ -20,4 +22,6 @@ public interface EdgeSetGroupRequest {
2022
AttributeRequest neighborTypeAttribute();
2123

2224
Single<EntityRequest> buildNeighborRequest(String entityType, Collection<String> neighborIds);
25+
26+
Collection<AttributeAssociation<FilterArgument>> filterArguments();
2327
}

0 commit comments

Comments
 (0)