diff --git a/hypertrace-core-graphql b/hypertrace-core-graphql index 6f68e95d..beeb208f 160000 --- a/hypertrace-core-graphql +++ b/hypertrace-core-graphql @@ -1 +1 @@ -Subproject commit 6f68e95d677dd06da2e30bc25418a09ce0596b4c +Subproject commit beeb208f19cf46b7e7f001e59abdf7be18209e44 diff --git a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityEdgeFetcher.java b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityEdgeFetcher.java index ae723f78..ba729000 100644 --- a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityEdgeFetcher.java +++ b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityEdgeFetcher.java @@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; class GatewayServiceEntityEdgeFetcher { + private static final Logger LOG = LoggerFactory.getLogger(GatewayServiceEntityEdgeFetcher.class); static final EdgeResultSet EMPTY_EDGE_RESULT_SET = new ConvertedEdgeResultSet(List.of()); @@ -119,9 +120,14 @@ private Maybe buildEdge( return zip( this.attributeMapConverter.convert( - edgeSetGroupRequest.attributeRequests(), response.getAttributeMap()), + edgeSetGroupRequest.edgeSetRequests().get(neighbor.type()).attributeRequests(), + response.getAttributeMap()), this.baselineMetricAggregationContainerMapConverter.convert( - edgeSetGroupRequest.metricAggregationRequests(), response.getMetricsMap()), + edgeSetGroupRequest + .edgeSetRequests() + .get(neighbor.type()) + .metricAggregationRequests(), + response.getMetricsMap()), (attributes, metrics) -> (Edge) new ConvertedEdge(neighbor, attributes, metrics)) .toMaybe(); } @@ -129,6 +135,7 @@ private Maybe buildEdge( @lombok.Value @Accessors(fluent = true) private static class ConvertedEdge implements Edge { + Entity neighbor; Map attributeValues; Map metricContainers; @@ -147,6 +154,7 @@ public BaselinedMetricAggregationContainer metric(AttributeExpression attributeE @lombok.Value @Accessors(fluent = true) private static class ConvertedEdgeResultSet implements EdgeResultSet { + List results; @Override diff --git a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityInteractionRequestBuilder.java b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityInteractionRequestBuilder.java index d44cf876..f71a3ec1 100644 --- a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityInteractionRequestBuilder.java +++ b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/dao/GatewayServiceEntityInteractionRequestBuilder.java @@ -21,8 +21,10 @@ import org.hypertrace.core.graphql.common.utils.Converter; import org.hypertrace.gateway.service.v1.common.Expression; import org.hypertrace.gateway.service.v1.common.Filter; +import org.hypertrace.gateway.service.v1.common.Operator; import org.hypertrace.gateway.service.v1.entity.InteractionsRequest; import org.hypertrace.graphql.entity.request.EdgeSetGroupRequest; +import org.hypertrace.graphql.entity.request.EdgeSetRequest; import org.hypertrace.graphql.metric.request.MetricAggregationRequest; class GatewayServiceEntityInteractionRequestBuilder { @@ -44,7 +46,7 @@ class GatewayServiceEntityInteractionRequestBuilder { } Single build(EdgeSetGroupRequest edgeSetRequestGroup) { - if (edgeSetRequestGroup.entityTypes().isEmpty()) { + if (edgeSetRequestGroup.edgeSetRequests().isEmpty()) { return Single.just(InteractionsRequest.getDefaultInstance()); } @@ -61,40 +63,64 @@ Single build(EdgeSetGroupRequest edgeSetRequestGroup) { private Single> collectSelectionsAndAggregations(EdgeSetGroupRequest request) { return this.selectionConverter - .convert(request.attributeRequests()) - .mergeWith(this.aggregationConverter.convert(request.metricAggregationRequests())) + .convert(getAllAttributeRequests(request)) + .mergeWith(this.aggregationConverter.convert(getAllMetricAggregationRequests(request))) .toObservable() .flatMap(Observable::fromIterable) .collect(Collectors.toUnmodifiableSet()); } + private Set getAllAttributeRequests(EdgeSetGroupRequest request) { + return request.edgeSetRequests().values().stream() + .map(EdgeSetRequest::attributeRequests) + .flatMap(Collection::stream) + .collect(Collectors.toUnmodifiableSet()); + } + + private Set getAllMetricAggregationRequests( + EdgeSetGroupRequest request) { + return request.edgeSetRequests().values().stream() + .map(EdgeSetRequest::metricAggregationRequests) + .flatMap(Collection::stream) + .collect(Collectors.toUnmodifiableSet()); + } + private Single buildEntityInteractionFilter(EdgeSetGroupRequest request) { - return Observable.fromIterable(request.entityTypes()) // add entity types filter - .collect(Collectors.toUnmodifiableSet()) + // Todo: we should be using converter taking argument as logical filters with filter arg schema + return Observable.fromIterable(request.edgeSetRequests().entrySet()) + .map( + entry -> + Stream.concat( + Stream.of(buildEntityTypeFilter(request, entry.getKey())), + entry.getValue().filterArguments().stream()) + .collect(Collectors.toUnmodifiableList())) + .flatMapSingle(this.filterConverter::convert) + .collect(Collectors.toUnmodifiableList()) .map( - entityTypes -> - AttributeAssociation.of( - request.neighborTypeAttribute().attributeExpressionAssociation().attribute(), - new EntityNeighborTypeFilter( - request.neighborTypeAttribute().attributeExpressionAssociation().value(), - entityTypes))) - .flatMap( - filterAssociation -> - this.filterConverter.convert( - Stream.concat( - request.filterArguments().stream(), // add all other filters - Stream.of(filterAssociation)) - .collect(Collectors.toUnmodifiableSet()))); + childFilters -> + Filter.newBuilder() + .setOperator(Operator.OR) + .addAllChildFilter(childFilters) + .build()); + } + + private AttributeAssociation buildEntityTypeFilter( + EdgeSetGroupRequest request, String entityType) { + return AttributeAssociation.of( + request.neighborTypeAttribute().attributeExpressionAssociation().attribute(), + new EntityNeighborTypeFilter( + request.neighborTypeAttribute().attributeExpressionAssociation().value(), entityType)); } @Value @Accessors(fluent = true) private static class EntityNeighborTypeFilter implements FilterArgument { + FilterType type = FilterType.ATTRIBUTE; String key = null; AttributeExpression keyExpression; - FilterOperatorType operator = FilterOperatorType.IN; - Collection value; + FilterOperatorType operator = FilterOperatorType.EQUALS; + String value; AttributeScope idType = null; String idScope = null; } diff --git a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/joiner/DefaultEntityJoinerBuilder.java b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/joiner/DefaultEntityJoinerBuilder.java index 0a5a2daf..8a24ce58 100644 --- a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/joiner/DefaultEntityJoinerBuilder.java +++ b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/joiner/DefaultEntityJoinerBuilder.java @@ -52,6 +52,7 @@ import org.hypertrace.core.graphql.utils.schema.SelectionQuery; import org.hypertrace.graphql.entity.dao.EntityDao; import org.hypertrace.graphql.entity.request.EdgeSetGroupRequest; +import org.hypertrace.graphql.entity.request.EdgeSetRequest; import org.hypertrace.graphql.entity.request.EntityLabelRequest; import org.hypertrace.graphql.entity.request.EntityLabelRequestBuilder; import org.hypertrace.graphql.entity.request.EntityRequest; @@ -59,7 +60,6 @@ import org.hypertrace.graphql.entity.schema.EntityJoinable; import org.hypertrace.graphql.entity.schema.EntityResultSet; import org.hypertrace.graphql.entity.schema.argument.EntityTypeStringArgument; -import org.hypertrace.graphql.metric.request.MetricAggregationRequest; import org.hypertrace.graphql.metric.request.MetricRequest; import org.hypertrace.graphql.metric.request.MetricRequestBuilder; import org.hypertrace.graphql.metric.schema.argument.AggregatableOrderArgument; @@ -317,12 +317,9 @@ private static class DefaultEntityRequest implements EntityRequest { @Value @Accessors(fluent = true) private static class EmptyEdgeSetGroupRequest implements EdgeSetGroupRequest { - Set entityTypes = Collections.emptySet(); - Collection attributeRequests = Collections.emptyList(); - Collection metricAggregationRequests = Collections.emptyList(); - Collection> filterArguments = Collections.emptyList(); AttributeRequest neighborIdAttribute = null; AttributeRequest neighborTypeAttribute = null; + Map edgeSetRequests = Collections.emptyMap(); @Override public Single buildNeighborRequest( diff --git a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeRequestBuilder.java b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeRequestBuilder.java index 34611e5f..62c6764c 100644 --- a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeRequestBuilder.java +++ b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeRequestBuilder.java @@ -3,11 +3,12 @@ import static io.reactivex.rxjava3.core.Single.zip; import graphql.schema.SelectedField; -import io.grpc.Status; +import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; @@ -89,35 +90,18 @@ private Single buildEdgeRequest( Stream edgeSetFields, EdgeType edgeType) { Set edgeFields = edgeSetFields.collect(Collectors.toUnmodifiableSet()); - List filterArguments = this.getFilters(edgeFields); - - if (!filterArguments.isEmpty() && edgeFields.size() > 1) { - throw Status.UNIMPLEMENTED - .withDescription("Cannot specify more than one edge type with edge filters") - .asRuntimeException(); - } - - Map> edgesByType = this.getEdgesByType(edgeFields.stream()); - Set allEdges = - edgesByType.values().stream() - .flatMap(Collection::stream) - .collect(Collectors.toUnmodifiableSet()); + Map> edgesSelectionsByType = + this.getEdgesSelectionByType(edgeFields.stream()); return zip( - this.getRequestedAndRequiredAttributes(context, allEdges, edgeType), this.getNeighborIdAttribute(context, edgeType), this.getNeighborTypeAttribute(context, edgeType), - this.metricAggregationRequestBuilder.build( - context, HypertraceAttributeScopeString.INTERACTION, allEdges.stream()), - this.filterRequestBuilder.build( - context, HypertraceAttributeScopeString.INTERACTION, filterArguments), - (attributeRequests, neighborIdRequest, neighborTypeRequest, metricRequests, filters) -> + this.getEntityTypeToEdgeSetRequest(context, edgeType, edgeFields), + (neighborIdRequest, neighborTypeRequest, edgeRequests) -> new DefaultEdgeSetGroupRequest( - edgesByType.keySet(), - attributeRequests, - metricRequests, neighborIdRequest, neighborTypeRequest, + edgeRequests, (entityType, neighborIds) -> this.neighborEntitiesRequestBuilderProvider .get() @@ -127,12 +111,44 @@ private Single buildEdgeRequest( timeRange, space, neighborIds, - edgesByType.get(entityType)), - filters)); + edgesSelectionsByType.get(entityType)))); + } + + private Single> getEntityTypeToEdgeSetRequest( + GraphQlRequestContext context, EdgeType edgeType, Set edgeFields) { + return Observable.fromIterable(edgeFields) + .collect(Collectors.groupingBy(this::getEntityType, Collectors.toUnmodifiableSet())) + .flatMap(edgeFieldsMap -> this.getEdgeSetRequestMap(context, edgeType, edgeFieldsMap)); } - private Map> getEdgesByType(Stream edgeSetStream) { + private Single> getEdgeSetRequestMap( + GraphQlRequestContext context, + EdgeType edgeType, + Map> entityTypeToEdgeFieldsMap) { + return Observable.fromIterable(entityTypeToEdgeFieldsMap.entrySet()) + .flatMapSingle( + entry -> + this.getEdgeSetRequestEntry(context, edgeType, entry.getKey(), entry.getValue())) + .collect(Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue)); + } + private Single> getEdgeSetRequestEntry( + GraphQlRequestContext context, + EdgeType edgeType, + String entityType, + Set edgeFields) { + return zip( + this.getRequestedAndRequiredAttributes(context, edgeFields, edgeType), + this.getMetricAggregationRequestAttributes(context, edgeFields), + this.getFilterArguments(context, edgeFields), + (requestAttributes, metricAttributes, filters) -> + Map.entry( + entityType, + new DefaultEdgeSetRequest(requestAttributes, metricAttributes, filters))); + } + + private Map> getEdgesSelectionByType( + Stream edgeSetStream) { return edgeSetStream.collect( Collectors.groupingBy( this::getEntityType, @@ -155,11 +171,25 @@ private String getEntityType(SelectedField edgeSetField) { .orElseThrow(); } + private Single> getMetricAggregationRequestAttributes( + GraphQlRequestContext context, Collection edges) { + Set selections = + edges.stream() + .collect( + Collectors.flatMapping(this::getEdgesForEdgeSet, Collectors.toUnmodifiableSet())); + return this.metricAggregationRequestBuilder.build( + context, HypertraceAttributeScopeString.INTERACTION, selections.stream()); + } + private Single> getRequestedAndRequiredAttributes( GraphQlRequestContext context, Collection edges, EdgeType edgeType) { + Set selections = + edges.stream() + .collect( + Collectors.flatMapping(this::getEdgesForEdgeSet, Collectors.toUnmodifiableSet())); return this.attributeRequestBuilder .buildForAttributeQueryableFields( - context, HypertraceAttributeScopeString.INTERACTION, edges.stream()) + context, HypertraceAttributeScopeString.INTERACTION, selections.stream()) .mergeWith(this.getNeighborIdAttribute(context, edgeType)) .mergeWith(this.getNeighborTypeAttribute(context, edgeType)) .collect(Collectors.toUnmodifiableList()); @@ -183,15 +213,21 @@ private Single getNeighborIdAttribute( } } - private List getFilters(Set selectedFields) { - return selectedFields.stream() - .map( - selectedField -> - this.argumentDeserializer.deserializeObjectList( - selectedField.getArguments(), FilterArgument.class)) - .flatMap(Optional::stream) - .flatMap(Collection::stream) - .collect(Collectors.toUnmodifiableList()); + private Single>> getFilterArguments( + GraphQlRequestContext context, Set edgeFields) { + Set filterArguments = + edgeFields.stream() + .collect(Collectors.flatMapping(this::getFilter, Collectors.toUnmodifiableSet())); + + return this.filterRequestBuilder.build( + context, HypertraceAttributeScopeString.INTERACTION, filterArguments); + } + + private Stream getFilter(SelectedField selectedField) { + return this.argumentDeserializer + .deserializeObjectList(selectedField.getArguments(), FilterArgument.class) + .stream() + .flatMap(Collection::stream); } private Single getNeighborTypeAttribute( @@ -220,14 +256,10 @@ private enum EdgeType { @Value @Accessors(fluent = true) private static class DefaultEdgeSetGroupRequest implements EdgeSetGroupRequest { - - Set entityTypes; - Collection attributeRequests; - Collection metricAggregationRequests; AttributeRequest neighborIdAttribute; AttributeRequest neighborTypeAttribute; + Map edgeSetRequests; BiFunction, Single> neighborRequestBuilder; - Collection> filterArguments; @Override public Single buildNeighborRequest( @@ -235,4 +267,12 @@ public Single buildNeighborRequest( return this.neighborRequestBuilder.apply(entityType, neighborIds); } } + + @Value + @Accessors(fluent = true) + private static class DefaultEdgeSetRequest implements EdgeSetRequest { + Collection attributeRequests; + Collection metricAggregationRequests; + Collection> filterArguments; + } } diff --git a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetGroupRequest.java b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetGroupRequest.java index c6859008..b5075fbb 100644 --- a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetGroupRequest.java +++ b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetGroupRequest.java @@ -2,26 +2,15 @@ import io.reactivex.rxjava3.core.Single; import java.util.Collection; -import java.util.Set; -import org.hypertrace.core.graphql.common.request.AttributeAssociation; +import java.util.Map; import org.hypertrace.core.graphql.common.request.AttributeRequest; -import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; -import org.hypertrace.graphql.metric.request.MetricAggregationRequest; public interface EdgeSetGroupRequest { - - Set entityTypes(); - - // Includes neighbor id and type - Collection attributeRequests(); - - Collection metricAggregationRequests(); - AttributeRequest neighborIdAttribute(); AttributeRequest neighborTypeAttribute(); Single buildNeighborRequest(String entityType, Collection neighborIds); - Collection> filterArguments(); + Map edgeSetRequests(); } diff --git a/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetRequest.java b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetRequest.java new file mode 100644 index 00000000..9ca1a375 --- /dev/null +++ b/hypertrace-graphql-entity-schema/src/main/java/org/hypertrace/graphql/entity/request/EdgeSetRequest.java @@ -0,0 +1,17 @@ +package org.hypertrace.graphql.entity.request; + +import java.util.Collection; +import org.hypertrace.core.graphql.common.request.AttributeAssociation; +import org.hypertrace.core.graphql.common.request.AttributeRequest; +import org.hypertrace.core.graphql.common.schema.results.arguments.filter.FilterArgument; +import org.hypertrace.graphql.metric.request.MetricAggregationRequest; + +public interface EdgeSetRequest { + // list of filter arguments + Collection> filterArguments(); + + // Includes neighbor id and type + Collection attributeRequests(); + + Collection metricAggregationRequests(); +} diff --git a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/joiner/DefaultEntityAndRuleJoinerBuilder.java b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/joiner/DefaultEntityAndRuleJoinerBuilder.java index a15737a6..0cd0c575 100644 --- a/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/joiner/DefaultEntityAndRuleJoinerBuilder.java +++ b/hypertrace-graphql-labels-schema-impl/src/main/java/org/hypertrace/graphql/label/joiner/DefaultEntityAndRuleJoinerBuilder.java @@ -44,6 +44,7 @@ import org.hypertrace.core.graphql.utils.schema.SelectionQuery; import org.hypertrace.graphql.entity.dao.EntityDao; import org.hypertrace.graphql.entity.request.EdgeSetGroupRequest; +import org.hypertrace.graphql.entity.request.EdgeSetRequest; import org.hypertrace.graphql.entity.request.EntityLabelRequest; import org.hypertrace.graphql.entity.request.EntityRequest; import org.hypertrace.graphql.entity.schema.EntityResultSet; @@ -55,7 +56,6 @@ import org.hypertrace.graphql.label.schema.LabelResultSet; import org.hypertrace.graphql.label.schema.rule.LabelApplicationRule; import org.hypertrace.graphql.label.schema.rule.LabelApplicationRuleResultSet; -import org.hypertrace.graphql.metric.request.MetricAggregationRequest; import org.hypertrace.graphql.metric.request.MetricRequest; import org.hypertrace.graphql.metric.schema.argument.AggregatableOrderArgument; @@ -316,12 +316,9 @@ private static class DefaultEntityRequest implements EntityRequest { @Value @Accessors(fluent = true) private static class EmptyEdgeSetGroupRequest implements EdgeSetGroupRequest { - Set entityTypes = Collections.emptySet(); - Collection attributeRequests = Collections.emptyList(); - Collection metricAggregationRequests = Collections.emptyList(); - Collection> filterArguments = Collections.emptyList(); AttributeRequest neighborIdAttribute = null; AttributeRequest neighborTypeAttribute = null; + Map edgeSetRequests = Collections.emptyMap(); @Override public Single buildNeighborRequest(