Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ describe('Explorer component', () => {
spectator.query(ExploreQueryEditorComponent)!.setInterval(new TimeDuration(30, TimeUnit.Second));
spectator.query(ExploreQueryEditorComponent)!.updateGroupByKey(
{
keys: ['apiName'],
keyExpressions: [{ key: 'apiName' }],
limit: 6,
includeRest: true
},
Expand All @@ -370,7 +370,7 @@ describe('Explorer component', () => {
expect(queryParamChangeSpy).toHaveBeenLastCalledWith({
scope: 'spans',
series: ['column:avg(second)'],
group: 'apiName',
group: ['apiName'],
limit: 6,
other: true,
interval: '30s'
Expand Down
26 changes: 21 additions & 5 deletions projects/observability/src/pages/explorer/explorer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
TimeDurationService
} from '@hypertrace/common';
import { Filter, ToggleItem } from '@hypertrace/components';
import { isNil } from 'lodash-es';
import { isEmpty, isNil } from 'lodash-es';
import { concat, EMPTY, Observable, Subject } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { CartesianSeriesVisualizationType } from '../../shared/components/cartesian/chart';
Expand All @@ -19,6 +19,7 @@ import {
ExploreVisualizationRequest
} from '../../shared/components/explore-query-editor/explore-visualization-builder';
import { IntervalValue } from '../../shared/components/interval-select/interval-select.component';
import { AttributeExpression } from '../../shared/graphql/model/attribute/attribute-expression';
import { AttributeMetadata } from '../../shared/graphql/model/metadata/attribute-metadata';
import { MetricAggregationType } from '../../shared/graphql/model/metrics/metric-aggregation';
import { GraphQlGroupBy } from '../../shared/graphql/model/schema/groupby/graphql-group-by';
Expand Down Expand Up @@ -216,8 +217,8 @@ export class ExplorerComponent {
}

private getGroupByQueryParams(groupBy?: GraphQlGroupBy): QueryParamObject {
const key = groupBy?.keys[0];
if (key === undefined) {
const keyExpressions = groupBy?.keyExpressions ?? [];
if (keyExpressions.length === 0) {
return {
// Clear existing selection
[ExplorerQueryParam.Group]: undefined,
Expand All @@ -227,7 +228,7 @@ export class ExplorerComponent {
}

return {
[ExplorerQueryParam.Group]: key,
[ExplorerQueryParam.Group]: keyExpressions.map(expression => this.encodeAttributeExpression(expression)),
[ExplorerQueryParam.OtherGroup]: groupBy?.includeRest || undefined, // No param needed for false
[ExplorerQueryParam.GroupLimit]: groupBy?.limit
};
Expand All @@ -238,7 +239,9 @@ export class ExplorerComponent {
contextToggle: this.getOrDefaultContextItemFromQueryParam(param.get(ExplorerQueryParam.Scope) as ScopeQueryParam),
groupBy: param.has(ExplorerQueryParam.Group)
? {
keys: param.getAll(ExplorerQueryParam.Group),
keyExpressions: param
.getAll(ExplorerQueryParam.Group)
.flatMap(expressionString => this.tryDecodeAttributeExpression(expressionString)),
includeRest: param.get(ExplorerQueryParam.OtherGroup) === 'true',
// tslint:disable-next-line: strict-boolean-expressions
limit: parseInt(param.get(ExplorerQueryParam.GroupLimit)!) || 5
Expand Down Expand Up @@ -294,6 +297,19 @@ export class ExplorerComponent {
}
];
}

private encodeAttributeExpression(attributeExpression: AttributeExpression): string {
if (isEmpty(attributeExpression.subpath)) {
return attributeExpression.key;
}

return `${attributeExpression.key}__${attributeExpression.subpath}`;
}
private tryDecodeAttributeExpression(expressionString: string): [AttributeExpression] | [] {
const [key, subpath] = expressionString.split('__');

return [{ key: key, ...(isEmpty(subpath) ? { subpath: subpath } : {}) }];
}
}
interface ContextToggleItem extends ToggleItem<ExplorerContextScope> {
value: ExplorerContextScope;
Expand Down
1 change: 1 addition & 0 deletions projects/observability/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export * from './shared/services/metadata/metadata.service';
export * from './shared/services/metadata/metadata.service.module';
export * from './shared/graphql/model/metadata/attribute-metadata';

export * from './shared/graphql/model/attribute/attribute-expression';
export * from './shared/graphql/model/metrics/metric-aggregation';
export * from './shared/graphql/model/metrics/metric-health';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ describe('Explore query editor', () => {
expect.objectContaining({
series: [defaultSeries],
groupBy: {
keys: ['first groupable'],
keyExpressions: [{ key: 'first groupable' }],
limit: 5 // Default group by limit
}
})
Expand Down Expand Up @@ -215,7 +215,7 @@ describe('Explore query editor', () => {
expect.objectContaining({
series: [defaultSeries],
groupBy: {
keys: ['first groupable'],
keyExpressions: [{ key: 'first groupable' }],
limit: 6
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {
<ht-explore-query-group-by-editor
class="group-by"
[context]="currentVisualization.context"
[groupByKey]="(currentVisualization.groupBy?.keys)[0]"
[groupByKey]="(currentVisualization.groupBy?.keyExpressions)?.[0]?.key"
(groupByKeyChange)="this.updateGroupByKey(currentVisualization.groupBy, $event)"
></ht-explore-query-group-by-editor>

Expand Down Expand Up @@ -103,8 +103,8 @@ export class ExploreQueryEditorComponent implements OnChanges, OnInit {
this.setInterval(this.interval);
}

if (changeObject.groupBy && this.groupBy?.keys.length) {
this.updateGroupByKey(this.groupBy, this.groupBy.keys[0]);
if (changeObject.groupBy && this.groupBy?.keyExpressions.length) {
this.updateGroupByKey(this.groupBy, this.groupBy.keyExpressions[0]?.key);
}
}

Expand All @@ -117,7 +117,9 @@ export class ExploreQueryEditorComponent implements OnChanges, OnInit {
this.visualizationBuilder.groupBy();
} else {
this.visualizationBuilder.groupBy(
groupBy ? { ...groupBy, keys: [key] } : { keys: [key], limit: ExploreQueryEditorComponent.DEFAULT_GROUP_LIMIT }
groupBy
? { ...groupBy, keyExpressions: [{ key: key }] }
: { keyExpressions: [{ key: key }], limit: ExploreQueryEditorComponent.DEFAULT_GROUP_LIMIT }
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ describe('Explore visualization builder', () => {
spectator.service
.setSeries([buildSeries('test1')])
.groupBy({
keys: ['testGroupBy'],
keyExpressions: [{ key: 'testGroupBy' }],
limit: 15
})
.setSeries([buildSeries('test2')]);

expectObservable(recordedRequests).toBe('10ms x', {
x: expectedQuery({
series: [matchSeriesWithName('test2')],
groupBy: { keys: ['testGroupBy'], limit: 15 }
groupBy: { keyExpressions: [{ key: 'testGroupBy' }], limit: 15 }
})
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ describe('Navigable dashboard component', () => {
};
spectator.query(FilterBarComponent)?.filtersChange.next([explicitFilter]);
expect(mockDataSource.addFilters).toHaveBeenCalledWith(
expect.objectContaining({ key: 'foo', operator: GraphQlOperatorType.Equals, value: 'bar' })
expect.objectContaining({ keyOrExpression: 'foo', operator: GraphQlOperatorType.Equals, value: 'bar' })
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ describe('Explore cartesian data source model', () => {
];

model.groupBy = {
keys: ['baz'],
keyExpressions: [{ key: 'baz' }],
includeRest: true,
limit: 5
};
Expand Down Expand Up @@ -220,7 +220,7 @@ describe('Explore cartesian data source model', () => {
];

model.groupBy = {
keys: ['baz'],
keyExpressions: [{ key: 'baz' }],
limit: 5
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NEVER, Observable, of } from 'rxjs';
import { map, mergeMap } from 'rxjs/operators';
import { Series } from '../../../../components/cartesian/chart';
import { ExploreRequestState } from '../../../../components/explore-query-editor/explore-visualization-builder';
import { AttributeExpression } from '../../../../graphql/model/attribute/attribute-expression';
import { MetricTimeseriesInterval } from '../../../../graphql/model/metric/metric-timeseries';
import { GraphQlFilter } from '../../../../graphql/model/schema/filter/graphql-filter';
import { ExploreSpecification } from '../../../../graphql/model/schema/specifications/explore-specification';
Expand Down Expand Up @@ -107,19 +108,19 @@ export abstract class ExploreCartesianDataSourceModel extends GraphQlDataSourceM
(selection): selection is RequireBy<ExploreSpecification, 'aggregation'> => selection.aggregation !== undefined
);

const groupByKeys = request.groupBy?.keys ?? [];
const isGroupBy = groupByKeys.length > 0;
const groupByExpressions = request.groupBy?.keyExpressions ?? [];
const isGroupBy = groupByExpressions.length > 0;

if (!isGroupBy && request.interval) {
return aggregatableSpecs.map(spec => this.buildTimeseriesData(spec, result));
}

if (isGroupBy && !request.interval) {
return aggregatableSpecs.map(spec => this.buildGroupedSeriesData(spec, groupByKeys, result));
return aggregatableSpecs.map(spec => this.buildGroupedSeriesData(spec, groupByExpressions, result));
}

if (isGroupBy && request.interval) {
return aggregatableSpecs.map(spec => this.buildGroupedTimeseriesData(spec, groupByKeys, result)).flat();
return aggregatableSpecs.map(spec => this.buildGroupedTimeseriesData(spec, groupByExpressions, result)).flat();
}

return [];
Expand Down Expand Up @@ -149,21 +150,25 @@ export abstract class ExploreCartesianDataSourceModel extends GraphQlDataSourceM
};
}

public buildGroupedSeriesData(spec: AggregatableSpec, groupByKeys: string[], result: ExploreResult): SeriesData {
public buildGroupedSeriesData(
spec: AggregatableSpec,
groupByExpressions: AttributeExpression[],
result: ExploreResult
): SeriesData {
return {
data: result
.getGroupedSeriesData(groupByKeys, spec.name, spec.aggregation)
.getGroupedSeriesData(groupByExpressions, spec.name, spec.aggregation)
.map(({ keys, value }) => [this.buildGroupedSeriesName(keys), value]),
spec: spec
};
}

public buildGroupedTimeseriesData(
spec: AggregatableSpec,
groupByKeys: string[],
groupByExpressions: AttributeExpression[],
result: ExploreResult
): SeriesData[] {
return Array.from(result.getGroupedTimeSeriesData(groupByKeys, spec.name, spec.aggregation).entries()).map(
return Array.from(result.getGroupedTimeSeriesData(groupByExpressions, spec.name, spec.aggregation).entries()).map(
([groupNames, data]) => ({
data: data,
groupName: this.buildGroupedSeriesName(groupNames),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('Explore result', () => {
]
});

expect(result.getGroupedSeriesData(['group'], 'foo', MetricAggregationType.Sum)).toEqual([
expect(result.getGroupedSeriesData([{ key: 'group' }], 'foo', MetricAggregationType.Sum)).toEqual([
{ keys: ['first'], value: 10 },
{ keys: ['second'], value: 15 },
{ keys: ['third'], value: 20 }
Expand Down Expand Up @@ -116,7 +116,7 @@ describe('Explore result', () => {
]
});

expect(result.getGroupedSeriesData(['group'], 'foo', MetricAggregationType.Sum)).toEqual([
expect(result.getGroupedSeriesData([{ key: 'group' }], 'foo', MetricAggregationType.Sum)).toEqual([
{ keys: ['first'], value: 10 },
{ keys: ['Others'], value: 15 }
]);
Expand Down Expand Up @@ -172,7 +172,7 @@ describe('Explore result', () => {
]
});

expect(result.getGroupedTimeSeriesData(['group'], 'foo', MetricAggregationType.Sum)).toEqual(
expect(result.getGroupedTimeSeriesData([{ key: 'group' }], 'foo', MetricAggregationType.Sum)).toEqual(
new Map([
[
['first'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TimeDuration } from '@hypertrace/common';
import { groupBy } from 'lodash-es';
import { AttributeExpression } from '../../../../graphql/model/attribute/attribute-expression';
import { MetricTimeseriesInterval } from '../../../../graphql/model/metric/metric-timeseries';
import { MetricAggregationType } from '../../../../graphql/model/metrics/metric-aggregation';
import { ExploreSpecification } from '../../../../graphql/model/schema/specifications/explore-specification';
Expand Down Expand Up @@ -27,19 +28,25 @@ export class ExploreResult {
return this.extractTimeseriesForSpec(this.specBuilder.exploreSpecificationForKey(metricKey, aggregation));
}

public getGroupedSeriesData(groupKeys: string[], metricKey: string, aggregation: MetricAggregationType): GroupData[] {
public getGroupedSeriesData(
groupExpressions: AttributeExpression[],
metricKey: string,
aggregation: MetricAggregationType
): GroupData[] {
return this.extractGroupSeriesForSpec(
groupKeys.map(key => this.specBuilder.exploreSpecificationForKey(key)),
groupExpressions.map(expression => this.specBuilder.exploreSpecificationForAttributeExpression(expression)),
this.specBuilder.exploreSpecificationForKey(metricKey, aggregation)
);
}

public getGroupedTimeSeriesData(
groupKeys: string[],
groupExpressions: AttributeExpression[],
metricKey: string,
aggregation: MetricAggregationType
): Map<string[], MetricTimeseriesInterval[]> {
const groupSpecs = groupKeys.map(key => this.specBuilder.exploreSpecificationForKey(key));
const groupSpecs = groupExpressions.map(expression =>
this.specBuilder.exploreSpecificationForAttributeExpression(expression)
);
const spec = this.specBuilder.exploreSpecificationForKey(metricKey, aggregation);
const groupedResults = groupBy(this.response.results, result =>
this.getGroupNamesFromResult(result, groupSpecs).join(',')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ describe('Explorer Visualization cartesian data source model', () => {
model.request = buildVisualizationRequest({
interval: undefined,
groupBy: {
keys: ['baz'],
keyExpressions: [{ key: 'baz' }],
limit: 10
},
series: [
Expand Down Expand Up @@ -233,7 +233,7 @@ describe('Explorer Visualization cartesian data source model', () => {
model.request = buildVisualizationRequest({
interval: new TimeDuration(5, TimeUnit.Minute),
groupBy: {
keys: ['baz'],
keyExpressions: [{ key: 'baz' }],
limit: 5
},
series: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { SpanDataSourceModel } from './span/span-data-source.model';
import { AttributeSpecificationModel } from './specifiers/attribute-specification.model';
import { CompositeSpecificationModel } from './specifiers/composite-specification.model';
import { EnrichedAttributeSpecificationModel } from './specifiers/enriched-attribute-specification.model';
import { FieldSpecificationModel } from './specifiers/field-specification.model';
import { MappedAttributeSpecificationModel } from './specifiers/mapped-attribute-specification.model';
import { TraceStatusSpecificationModel } from './specifiers/trace-status-specification.model';
import { SpansTableDataSourceModel } from './table/spans/spans-table-data-source.model';
Expand Down Expand Up @@ -40,7 +39,6 @@ import { TraceWaterfallDataSourceModel } from './waterfall/trace-waterfall-data-
TracesDataSourceModel,
CompositeSpecificationModel,
AttributeSpecificationModel,
FieldSpecificationModel,
TraceStatusSpecificationModel,
EnrichedAttributeSpecificationModel,
MappedAttributeSpecificationModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('Metric aggregation data source model', () => {
}),
filters: [
expect.objectContaining({
key: 'duration',
keyOrExpression: 'duration',
operator: GraphQlOperatorType.GreaterThan,
value: 500
})
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ describe('Explore table data source model', () => {
limit: 100,
offset: 0,
groupBy: expect.objectContaining({
keys: ['name'],
keyExpressions: [{ key: 'name' }],
includeRest: false
}),
orderBy: [
Expand Down
Loading