55import com .google .common .collect .Streams ;
66import com .google .protobuf .InvalidProtocolBufferException ;
77import com .google .protobuf .util .JsonFormat ;
8- import java . util . ArrayList ;
8+ import io . grpc . Status ;
99import java .util .Collections ;
1010import java .util .Iterator ;
1111import java .util .List ;
3535import org .hypertrace .gateway .service .entity .EntitiesRequestContext ;
3636import org .hypertrace .gateway .service .entity .config .EntityIdColumnsConfig ;
3737import org .hypertrace .gateway .service .explore .entity .EntityServiceEntityFetcher ;
38+ import org .hypertrace .gateway .service .v1 .common .AttributeExpression ;
3839import org .hypertrace .gateway .service .v1 .common .Expression ;
3940import org .hypertrace .gateway .service .v1 .common .FunctionExpression ;
41+ import org .hypertrace .gateway .service .v1 .common .LiteralConstant ;
42+ import org .hypertrace .gateway .service .v1 .common .Operator ;
4043import org .hypertrace .gateway .service .v1 .common .OrderByExpression ;
4144import org .hypertrace .gateway .service .v1 .common .TimeAggregation ;
45+ import org .hypertrace .gateway .service .v1 .common .ValueType ;
4246import org .hypertrace .gateway .service .v1 .entity .EntitiesRequest ;
4347import org .hypertrace .gateway .service .v1 .entity .Entity .Builder ;
4448import org .hypertrace .gateway .service .v1 .explore .ExploreRequest ;
6367 */
6468public class RequestHandler implements RequestHandlerWithSorting {
6569 private static final Logger LOG = LoggerFactory .getLogger (RequestHandler .class );
70+ private static final Expression NULL_VALUE_EXPRESSION =
71+ Expression .newBuilder ()
72+ .setLiteral (
73+ LiteralConstant .newBuilder ()
74+ .setValue (
75+ org .hypertrace .gateway .service .v1 .common .Value .newBuilder ()
76+ .setValueType (ValueType .STRING )
77+ .setString ("null" )
78+ .build ())
79+ .build ())
80+ .build ();
6681
6782 private final QueryServiceClient queryServiceClient ;
6883 private final AttributeMetadataProvider attributeMetadataProvider ;
@@ -107,15 +122,30 @@ QueryRequest buildQueryRequest(
107122 requestContext .setHasGroupBy (true );
108123 }
109124
110- List <String > entityIds = new ArrayList <>();
111125 org .hypertrace .gateway .service .v1 .common .Filter qsSourceFilter = request .getFilter ();
112126 Map <String , AttributeMetadata > attributeMetadataMap =
113127 attributeMetadataProvider .getAttributesMetadata (requestContext , request .getContext ());
114128 if (hasOnlyAttributeSource (request .getFilter (), AttributeSource .EDS , attributeMetadataMap )) {
115- entityIds = getEntityIdsToFilterFromSourceEDS (requestContext , request , attributeMetadataMap );
129+ List <String > entityIds =
130+ getEntityIdsToFilterFromSourceEDS (requestContext , request , attributeMetadataMap );
131+ List <String > entityIdAttributes =
132+ AttributeMetadataUtil .getIdAttributeIds (
133+ attributeMetadataProvider ,
134+ entityIdColumnsConfig ,
135+ requestContext ,
136+ request .getContext ());
116137 qsSourceFilter =
117- buildFilter (request .getFilter (), AttributeSource .QS , attributeMetadataMap )
118- .orElse (request .getFilter ());
138+ org .hypertrace .gateway .service .v1 .common .Filter .newBuilder ()
139+ .setOperator (Operator .AND )
140+ .addChildFilter (
141+ buildFilter (request .getFilter (), AttributeSource .QS , attributeMetadataMap )
142+ .orElse (request .getFilter ()))
143+ .addChildFilter (
144+ createEntityIdAttributeFilter (
145+ entityIdAttributes ,
146+ org .hypertrace .gateway .service .v1 .common .Operator .IN ,
147+ entityIds ))
148+ .build ();
119149 }
120150
121151 QueryRequest .Builder builder = QueryRequest .newBuilder ();
@@ -141,7 +171,7 @@ QueryRequest buildQueryRequest(
141171 // 2. Add filter
142172 builder .setFilter (
143173 constructQueryServiceFilter (
144- request , qsSourceFilter , requestContext , attributeMetadataProvider , entityIds ));
174+ request , qsSourceFilter , requestContext , attributeMetadataProvider ));
145175
146176 if (requestContext .hasGroupBy () && request .getIncludeRestGroup () && request .getOffset () > 0 ) {
147177 // including rest group with offset is an invalid combination
@@ -280,33 +310,22 @@ Filter constructQueryServiceFilter(
280310 ExploreRequestContext exploreRequestContext ,
281311 AttributeMetadataProvider attributeMetadataProvider ) {
282312 return this .constructQueryServiceFilter (
283- request ,
284- request .getFilter (),
285- exploreRequestContext ,
286- attributeMetadataProvider ,
287- Collections .emptyList ());
313+ request , request .getFilter (), exploreRequestContext , attributeMetadataProvider );
288314 }
289315
290316 Filter constructQueryServiceFilter (
291317 ExploreRequest request ,
292318 org .hypertrace .gateway .service .v1 .common .Filter requestFilter ,
293319 ExploreRequestContext exploreRequestContext ,
294- AttributeMetadataProvider attributeMetadataProvider ,
295- List <String > entityIds ) {
296- return QueryAndGatewayDtoConverter .addTimeSpaceAndIdFiltersAndConvertToQueryFilter (
320+ AttributeMetadataProvider attributeMetadataProvider ) {
321+ return QueryAndGatewayDtoConverter .addTimeAndSpaceFiltersAndConvertToQueryFilter (
297322 request .getStartTimeMillis (),
298323 request .getEndTimeMillis (),
299324 request .getSpaceId (),
300- entityIds ,
301325 AttributeMetadataUtil .getTimestampAttributeId (
302326 attributeMetadataProvider , exploreRequestContext , request .getContext ()),
303327 AttributeMetadataUtil .getSpaceAttributeId (
304328 attributeMetadataProvider , exploreRequestContext , request .getContext ()),
305- AttributeMetadataUtil .getIdAttributeIds (
306- attributeMetadataProvider ,
307- entityIdColumnsConfig ,
308- exploreRequestContext ,
309- request .getContext ()),
310329 requestFilter );
311330 }
312331
@@ -532,6 +551,72 @@ private Map<String, AttributeMetadata> remapAttributeMetadataByResultName(
532551 attributeMetadataByIdMap );
533552 }
534553
554+ private org .hypertrace .gateway .service .v1 .common .Filter createEntityIdAttributeFilter (
555+ List <String > entityIdAttributes ,
556+ org .hypertrace .gateway .service .v1 .common .Operator operator ,
557+ List <String > entityIds ) {
558+ if (entityIdAttributes .size () != 1 ) {
559+ throw Status .FAILED_PRECONDITION
560+ .withDescription ("entity must have one id attribute." )
561+ .asRuntimeException ();
562+ }
563+
564+ if (entityIds .isEmpty ()) {
565+ // TODO: have a better approach here. One possible solution could be to
566+ // form a dummy response based on the selections provided
567+
568+ // Having empty entity ids is valid filter because this means that
569+ // EDS source has filtered out all the entities and the result should
570+ // be empty. But QS doesn't recognize empty IN filter as valid filter
571+ // and ignoring this filter will generate wrong results.
572+ //
573+ // We could have return empty but clients expects response to contain
574+ // information as per the selections they have sent. So we are converting
575+ // empty entity Id filter into a false filter (id != null && id == null)
576+ // so that results are always empty but must contain the valid selections.
577+ return org .hypertrace .gateway .service .v1 .common .Filter .newBuilder ()
578+ .setOperator (Operator .AND )
579+ .addChildFilter (
580+ org .hypertrace .gateway .service .v1 .common .Filter .newBuilder ()
581+ .setLhs (buildAttributeExpression (entityIdAttributes .get (0 )))
582+ .setOperator (Operator .EQ )
583+ .setRhs (NULL_VALUE_EXPRESSION )
584+ .build ())
585+ .addChildFilter (
586+ org .hypertrace .gateway .service .v1 .common .Filter .newBuilder ()
587+ .setLhs (buildAttributeExpression (entityIdAttributes .get (0 )))
588+ .setOperator (Operator .NEQ )
589+ .setRhs (NULL_VALUE_EXPRESSION )
590+ .build ())
591+ .build ();
592+ }
593+
594+ return org .hypertrace .gateway .service .v1 .common .Filter .newBuilder ()
595+ .setLhs (buildAttributeExpression (entityIdAttributes .get (0 )))
596+ .setOperator (operator )
597+ .setRhs (buildStringArrayExpression (entityIds ))
598+ .build ();
599+ }
600+
601+ private Expression buildStringArrayExpression (List <String > values ) {
602+ return Expression .newBuilder ()
603+ .setLiteral (
604+ LiteralConstant .newBuilder ()
605+ .setValue (
606+ org .hypertrace .gateway .service .v1 .common .Value .newBuilder ()
607+ .setValueType (ValueType .STRING_ARRAY )
608+ .addAllStringArray (values )
609+ .build ())
610+ .build ())
611+ .build ();
612+ }
613+
614+ private Expression buildAttributeExpression (String value ) {
615+ return Expression .newBuilder ()
616+ .setAttributeExpression (AttributeExpression .newBuilder ().setAttributeId (value ).build ())
617+ .build ();
618+ }
619+
535620 private boolean hasOnlyAttributeSource (
536621 org .hypertrace .gateway .service .v1 .common .Filter filter ,
537622 AttributeSource source ,
0 commit comments